모던 자바스크립트 Deep Dive : 26장 ES6 함수의 추가 기능

EdLee·2022년 11월 16일

javascript

목록 보기
16/37

26장 ES6 함수의 추가 기능

1. 함수의 구분


  • ES6 이전까지의 함수는 목적에 따라 다양한 방법으로 호출이 가능했다
  • 이것은 실수를 유발하고 성능에도 악영향을 끼친다
// ES6 이전의 모든 함수는 callable이면서 constructor였다
var foo = function () {
  return 1;
};

// 일반함수
foo(); // → 1


// 생성자 함수로서 호출
new foo(); // → foo {}

// 메서드로서 호출
var obj = { foo: foo };
obj.foo(); // → 1

문제점

  • 객체에 바인딩된 함수를 생성자 함수로 호출 가능하다.
  • 콜백 함수도 생성자 함수로 호출이 가능하다.
  • 이런 함수들은 prototype 프로퍼티를 갖고 불필요한 프로토타입 객체를 생성하여 성능을 떨어뜨린다.
  • 위와 같은 문제점을 해결하기 위해, ES6 부터는 함수를 사용 목적에 따라 세 가지 종류로 명확히 구분했다.
ES6 함수의 구분constructorprototypesuperarguments
일반함수OOXO
메서드XXOO
화살표 함수XXXX

😂외울게 늘었다.

2. 메서드


  • ES6에서는 메서드에 대한 정의가 명확해졌다
  • ES6 전 : 메서드 - 객체에 바인딩된 함수
  • ES6 후 : 메서드 - 메서드 축약 표현으로 정의된 함수
const obj = {
  x: 1,

  // 메서드로 정의
  foo () { return this.x; }
  
  // 일반 함수처럼 정의
  bar: function () { return this.x;}
};
console.log(obj.foo()); // 1
console.log(obj.bar()); // 1

// ES6의 메서드는 생성자 함수로써 호출 불가
new obj.foo(); // -> TypeError: obj.foo is not a constructor
new obj.bar(); // -> bar{}

참고 : 표준 빌트인 객체가 제공하는 프로토타입 메서드와 정적 메서드는 모두 non-constructor

String.prototype.toUpperCase.prototype; // -> undefined
String.fromCharCode.prototype; // -> undefined

Number.prototype.toFixed.prototype; // -> undefined
Number.isFinite.prototype; // -> undefined

Array.prototype.map.prototype; // -> undefined
Array.from.prototype; // -> undefined
  • ES6 메서드는 자신을 바인딩한 객체를 가리키는 내부 슬롯 [[HomeObject]]를 갖는다.
  • super 참조는 이 내부 슬롯을 사용해 수퍼 클래스의 메서드를 참조하므로, ES6메서드는 super 사용이 가능한 것
  • 반면 다른 함수는 [[HomeObject]]가 없으므로, super 사용 불가능

3. 화살표 함수


  • 화살표 함수는 간략한 표현의 장점을 갖는다
  • 특히 콜백 함수 내부에서 this가 전역 객체를 가리키는 문제를 해결하기 위한 대안으로 유용하다

3.1 화살표 함수 정의

  • 함수 정의 : 화살표 함수는 함수 표현식으로 정의해야 한다
const multiply = (x, y) => x * y;
multiply(2,3); // -> 6

// 매개변수의 개수에 따라 다음과 같은 형태로 사용 가능
const arrow = (x, y) => { ... };

const arrow = x => { ... }; // 하나라면 소괄호 생략 가능

const arrow = () => { ... }; // 매개변수가 없다면 소괄호 생략 불가능

// 함수 몸체의 중괄호는 경우에 따라 생략 가능
const power = x => x ** 2; // 몸체가 하나의 표현식인 문이라면 중괄호 생략 가능
const power = x => { return x ** 2;};
power(2); // -> 4

// 표현식이 아닌 문은 오류 발생
const arrow = () => const x = 1; // SyntaxError: Unexpected token 'const'
const arrow = () => {return const x = 1;}; // 이렇게 해석되기 때문

