JavaScript - Function vs Arrow Function

modolee·2020년 8월 30일
7
post-thumbnail

Arrow Function의 정의

  • ES6 부터 추가 된 Function 표현 형태
  • 일반 Function 표현에 비해 구문이 짧고 자신의 this, arguments, super 또는 new.target을 바인딩 하지 않음
  • 항상 익명 함수의 형태
  • 이 함수 표현은 메소드 함수가 아닌 곳에 가장 적합하며, 그래서 생성자로서 사용할 수 없음

기본 구문

(param1, param2,, paramN) => { statements }
(param1, param2,, paramN) => expression
// 다음과 동일함:  => { return expression; }

// 매개변수가 하나뿐인 경우 괄호는 선택사항:
(singleParam) => { statements }
singleParam => { statements }

// 매개변수가 없는 함수는 괄호가 필요:
() => { statements }

고급 구문

// 객체 리터럴 표현을 반환하기 위해서는 함수 본문(body)을 괄호 속에 넣음:
params => ({foo: bar})

// 나머지(Rest) 매개변수 및 기본(Default) 매개변수를 지원함
(param1, param2, ...rest) => { statements }
(param1 = defaultValue1, param2,, paramN = defaultValueN) => { statements }

// 매개변수 목록 내 비구조화도 지원됨
var f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c;
f();  // 6

Function 표현과의 비교

// Function (ES5)
var selected = allJobs.filter(function (job) {
  return job.isSelected();
});

// Arrow Function (ES6)
var selected = allJobs.filter(job => job.isSelected());

// Function (ES5)
var total = values.reduce(function (a, b) {
  return a + b;
}, 0);

// Arrow Function (ES6)
var total = values.reduce((a, b) => a + b, 0);

Arrow Function 특징

짧은 함수

  • 일반 Function 표현 보다 짧게 표현할 수 있음
var elements = [
  'Hydrogen',
  'Helium',
  'Lithium',
  'Beryllium'
];

// 이 문장은 배열을 반환함: [8, 6, 7, 9]
elements.map(function(element) {
  return element.length;
});

// 위의 일반적인 함수 표현은 아래 화살표 함수로 쓸 수 있다.
elements.map((element) => {
  return element.length;
}); // [8, 6, 7, 9]

// 파라미터가 하나만 있을 때는 주변 괄호를 생략할 수 있다.
elements.map(element => {
  return element.length;
}); // [8, 6, 7, 9]

// 화살표 함수의 유일한 문장이 'return'일 때 'return'과
// 중괄호({})를 생략할 수 있다.
elements.map(element => element.length); // [8, 6, 7, 9]

// 이 경우 length 속성만 필요하므로 destructuring 매개변수를 사용할 수 있다.
// 'length'는 우리가 얻고자 하는 속성에 해당하는 반면,
// lengthFooBArX'는 변경 가능한 변수의 이름일 뿐이므로
// 원하는 유효한 변수명으로 변경할 수 있다.
elements.map(({ length: lengthFooBArX }) => lengthFooBArX); // [8, 6, 7, 9]

// destructuring 파라미터 할당도 아래와 같이 작성할 수 있다.
// 이 예에서는 'length'값을 구성 속성에 할당하지 않는다. 대신, 문자 이름
// "length" 변수 자체는 개체에서 검색할 속성으로 사용된다.
elements.map(({ length }) => length); // [8, 6, 7, 9]

바인딩 되지 않은 this

  • 일반 Function 표현에서는 모든 새로운 함수가 자신의 this 값을 정의했음
  • 함수 종류에 따라 this가 가리키는 객체가 다름
    • 생성자 : 새로운 객체
    • strict 모드에서의 함수 : undefined
    • 객체 메서드 : 베이스 객체
    • callback 함수 : 전역 객체 window
  • Arrow Function에서는 전역 컨텍스트에서 실행 될 때 this를 새로 정의하지 않음
    • 코드에서 바로 바깥의 함수(혹은 class)의 this(Lexcial this) 값을 사용
    • this를 클로저 값으로 처리하는 것과 같음
