화살표 함수?

Parker.Park·2022년 6월 21일
0

개념

목록 보기
1/16

화살표 함수?

ES6문법 특징을 정리했는데 몇 가지 특징만 나열하면 끝날 줄 알았는데 몇 가지는 내용이 많아질 거 같아서 따로 빼서 정리하려고 한다. 화살표 함수를 쓰기까지 꽤 오랜시간이 걸렸다. this 바인딩에 관해서도 부족했고, scope관련해서도 더욱 보완했다. 그래도 부족한감은 있지만 이번 기회에 제대로 채워보리라하고 다짐한다.

함수?

먼저 자바스크립트에서 함수는 정확히 어떻게 표현할까?
다음과 같이 3가지가 있다고 한다.

  • 함수 선언문
  • 함수 표현식
  • Function 생성자 함수

함수 선언문(Function declaration)

함수 선언문 방식으로 정의한 함수는 function키워드와 아래 내용으로 구성된다고 한다.

  • 함수명
    함수 선언문의 경우 함수명을 생략할 수 없다고 한다. 함수명은 자신을 재귀적 호출하거나 자바스크립트 디버거가 해당 함수를 구분할 수 있는 식별자라고 한다.
  • 매개변수 목록
    0개 이상의 목록으로 ()괄호로 감싸고 ,콤마로 분리한다고 한다. 다른언어와 차이점으로는 매개변수 타입을 지정하지 않는다고 한다. 이 때문에 함수 내에서 매개변수의 타입을 체크할 필요가 있다고 한다.
  • 함수 몸체
    함수가 호출되었을 때 실행되는 문들의 집합이라고 한다. {}중괄호로 감싸고, return문으로 결과값을 반환할 수 있다고 한다.
//function declaration
function dog(n){
  return n + ' dog(s)'
}

함수 표현식(function expression)

자밬스크립트 함수는 일급 객체(first-class object)라고 한다. 그리고 특징은 다음과 같다고 한다.

  • 무명의 리터럴로 표현이 가능하다.
  • 변수나 자료구조에 저장할 수 있다.
  • 함수의 파라미터로 전달 할 수 있다.
  • 반환값(return value)로 사용 할 수 있다.

위 특징을 이용하여 함수 리터럴 방식으로 함수를 정의하고 변수에 할당 할수 있는데, 이것을 함수 표현식이라고 한다.

리터럴 표기법이란?

변수를 선언함과 동시에 그값을 지정해주는 표기법을 말한다고 한다.
참조사이트 : 리터럴(Literal)이란?

//function expression
var dog = function(n){
  return n + ' dog(s)'
}

함수표현식 방식으로 정의한 함수는 함수명을 생략할 수 있다고 한다. 이런 함수를 익명함수(anonymous function)이라고 한다. 일반적으로 함수명을 생략한다고 한다.

// 기명 함수 표현식(named function expression)
var dog = function Dog(n){
  return n + ' dog(s)'
}

// 익명 함수 표현식(anonymous function expression)
var dog = function(n){
  return n + ' dog(s)'
}

console.log(dog(2)) //'2 dog(s)'
console.log(Dog(2)) // ReferenceError: Dog is not defined

위 예에서 에러가 났는데 그 이유는 표현식에서 사용한 함수명은 외부코드에서는 접근이 불가능하기 때문이라고 한다. 함수는 일급객체이기 때문에 변수에 할당할 수 있는데, 이 변수는 함수명이 아니라 함수를 가르키는 참조값을 저장하게 된다고 한다. 그렇기 때문에 함수 호출시 함수명이 아니라 함수를 가르키는 변수명을 사용하여야 한다고 한다. 사실 함수 선언문도 함수 표현식과 동일하게 함수 리터럴 방식으로 정의 된 것이라고 한다.

//function delaration
function dog(n){
  return n + ' dog(s)'
}
//함수 선언문도 결국 자바스크립트 엔진에서 표현식으로 형태가 변경하기 때문에 접근이 가능했던 것이다.
var dog = function dog(n){
  return n + ' dog(s)'
}

Function 생성자 함수