// 객체 리터럴 반환시, 객체 리터럴을 소괄호()로 감싸주어야 한다. 이 규칙이 매번 가장 기억안남🤬
const create = (id, content) => ({id, content});
const create = (id, content) => { return { id, content };}; // 위 아래 동일
create(1, 'JavaScript');

// 소괄호로 감싸지 않는다면?
const create = (id, content) => {id, content}; // 그냥 이렇게 생긴 그대로 해석하고 return은 안붙여준다.

// 즉시 실행 함수로 사용 가능
const person = (name => ({
  sayHi() { return `Hi? My name is ${name}.`;}
}))('Lee');

console.log(person.sayHi());
  • 특히 화살표 함수는 다음처럼 콜백 함수로서 정의할 때 유용하다.
// ES5
[1,2,3].map(function (v) {
  return v * 2;
});

// ES6
[1,2,3].map(v => v * 2); // -> [2, 4, 6]

3.2 화살표 함수와 일반 함수의 차이

1. 화살표 함수는 인스턴스를 생성할 수 없는 non-constructor다.

  • 인스턴스 생성 불가
  • prototype 프로퍼티가 없고 프로토 타입도 생성하지 않는다.

2. 중복된 매개변수 이름을 선언 불가

3. 함수 자체의 this, arguments, super, new.target 바인딩을 갖지 않는다.

  • 만약 화살표 내부에서 this, arguments, super, new.target을 참조하면 스코프 체인을 통해 상위 스코프의 this, arguments, super, new.target를 참조한다
  • 만약 화살표 함수끼리 중첩외어 있다면, 그 위의 상위 함수를 참조한다.

3.3 this

  • 화살표 함수와 일반 함수와 구별되는 가장 큰 특징은 바로 this
  • 렉시컬 this : 화살표 함수는 자체적으로 바인딩된 this가 없고, 상위 스코프 this를 그대로 참조한다
    => 바로 이것이 화살표 함수가 콜백 함수에 유용하면서, 메서드를 통한 바인딩이 불가능한 이유이다.
// 중첩 함수 foo의 상위 스코프는 즉시 실행 함수다.
// 따라서 화살표 함수 foo의 this는 상위 스코프인 즉시 실행 함수의 this를 가리킨다.
(function() {
  const foo = () => console.log(this);
  foo();
}.call({ a: 1 })); // {a: 1}

// bar 함수는 화살표 함수를 반환한다.
// bar 함수가 반환한 화살표 함수의 상위 스코프는 화살표 함수 bar다.
// 하지만 화살표 함수는 함수 자체의 this 바인딩을 갖지 않으므로 bar 함수가 반환한
// 화살표 함수 내부에서 참조하는 this는 화살표 함수가 아닌 즉시 실행 함수의 this를 가리킨다.
(function() {
  const bar = () => () => console.log(this);
  bar()();
}.call({ a: 1 })); // { a: 1}

// increase 프로퍼티에 할당한 화살표 함수의 상위 스코프는 전역
// 따라서 increase 프로퍼티에 할당한 화살표 함수의 this는 전역 객체를 가리킨다.
const counter = {
  num: 1,
  increase: () => ++this.num
};
console.log(counter.increase()); // NaN
  • 클래스 필드 정의 제안을 사용해 클래스 필드에 화살표 함수를 할당할 수도 있다.
  • 하지만 클래스 필드에 할당한 화살표 함수는 프로토타입 메서드가 아니라 인스턴스 메서드가 되므로, 메서드 정의 시에는 ES6 메서드를 사용 할 것

3.4 super

  • 화살표 함수는 자체의 super 바인딩을 갖지 않는다.
  • this와 같이 상위 스코프의 super를 참조한다.
class Base {
  constructor(name) {
    this.name = name;
  }

  sayHi() {
    return `Hi! ${this.name}`;
  }
}

