한준호

[Modern javascript] ES6 함수의 추가 기능 본문

Frontend/modern javascript

[Modern javascript] ES6 함수의 추가 기능

igoman2 2021. 9. 2. 00:42
728x90
ES6 함수의 구분 constructor prototype super arguments
일반 함수 O O X O
메서드 X X O O
화살표 함수 X X X

 

■ 메서드

ES6에서 메서드는 메서드 축약 표현으로 정의된 함수만을 의미한다.

const obj = {
  x: 1,
  // foo는 메서드이다.
  foo() { return this.x; },
  // bar에 바인딩된 함수는 메서드가 아닌 일반 함수이다.
  bar: function() { return this.x; }
};

console.log(obj.foo()); // 1
console.log(obj.bar()); // 1

- ES6에서 메서드는 인스턴스를 생성할 수 없는 non-constructor다. 따라서 생성자 함수로 호출할 수 없다.

- 인스턴스를 생성할 수 없으므로 prototype 프러퍼티가 없고 프로토타입도 생성하지 않는다.

- 자신을 바인딩한 객체를 가리키는 내부 슬롯 [[HomeObject]]를 가지므로 super를 사용할 수 있다.

 

 

■ 화살표 함수

const multiply = (x, y) => x * y;
multiply(2, 3); // -> 6

const arrow = (x, y) => { ... };

const arrow = x => { ... };

const arrow = () => { ... };

// concise body
const power = x => x ** 2;
power(2); // -> 4

// 위 표현은 다음과 동일하다.
// block body
const power = x => { return x ** 2; };

const arrow = () => { const x = 1; };

const create = (id, content) => ({ id, content });
create(1, 'JavaScript'); // -> {id: 1, content: "JavaScript"}

// 위 표현은 다음과 동일하다.
const create = (id, content) => { return { id, content }; };

// ES5
[1, 2, 3].map(function (v) {
  return v * 2;
});

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

 

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

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

2. 중복된 매개변수 이름을 선언할 수 없다.

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

    => 따라서 함수 내부에서 위 키워드를 참조하면 스코프 체인을 통해 상위 스코프의 것을 참조한다.

 

◎ 콜백 함수 내부의 this 문제

class Prefixer {
  constructor(prefix) {
    this.prefix = prefix;
  }

  add(arr) {
    // add 메서드는 인수로 전달된 배열 arr을 순회하며 배열의 모든 요소에 prefix를 추가한다.
    // ①
    return arr.map(function (item) {
      return this.prefix + item; // ②
      // -> TypeError: Cannot read property 'prefix' of undefined
    });
  }
}

const prefixer = new Prefixer('-webkit-');
console.log(prefixer.add(['transition', 'user-select']));

일반 함수로 호출되는 함수 내부의 this는 전역 객체를 가리킨다. 그런데 클래스 내부의 모든 코드엔 strict mode가 적용 되므로 일반 함수로 호출된 함수 내부의 this는 전역 객체가 아니라 undefined가 바인딩 된다.

콜백 함수의 this와 외부 함수의 this가 다른 값을 가리키고 있다.

 

화살표 함수를 이용하여 다음과 같이 해결할 수 있다.

...
add(arr) {
  return arr.map(function (item) {
    return this.prefix + ' ' + item;
  }.bind(this)); // this에 바인딩된 값이 콜백 함수 내부의 this에 바인딩된다.
}
...

화살표 함수 내에서 this를 참조하면 상위 스코프의 this를 참조하는 것을 lexical this라고 한다.

화살표 함수를 제외한 모든 함수는 this 바인딩 존재. 하지만 화살표 함수는 없다.

-> this를 참조하면 일반 식별자처럼 스코프 체인을 통해 상위 스코프에서 this 탐색.

-> Function.prototype.call, apply, bind 메서드를 사용해도 화살표 함수 내부의 this를 교체할 수 없다.

 

메서드를 화살표 함수로 정의하는 것은 피해야 한다. 메서드 축약 표현을 권장.

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

// sayHi 프로퍼티에 할당된 화살표 함수 내부의 this는 상위 스코프인 전역의 this가 가리키는
// 전역 객체를 가리키므로 이 예제를 브라우저에서 실행하면 this.name은 빈 문자열을 갖는
// window.name과 같다. 전역 객체 window에는 빌트인 프로퍼티 name이 존재한다.
person.sayHi(); // Hi


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