위 에서 함수 선언문 또한 기명 함수 표현식으로 변환 되므로 결국 함수 리터럴 방식이라는 것을 알았다.
이는 결국 내장 함수인 Function 생성자 함수로 함수를 생성한 것을 단축 시킨 short-hand(축약법) 이라고 한다.
Function 생성자 함수는 Function.prototype.constructor 프로퍼티로 접근할 수 있다고 한다.(Prototype에 대한 velog)

구문

new Function ([arg1[, arg2[, ...argN]],] functionBody)
let dog = new Function('n', `return n + ' dog(s)'`)

console.log(dog(10)) // '10 dog(s)'

화살표함수 특징

앞에서 함수생성의 3가지 특징과 결국 함수는 Function생성자 함수로 생성한다는 것을 알았다. 그렇다면 화살표 함수는 어떤 특징을 갖고 있을까?

  • thissuper에 대한 바인딩이 없고, method로 사용될 수 없다고 한다.
  • new.target 키워드가 없다고 한다.
  • 일반적 스코프를 사용한는 call, apply, bind method를 이용 할 수 없다고 한다.
  • 생성자로 사용할 수 없다고 한다.
  • 내부함수인 yield를 사용 할 수 없다고 한다.

구문

//function declaration
function abplus(a, b){
  return a + b
}

//arrow function
const abplus=(a, b)=>{
  return a+b
}
//매개 변수가 없는 경우
const dog=()=>{}

//매개 변수가 한 개인 경우, 소괄호 생략 가능
x => {}
//매개 변수가 여러개인 경우, 소괄호 생략 불가능
(a, b) => {}


//single line block
//한줄의 구문이라면 중괄호를 생략가능하다. 암묵적 return이 가능하다고 한다.
const abplus=(a, b)=> a + b

//객체 반환 시 소괄호를 사용한다고 한다.
const abplus=(a, b)=>({a: a +b}) 
const abplus=(a, b)=>{return {a: a +b}} 

//destructuring assignment(구조분해할당)도 가능하다고 한다.
let abplus = ([a,b] = [1, 2], {x: c} = {x: a + b}) => a + b + c

abplus() //6

//default function parameter 사용도 가능하다고 한다.
let abplus = (a = 1, b = 2) => a + b

console.log(abplus()) //3
console.log(abplus(4)) //6
console.log(abplus(3,3)) //6

// Rest parameter(나머지 매개변수)사용도 가능하다고 한다.
let abc = (a,b,...c) => {
  console.log(`1: ${a}, 2: ${b}`)
  console.log(`3 ~ : ${c}`)
}

console.log(abc(1,2,3,4,5,6)) 
//'1: 1, 2: 2'
//'3 ~ : 3,4,5,6'

this

function 키워드로 생성한 일반함수와 화살표 함수의 가장 큰 차이점은 this라고 한다.
자바스크립트는 함수 호출 방식에 의해 this가 바인딩할 객체를 동적으로 결정된다고 한다.
콜백 함수 내부의 this는 전역객체(window)를 가르킨다고 한다. (참고 : this와 바인딩?
)

function Dog(x){
  this.x = x
}

Dog.prototype.dognameArr = function(arr){
  
  return arr.map(function(el){
    return `${el} ${this.x}~!`
  })
}

let dog = new Dog('hello')
console.log(dog.dognameArr(['ddoddo', 'bbobbi'])) 
//[ 'ddoddo undefined~!', 'bbobbi undefined~!' ]

위 경우는 new 연산자를 사용하여 this는 생성된 인스턴스 객체에 바인딩 되었다고 한다. 그러나 생성자 함수인 'dognameArr' 안에 있는 반환 값인, map메소드 안에 있는 콜백함수에서의 this는 그렇지 않다고 한다. 이유는 생성자 함수와 객체의 메소드를 제외한 모든 함수(내부 함수, 콜백 함수 포함) 내부의 this는 전역객체를 가리키기 때문이라고 한다.

위 문제를 해결하기 위해서는 3가지 방법이 있다고 한다.

//1. 변수 할당
function Dog(x){
  this.x = x
}

Dog.prototype.dognameArr = function(arr){
	let abc = this // abc에 할당(?)
  return arr.map(function(el){
    return `${el} ${abc.x}~!`
  })
}

let dog = new Dog('hello')
console.log(dog.dognameArr(['ddoddo', 'bbobbi']))
//[ 'ddoddo hello~!', 'bbobbi hello~!' ]
// 2. map - thisArg 사용
unction Dog(x){
  this.x = x
}