class Derived extends Base {
  // 화살표 함수의 super는 상위 스코프인 constructor의 super를 가리킨다.
  sayHi = () => `${super.sayHi()} how are you doing?`
}

const derived = new Derived('Lee')
console.log(derived.sayHi()); // Hi! Lee how are you doing?

3.5 arguments

  • 상동
(function () {
  // 화살표 함수 foo의 arguments는 상위 스코프인 즉시 실행 함수의 arguments를 가리킨다.
  const foo = () => console.log(arguments); // [Arguments] { '0': 1, '1': 2}
  foo(3,4); // 3,4 무시됨
} (1,2)); // 얠 가져옴

// 전역에는 arguments 객체가 없다. 따라서 에러
const foo = () => console.log(arguments);
foo(1,2); // ReferenceError: arguments is not defined

4. Rest 파라미터


4.1 기본 문법

  • Rest 파라미터는 함수에 전달된 인수들의 목록을 배열로 전달 받는다.
function foo(...rest) {
  // 매개변수 rest는 인수들의 목록을 배열로 전달받는 Rest 파라미터
  console.log(rest); // [1,2,3,4,5]
}
foo(1,2,3,4,5);

// 매개변수와 Rest는 같이 사용 가능. 인수는 매개변수와 Rest 파라미터에 순차적으로 할당된다.
function foo(param1, param2, ...rest) {
  console.log(param1); // 1
  console.log(param2); // 2
  console.log(rest); // [3,4,5]
}
foo(1,2,3,4,5);

// Rest는 반드시 마지막 파라미터어야 한다
function foo(...rest, param1, param2){}
foo(1,2,3,4) // SyntaxError

// Rest는 단 하나만 가능
function foo(...rest1, ...rest2){}
foo(1,2,3,4) // SyntaxError

// 함수 length 프로퍼티와 무관
function foo(...rest) {}
console.log(foo.length); // 0
function foo(a,b,c,...rest) {}
console.log(foo.length); // 3

4.5 Rest파라미터와 arguments객체

  • 둘의 차이가 뭔데?
  • arguments : 함수 호출 시 전달된 인수들의 정보를 담고 있는 순회 가능한 유사 배열 객체. 함수 내에서 지역 변수처럼 사용 가능.
    => 그러나 배열이 아니라서, FUnction.prototype.call, apply 메서드를 사용해 arguments 객체를 배열로 변환해야 한다.
  • Rest : 가변 인자 함수의 인수 목록을 배열로 직접 전달 받을 수 있다.
// arguments의 경우
function sum() {
  var array = Array.prototype.slice.call(arguments);
  return array.reduce(function (pre, cur) {
    return pre + cur;
  }, 0);
}
console.log(sum(1,2,3,4,5)); // 15

// Rest의 경우
function sum(...args) {
  return args.reduce((pre, cur) => pre + cur, 0);
}
console.log(sum(1,2,3,4,5)); // 15

화살표 함수는 arguments 객체가 바인딩되어 있지 않다. 대신 Rest 파라미터는 지원한다.

5. 매개변수 기본값


  • 매개변수의 기본값은 undefined
// 매개변수가 모자라면 1 + undefined = NaN
function sum(x,y) {
  return x + y;
}
console.log(sum(1)); // NaN
  • 위와 같은 상황을 방지하기 위해 기본값을 할당하는 방어코드가 필요
function sum(x = 0, y = 0) {
  return x + y;
}
console.log(sum(1,2)); // 3
console.log(sum(1)); // 1
  • 매개변수 기본값이 설정된 매개변수는 length에서 제외된다
function sum(x, y = 1, z = 1) {
  console.log(arguments);
}
console.log(sum.length); // 1

function sum(x, y, z = 1) {
  console.log(arguments);
}
console.log(sum.length); // 2
  • Rest 파라미터에는 기본값 지정 불가능
function foo(...rest = []) {
  console.log(rest);
}
// SyntaxError

0개의 댓글