// 기존 Function 표현
function Person() {
  // Person() 생성자는 `this`를 자신의 인스턴스로 정의.
  this.age = 0;

  setInterval(function growUp() {
    // 비엄격 모드에서, growUp() 함수는 `this`를
    // 전역 객체로 정의하고, 이는 Person() 생성자에
    // 정의된 `this`와 다름.
    this.age++;
  }, 1000);
}

var p = new Person();

//--------------------------------------------------------//

// ES 3/5에서는 this 값을 고정시킬 수 있게 지역 변수에 할당하여 해결
function Person() {
  var that = this;  
  that.age = 0;

  setInterval(function growUp() {
    // callback은  `that` 변수를 참조하고 이것은 값이 기대한 객체이다.
    that.age++;
  }, 1000);
}

//--------------------------------------------------------//

// Arrow Function
function Person(){
  this.age = 0;

  setInterval(() => {
    this.age++; // |this|는 Person 객체를 참조
  }, 1000);
}

var p = new Person();

Arrow Function을 사용해서는 안되는 경우

메서드 (method)

  • 메서드로 정의한 Arrow Function 내부의 this는 메서드를 소유한 객체, 즉 메서드를 호출한 객체를 가리키지 않고 상위 컨텍스트인 전역 객체 window를 가리킴
// Bad
const person = {
  name: 'Lee',
  sayHi: () => console.log(`Hi ${this.name}`)
};

person.sayHi(); // Hi undefined

// Good
const person = {
  name: 'Lee',
  sayHi() { // === sayHi: function() {
    console.log(`Hi ${this.name}`);
  }
};

person.sayHi(); // Hi Lee

프로토타입 (prototype)

  • 메서드의 경우와 동일
// Bad
const person = {
  name: 'Lee',
};

Object.prototype.sayHi = () => console.log(`Hi ${this.name}`);

person.sayHi(); // Hi undefined

// Good
const person = {
  name: 'Lee',
};

Object.prototype.sayHi = function() {
  console.log(`Hi ${this.name}`);
};

person.sayHi(); // Hi Lee

생성자 함수 (constructor)

  • Arrow Function은 prototype 프로퍼티를 가지고 있지 않음
const Foo = () => {};

// 화살표 함수는 prototype 프로퍼티가 없다
console.log(Foo.hasOwnProperty('prototype')); // false

const foo = new Foo(); // TypeError: Foo is not a constructor

addEventListener 함수의 callback 함수 (내부에서 this를 사용하는 경우)

  • addEventListener 함수의 콜백 함수를 화살표 함수로 정의하면 this가 상위 컨택스트인 전역 객체 window를 가리킴
// Bad
var button = document.getElementById('myButton');

button.addEventListener('click', () => {
  console.log(this === window); // => true
  this.innerHTML = 'Clicked button';
});

// Good
var button = document.getElementById('myButton');

button.addEventListener('click', function() {
  console.log(this === button); // => true
  this.innerHTML = 'Clicked button';
});

호이스팅

Arrow Function의 호이스팅

console.log(x()) // 에러! x is not a function

var x = () => {  
  return 100;  
}
  • 위의 코드를 실행하면 아래와 같은 에러가 발생

  • 이건 Arrow Function 에서만 발생하는 에러는 아님
  • 일반 Function의 경우에도 함수 표현식을 이용하는 경우, 함수의 선언 부분만 끌어 올리게 되어 동일한 에러가 발생

일반 Function의 호이스팅 비교

  • 함수 선언문 : 함수 정의부만 존재하고 별도의 할당 명령이 없는 것
    • 호이스팅 시 함수 전체를 끌어 올림
  • 함수 표현식 : 정의한 함수를 별도의 변수에 할당하는 것
    • 호이스팅 시 변수로 취급되어 선언 부분만 끌어 올림
// 익명 함수를 이용한 함수 표현식
console.log(x()) // 에러! x is not a function

var x = function() {  
  return 100;  
}

// 기명 함수를 이용한 함수 표현식
console.log(x()) // 에러! x is not a function

var x = function y() {  
  return 100;  
}

// 함수 선언문
console.log(x()) // 정상 동작. 100

function x() {
  return 100;
}

출처

profile
기초가 탄탄한 백엔드 개발자를 꿈꿉니다.

0개의 댓글