[딥다이브] 012. es6 함수의 추가 기능

1. 함수의 구분

  • ES6 (ECMAScript 2015)

    • const and let
    • Default parameters(기본 매개 변수)
    • Arrow functions(화살표 함수)
    • Template Literals(템플릿 리터럴)
    • Destructuring (구조분해할당)
    • Import and export(가져오기 및 내보내기)
    • Promise (프로미스)
    • Rest parameter (나머지 매개 변수)
    • Classes(클래스)
    • Spread Opertor (전개구문 연산자)
  • ES6 이전

    • 모든 함수가 callable (호출할 수 있는 함수객체)이면서 constructor(인스턴스를 생성할 수 있는 함수 객체)
    • 사용목적에 따라 명확한 구분 없음
    • 생성자 함수로 호출하지 않아도 프로토타입 객체 생성
  • ES6 함수

💡 `arguments` : 함수에 전달된 인수에 해당하는 `Array` 형태의 객체, 함수 내에서 이용 가능한 지역 변수 `super` : 부모 오브젝트의 함수를 호출할 때 사용

함수의 구분

2. ES6 사양에서 메서드

💡 `프로퍼티` : 객체의 상태를 나타내는 값(data) `메서드` : 프로퍼티(상태 데이터)를 참조하고 조작할 수 있는 동작(behavior) (딥다이브 10장)
  • 메서드 축약 표현으로 정의된 함수만을 의미
  • 인스턴스를 생성할 수 없는 non-constructor
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 {}

obj.foo.hasOwnProperty("prototype"); // false
obj.bar.hasOwnProperty("prototype"); // true
  • super
    • ES6 메서드는 자신을 바인딩한 객체를 가리키는 내부 슬롯 [[HomeObject]]를 가짐.
    • super 참조는 내부 슬롯 [[HomeObject]]를 사용하여 수퍼클래스의 메서드를 참조하므로 내부 슬롯 [[HomeObject]]를 갖는 ES6 메서드는 super 키워드를 사용가능.
const base = {
  name: "Lee",
  sayHi() {
    return `Hi! ${this.name}`;
  },
};

const derived = {
  __proto__: base,
  sayHi() {
    return `${super.sayHi()}. how are you doing?`;
  },
};

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

3. 화살표 함수

  • 화살표 함수(Arrow Function) : function 키워드 대신 화살표(=>, fat arrow)를 사용하여 기존의 함수 정의 방식보다 간략하게 함수를 정의함
  • 표현만 간략한 것이 아니라 내부 동작도 기존의 함수보다 간략함
  • 콜백 함수 내부에서 this가 전역 객체를 가리키는 문제를 해결하기 위한 대안으로 유용

3.1. 화살표 함수 정의

함수 정의

화살표 함수는 함수 선언문으로 정의할 수 없고, 함수 표현식으로 정의해야 합니다. 호출 방식은 기존 함수와 동일합니다.

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

매개변수 선언

매개변수가 여러 개인 경우 소괄호 () 안에 매개변수를 선언합니다. 매개변수가 한 개인 경우 소괄호 ()를 생략할 수 있습니다. 매개변수가 없는 경우 소괄호 ()를 생략할 수 없습니다.

const arrow1 = (x, y) => { ... };
const arrow2 = x => { ... };
const arrow3 = () => { ... };

함수 몸체 정의

  • 함수 몸체를 감싸는 중괄호  {} 생략 가능한 경우
    • 함수 몸체가 하나의 문으로 구성되었을 때
    • 함수 몸체 내부의 문이 값으로 평가될 수 있는 표현식일 때