Dog.prototype.dognameArr = function(arr){

  return arr.map(function(el){
    return `${el} ${this.x}~!`
  },this) //map method에서 thisArg 지정
}

let dog = new Dog('hello')
console.log(dog.dognameArr(['ddoddo', 'bbobbi']))
//[ 'ddoddo undefined~!', 'bbobbi undefined~!' ]
// 3. bind
function Dog(x){
  this.x = x
}

Dog.prototype.dognameArr = function(arr){

  return arr.map(function(el){
    return `${el} ${this.x}~!`
  }.bind(this)) // bind method 사용
}

let dog = new Dog('hello')
console.log(dog.dognameArr(['ddoddo', 'bbobbi']))
//[ 'ddoddo undefined~!', 'bbobbi undefined~!' ]

화살표 함수에서 this

화살표 함수에서 this는 일반 함수와 달리 정적으로 결정된다고 한다. 화살표 함수의 this는 언제나 상위 스코프의 this를 가르킨다고 한다. 이를 lexical this라고 한다. (마치 렉시컬 스코프와 유사하다고 한다.)

function Dog(x){
  this.x = x
}

Dog.prototype.dognameArr = function(arr){
	//상위 스코프인 dognameArr 메소드 내의 this를 가르킨다고 한다.
  return arr.map(el => `${el} ${this.x}~!`) 
}

let dog = new Dog('hello')
console.log(dog.dognameArr(['ddoddo', 'bbobbi']))
// [ 'ddoddo hello~!', 'bbobbi hello~!' ]

화살표 함수는 call, apply, bind 메소드를 사용하여 this를 변경할 수 없다고 한다.

x = 1
let abc1 = function(){return this.x}
let abc2 = () => this.x


console.log(abc1.call({x : 10})) //10
console.log(abc2.call({x : 10})) //1

화살표 함수를 사용하면 안되는 경우

화살표 함수에서 this는 lexical scope를 따르기 때문에 콜백함수로는 편리할 수도 있다. 하지만 오히려 혼란을 불러 일으킬수도 있다고 하니 주의해야할 경우를 짚어보자.

메소드

const Dog = {
  name: 'ddoddo',
  sayHello: () => console.log(`Hello ${this.name}`)
}

Dog.sayHello() //'Hello undefined'

이 경우 메소드를 정의한 화살표 함수 내부의 this는 메소드를 소유한 객체를 가르키지 않고 상위 스코프인 전역객체를 가르킨다고 한다.

const Dog ={
  name: 'ddoddo',
  sayHello: function(){
    console.log(`Hello ${this.name}`)
  }
}

Dog.sayHello() //'Hello ddoddo'

prototype

prototype에 할당 하는 함수의 경우도 동일한 문제가 발생한다고 한다.

const Dog = {
  name: 'ddoddo'
}
Object.prototype.sayHello = () => console.log(`Hello ${this.name}`)


Dog.sayHello() //'Hello undefined'

생성자 함수

생성자 함수인 경우 생성자 함수는 prototype property를 가지고, prototype property가 가르키는 constructor를 사용한다고 한다. 하지만 화살표 함수인 경우 prototype property를 가지고 있지 않다고 한다.

let dog = () => {}
let dog1 = new dog() // 'TypeError: dog is not a constructor'

마치면서

이번 개념은 너무 오래 걸렸다ㅠ. 짚어봐야할 개념도 많았지만, 한 번에 본다고 이해가 되지 않는 부분도 있어서 복습에 시간을 많이 할애 했다. 그 만큼 얻은 것도 많다.

참조

[화살표 함수, MDN, 2022년06월28일 접속]
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Functions/Arrow_functions
[함수, poiemaweb, 2022년06월28일 접속]
https://poiemaweb.com/js-function#1-%ED%95%A8%EC%88%98-%EC%A0%95%EC%9D%98
[리터럴(Literal)이란?, velog, 2022년06월28일 접속]
https://velog.io/@pjeeyoung/%EB%A6%AC%ED%84%B0%EB%9F%B4
[화살표 함수, poiemaweb, 2022년07월03일 접속]
https://poiemaweb.com/es6-arrow-function

profile
개발자준비중

0개의 댓글