person.sayHi(); // Hi Lee

 

단, 프러퍼티를 동적으로 추가할 때는 일반 함수를 할당한다.

// Bad
function Person(name) {
  this.name = name;
}

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

const person = new Person('Lee');
// 이 예제를 브라우저에서 실행하면 this.name은 빈 문자열을 갖는 window.name과 같다.
person.sayHi(); // Hi


// Good
function Person(name) {
  this.name = name;
}

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

const person = new Person('Lee');
person.sayHi(); // Hi Lee

 

클래스 필드 정의 제안을 사용하여 클래스 필드에 화살표 함수를 할당할 수도 있다.

// Bad
class Person {
  // 클래스 필드 정의 제안
  name = 'Lee';
  sayHi = () => console.log(`Hi ${this.name}`);
}

const person = new Person();
person.sayHi(); // Hi Lee


class Person {
  constructor() {
    this.name = 'Lee';
    // 클래스가 생성한 인스턴스(this)의 sayHi 프로퍼티에 화살표 함수를 할당한다.
    // sayHi 프로퍼티는 인스턴스 프로퍼티이다.
    this.sayHi = () => console.log(`Hi ${this.name}`);
  }
}

클래스 필드에서 참조하는 this는 상위 스코프 constructor이다. 즉, constructor 내부의 this 바인딩과 같으므로 생성한 인스턴스를 나타낸다. 하지만 sayHi 메서드는 프로토타입 메서드가 아니라 인스턴스 메서드가 되므로 역시 ES6 메서드 축약 표현을 사용하는 것을 권장한다.

 

◎ super, arguments

화살표 함수는 함수 자체의 super, arguments 바인딩을 갖지 않으므로 상위 스코프를 참조한다.

화살표 함수에서는 자기 자신에게 전달된 arguments는 확인할 수 없고 상위 스코프만 확인한다.

 

 

■ Rest 파라미터

- 인수들의 목록을 배열로 전달받는다.

function foo(param, ...rest) {
  console.log(param); // 1
  console.log(rest);  // [ 2, 3, 4, 5 ]
}

foo(1, 2, 3, 4, 5);

function bar(param1, param2, ...rest) {
  console.log(param1); // 1
  console.log(param2); // 2
  console.log(rest);   // [ 3, 4, 5 ]
}

bar(1, 2, 3, 4, 5);

위 결과처럼 남은 인수들을 배열로 만들기 때문에 가장 뒤에 와야 한다.

 

장점

1. arguments 객체는 유사 배열이므로 함수 내에서 인수들을 배열로 사용할 때 변환을 해주어야 하는데, Rest 파라매터는 그럴 필요가 없다.

2. 화살표 함수는 arguments 객체를 갖지 않으므로 화살표 함수로 가변 인자 함수를 구현할 때는 Rest 파라미터를 활용할 수 있다.

 

 

■ 매개변수 기본값

- 인수가 전달되지 않은 매개변수는 undefined로 초기화 되므로 다음과 같은 버그가 발생할 수 있다.

function sum(x, y) {
  return x + y;
}

console.log(sum(1)); // NaN​

 

따라서 기본값을 할당하는 것이 좋다.

- 매개변수 기본값은 매개변수에 인수를 전달하지 않은 경우와 undefined를 전달한 경우에만 유효하다.

- Rest 파라미터에는 기본값을 지정할 수 없다.

function sum(x, y) {
  // 인수가 전달되지 않아 매개변수의 값이 undefined인 경우 기본값을 할당한다.
  x = x || 0;
  y = y || 0;

  return x + y;
}

console.log(sum(1, 2)); // 3
console.log(sum(1));    // 1

 

728x90

'Frontend > modern javascript' 카테고리의 다른 글

[Modern javascript] 배열(2)  (0) 2021.09.14
[Modern javascript] 배열(1)  (0) 2021.09.12
[Modern javascript] 클래스(2)  (0) 2021.09.01
[Modern javascript] 클래스(1)  (0) 2021.08.31
[Modern javascript] 클로저  (0) 2021.08.31
Comments