const power = (x) => x ** 2;
// const power = (x) => { return x ** 2; };
power(2); // 4
  • 중괄호  {} 생략 불가능한 경우
    • 객체 리터럴을 반환하는 경우 소괄호() 로 감쌈 (소괄호로 감싸지 않으면 쉼표 연산자로 잘못 해석)
      const create = (id, content) => ({ id, contnet });
      // const create = (id, contnet) => { return { id, contnet }; };
      create(1, "JavaScript"); // { id: 1, content: "JavaScript" }
    • 함수 몸체가 여러개의 문으로 구성된 경우. 이 때 반환값이 있다면 명시적으로 반환
      const sum = (a, b) => {
        const result = a + b;
        return result;
      };
  • 즉시 실행함수(IIFE)로 사용가능
const person = ((name) => ({
  sayHi() {
    return `Hi? My name is ${name}.`;
  },
}))("Lee");

console.log(person.sayHi()); // Hi? My name is Lee.
  • 고차 함수(Higher-Oreder Function, HOF)에 인수로 전달가능
[1, 2, 3].map((v) => v * 2); // [2, 4, 6]

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

  • 화살표 함수는 non-constructor (인스턴스 생성 불가능)
    • prototype 프로퍼티 없음

    • 프로토타입 생성하지 않음

      const Foo = () => {};
      Foo.hasOwnProperty("prototype"); // false
  • 중복된 매개변수 선언 불가능
    //일반함수 (strict mode가 아닐 때)
    function normal(a, a) {
      return a + a;
    }
    
    console.log(normal(1, 2)); // 4
    
    //화살표함수
    const arrow = (a, a) => a + a; // SyntaxError화살표 함수에서는 중복된 매개변수 이름을 선언하면 에러가 발생.
  • 자체 this, arguments, new.target 바인딩을 갖지 않음.
    내부에서 이들을 참조하면 스코프체인을 통해 상위 스코프의 식별자 참조

