티스토리 뷰

스터디 자료

26장. ES6 함수의 추가 기능

도토리줄기 2022. 11. 1. 21:39

1. ES6이전의 모든 함수는 사용 목적에 따라 명확한 구분이 없었다. 호출 방식에 제약이 없고 프로토타입 객체를 생성했다. 

이러한 문제를 해결하기 위해 ES6에서는 함수를 사용 목적에 따라 세 가지 종류로 구분했다.

일반 함수는 함수 선언문이나 표현식으로 정의한 함수이며 ES6이전의 함수와 차이가 없다. 

 

2.메서드는 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

new obj.foo(); // -> TypeError: obj.foo is not a constructor
new obj.bar(); // -> bar {}

  ES6 함수에서 메서드만이 내부슬롯 [[HomeObject]]를 갖는다. 따라서 super를 사용할 수 있다.

const base = {
  name: 'Lee',
  sayHi() {
    return `Hi! ${this.name}`;
  }
};

const derived = {
  __proto__: base,
  // sayHi는 ES6 메서드다. ES6 메서드는 [[HomeObject]]를 갖는다.
  // sayHi의 [[HomeObject]]는 sayHi가 바인딩된 객체인 derived를 가리키고
  // super는 sayHi의 [[HomeObject]]의 프로토타입인 base를 가리킨다.
  sayHi() {
    return `${super.sayHi()}. how are you doing?`;
  }
};

console.log(derived.sayHi()); // Hi! Lee. how are you doing?

 

3.화살표함수는 function 대신 ()=>{} 의 표현방식을 사용하여 간략하게 함수를 표시하는 방법이다. 

중괄호를 생략한 경우는 함수 몸체의 내부가 표현식(return)인 경우에만 가능하다. 

const arrow = () => const x = 1; // SyntaxError: Unexpected token 'const'

// 위 표현은 다음과 같이 해석된다.
const arrow = () => { return const x = 1; };

객체 리터럴을 반환하는 경우 소괄호()로 감싸주거나 return 을 사용해야한다.

// { id, content }를 함수 몸체 내의 쉼표 연산자문으로 해석한다.
const create = (id, content) => { id, content };
create(1, 'JavaScript'); // -> undefined

화살표 함수도 일급객체이기 때문에 고차함수에 인수로 전달할 수도 있다.

간결하고 가독성이 좋아 훨씬 선호되는 방법이다.

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

 

4.일반함수와 화살표함수의 차이

1)화살표함수는 인스턴스를 생성할 수 없다. 따라서 prototype 프로퍼티도 없고 생성도 하지 않는다.

const Foo = () => {};
// 화살표 함수는 prototype 프로퍼티가 없다.
Foo.hasOwnProperty('prototype'); // -> false

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

function normal(a, a) { return a + a; }
// SyntaxError: Duplicate parameter name not allowed in this context

3)this, arguments, super, new.target 바인딩을 갖지 않는다.

따라서 위의 내용을 화살표함수에서 참조하면 스코프 체인을 통해 상위 스코프를 참조한다.

 

4.화살표 함수의 this는

콜백함수 내부의 this가 외부 함수의 this와 다르기 때문에 발생하는 문제를 해결하기 위해 의도적으로 설계되었다.

 

일반함수로서 호출되는 모든 함수 내부의 this는 전역 객체를 가리킨다. 하지만 클래스 내부는 모두

strict mode(strict 모드는 문법과 런타임 동작을 모두 검사하여, 실수를 에러로 변환하고, 변수 사용을 단순화(Simplifying) 시켜준다.)가 적용된다. 따라서 콜백함수에도 strict모드가 적용되기 때문에 일반함수로서 호출된 map메서드의 모든 함수 내부의 this에는 전역객체가 아니라 undefined가 바인딩되기 때문에 발생하는 문제가 바로 '콜백함수 내부의 this문제'이다.  ①과②의 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']));

이러한 문제점을 ES6에서 화살표 함수를 사용하여 해결할 수 있다. 화살표 함수는 자체의 this바인딩을 갖지 않기 때문에 내부에서 this를 참조하면 상위 스코프의 this를 참조하기 때문이다. 이것이 렉시컬 스코프와 같이 this가 정의된 위치에 의해 결정되는것과 비슷하기 때문에 lexical this라고 한다.

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

  add(arr) {
    return arr.map(item => this.prefix + item);
  }
}

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

5.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?

6.arguments

화살표함수는 arguments바인딩도 갖지 않는다. 똑같이 상위스코프의 arguments를 참조한다.

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

// 화살표 함수 foo의 arguments는 상위 스코프인 전역의 arguments를 가리킨다.
// 하지만 전역에는 arguments 객체가 존재하지 않는다. arguments 객체는 함수 내부에서만 유효하다.
const foo = () => console.log(arguments);
foo(1, 2); // ReferenceError: arguments is not defined

7.Rest파라미터(나머지 매개변수)

매개변수의 이름 앞에 ...을 붙여서 정의한 매개변수를 의미한다. Rest파라미터는 인수들의 목록을 배열로 전달받는다.

일반 매개변수와 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);

ES6에서는 argument객체를 통해 인수를 배열로 변환하는 번거로움을 피하기 위해서,  rest파라미터를 사용하여 가변인자 함수의 인수 목록을 배열로 직접 전달 받을 수 있다.

function sum(...args) {
  // Rest 파라미터 args에는 배열 [1, 2, 3, 4, 5]가 할당된다.
  return args.reduce((pre, cur) => pre + cur, 0);
}
console.log(sum(1, 2, 3, 4, 5)); // 15

 

8.매개변수 기본값

js는 매개변수의 개수와 인수의 개수를 체크하지 않는다. 의도치 않은 결과를 막기 위해, 방어코드가 필요하다.

ES6에서 도입된 매개변수 기본값을 사용하면 인수체크와 초기화를 간소화할 수 있다. 인수를 전달하지 않은 경우와 undefined를 전달한 경우에만 유효하다.

function logName(name = 'Lee') {
  console.log(name);
}

logName();          // Lee
logName(undefined); // Lee
logName(null);      // null

'스터디 자료' 카테고리의 다른 글

[JavaScript] 타이머  (0) 2022.12.06
브라우저의 렌더링 과정  (0) 2022.11.18
37장 Set과 Map  (0) 2022.11.18
24장 클로저  (0) 2022.10.25
12장 함수  (0) 2022.10.10
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/01   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함