3.3 this

  • this
    • 화살표 함수가 일반함수와 구별되는 가장 큰 특징
    • 콜백함수 내부의 this가 외부의 함수의 this와 달라서 생기는 문제를 해결하기 위해,
      의도적으로 설계
  • 콜백 함수 내부의 this문제를 방어하는 방법 : ES6이전
    • 문제상황

      
      class Prefixer {
        constructor(prefix) {
          this.prefix = prefix;
        }
      
        prefixArray(arr) {
          // ① this - 매서드를 호출한 객체(prefixer)
          // 인수로 전달된 배열 arr을 순회하며 배열의 모든 요소에 prefix를 추가한다.
          return arr.map(function (item) {
            return this.prefix + ' ' + item; // ② this - undefined
            // -> TypeError: Cannot read property 'prefix' of undefined
          });
        }
      }
      
      // 출력 기대값 : ['Hello Park','Hello Lee']
      
      const prefixer = new Prefixer('Hello');
      console.log(prefixer.prefixArray(['Park', 'Lee'])); // error
      
    • 방법 1 : 객체를 가리키는 this를 회피시킨 후 내부에서 사용

      class Prefixer {
        constructor(prefix) {
          this.prefix = prefix;
        }
      
        add(arr) {
          //this를 일단 회피시킨다.
          const that = this;
          return arr.map(function (item) {
            return that.prefix + " " + item;
          });
        }
      }
    • 방법2 : Array.prototpye.map의 두번째 인수로 this로 사용할 객체 전달

      class Prefixer {
        constructor(prefix) {
          this.prefix = prefix;
        }
      
        add(arr) {
          return arr.map(function (item) {
            return this.prefix + " " + item;
          }, this);
        }
      }
    • 방법3 : function.prototype.bind 메서드를 사용해 호출객체를 가리키는 this 바인딩

      class Prefixer {
        constructor(prefix) {
          this.prefix = prefix;
        }
      
        add(arr) {
          return arr.map(
            function (item) {
              return this.prefix + " " + item;
            }.bind(this)
          );
        }
      }
  • 콜백 함수 내부의 this 문제를 방어하는 방법 : 화살표함수
    • 화살표 함수는 함수 자체의 this 바인딩을 갖지 않음
    • 화살표 함수 내부에서 this 를 참조하면, 상위스코프의 this 를 참조함 (Lexical this)
    • 화살표 함수가 전역함수라면, this는 전역 객체를 가리킴
    • 화살표함수와 화살표함수가 중첩되어있다면,
      스코프체인 상 가장 가까운 상위함수 중 화살표함수가 아닌 함수의 this 참조
      ```jsx
      // 중첩 함수 foo의 상위 컨텍스트는 즉시 실행 함수이다.
      // 화살표 함수 foo의 this는 즉시 실행 함수의 this를 가리킨다.
      (function () {
        const foo = () => console.log(this);
        foo();
      }).call({ a: 1 }); // { a: 1 }
      ```
  • 화살표함수는 this 바인딩을 갖지 않음
    • Function.prototype.callFunction.prototype.applyFunction.prototype.bind 메서드를 사용해도 화살표 함수 내부 this 교체 불가
      window.x = 1;
      
      const normal = function () {
        return this.x;
      };
      const arrow = () => this.x;
      
      console.log(normal.call({ x: 10 })); // 10
      console.log(arrow.call({ x: 10 })); // 1
    • 메서드를 화살표함수로 정의하는 것을 피해야함. (프로퍼티에 화살표함수 할당한 경우에도)
      
      // 메서드를 호출한 객체를 가리키지 않고 상위 스코프의 this를 가리킴
      
      const person = {
        name: "Lee",
        sayHi: () => console.log(`Hi, ${this.name}`),
      };
      
      person.sayHi(); // Hi
    • 메서드를 정의할 때 ES6메서드 축약표현으로 정의한 ES6메서드 사용
      
      const person = {
        name: "Lee",
        sayHi(){
      			console.log(`Hi, ${this.name}`);
      	} 
      };
      
      person.sayHi(); // Hi Lee
  • 클래스 필드에 화살표 함수 할당 (클래스 필드 정의 제안 사용)
    • 화살표 함수 내부에서 this가 상위스코프의 this 바인딩을 참조
    • 이 때 상위 스코프는 constructor
    • 클래스필드에 할당한 화살표함수 내부에서 참조하는 this = constructor 내부의 this 바인딩 = 클래스가 생성한 인스턴스
    • 이 때 화살표 함수는 프로토타입 메서드가 아니라 인스턴스 메서드
      class Person {
        name = "Lee";
        sayHi = () => console.log(`Hi ${this.name}`);
      
        // constructor() {
        //   this.name = "Lee";
        //   this.sayHi = () => console.log(`Hi ${this.name}`);
        // }
      }
      
      const person = new Person();
      person.sayHi(); // Hi Lee

3.4. super

  • 화살표 함수는 자체 super 바인딩을 갖지 않음
  • 화살표 함수 내부에서 super를 참조하면 상위스코프의 super를 참조
    //화살표함수 내부에서 super를 참조하면 constructor 내부의 super 바인딩 참조
    
    class Base {
      constructor(name) {
        this.name = name;
      }
    
      sayHi() {
        return `Hi! ${this.name}`;
      }
    }
    
    class Derived extends Base {
      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

화살표 함수는 자체의 arguments 바인딩을 갖지않음.
따라서 화살표 함수 내부에서 arguments를 참조하면 this와 마찬가지로 상위 스코프의 arguments를 참조함.

따라서 화살표 함수로 가변 인자 함수를 구현해야 할 때는 반드시 Rest 파라미터를 사용해야함.

(function () {
  const foo = () => console.log(arguments); // [Arguments] { '0': 1, '1': 2 }
  foo(3, 4);
})(1, 2);

4. Rest 파라미터

  • Rest 파라미터(나머지 매개변수)는 매개변수 이름 앞에 세개의 점 ...를 붙여서 정의한 매개변수를 의미Rest- 파라미터는 함수에 전달된 인수들의 목록을 배열로 전달받음
function foo(...rest) {
  console.log(rest); // [1, 2, 3, 4, 5]
}

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

좋은 웹페이지 즐겨찾기