JavaScript

ES6 이후 함수들

dev_roach 2021. 9. 25. 17:15
728x90

ES6 함수의 추가 기능

ES6 이전의 함수들은 함수 사용 목적에 따라 명확히 구분되지 않았음.

일반 함수로도 호출이 가능했고, 생성자 함수로도 호출이 가능했음.

이게 문제가 됬던이유는 [[Call]][[Construct]] 를 공부했을때 배운것은 인스턴스를 생성할 수 있는 함수 객체는 constructor, 인스턴스를 생성 할수 없는 함수 객체는 non-constructor 인데 ES6 이전의 함수들은 아래와 같은 구분이 되지 않는 특성때문에 아래와 같은 로직이 가능하다.

var person = {
    age: 99,
    myAge: function() { return this.age }
}

var bar = person.myAge;

console.log(bar()); // undefined

위와 같은 형태가 가능하다. 물론 undefined 이지만, 위와 같은 형태가 문법적으로 가능하다는게 말이 안된다. 이는 성능상의 문제도 있는데 즉 myAge() 자체가 constructor 처럼 쓰일 수 있다는 것은 property 를 가질 수 있다는 것이고, 프로토 타입 객체도 만들어낸다는 것을 의미한다.

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

ES6 Function constructor prototype super arguments
Normal Function y y x y
Method x x y y
Arrow x. x x x

Method

아까의 자바스크립트 코드를 다시한번 보자.

var person = {
    age: 99,
    myAge: function() { return this.age }
}

console.log(person.myAge()); // 99
console.log(new person.myAge()); 

위에서 말했듯이 객체안의 function 이니 myAge() 는 메소드 일까?
Method 라면 new 생성자를 써주었을때 Error 를 뿜어야 할것이다.
하지만 아래와 같이 하면 console.log(new person.myAge()) 실행되지 않고, TypeError 를 내뿜는다고 생각하면, 아니다 아래와 같이 잘 나온다.

위의 로직이 작동하는 이유는 뭘까? JavaScript 의 Method 는 오직 축약표현으로 서술한 것을 Method 라고 정의했기 때문이다. 따라서 우리가 저 코드가 TypeError 를 뿜게 하기 위해서는 아래와 같이 코드를 바꿔야 한다.

var person = {
    age: 99,
    myAge() { return this.age }
}

console.log(person.myAge()); // 99
console.log(new person.myAge()); 

이렇게 바꾸면 아래와 같은 결과가 나온다.

또한 위에서 봤듯이 메소드는 property 를 가지고 있지도 않을 것이다.

console.log(person.myAge.hasOwnProperty('property')); // false

위와 같이 입력하면 false 가 나온다. 이는 당연한 결과이다. method 는 객체 내의 interface 처럼 이용되어야 하는데, method 가 property 를 가지고, 프로토타입 객체마저 만들어 낸다면, 이는 객체를 훼손시킬 수 있거나, 모순에 해당한다고 생각한다.

다만 기존의 객체지향 언어들처럼 super 를 관리하기 위해 [[HomeObject]] 를 갖는다.

const rocket = {
  boom() {
    return 'Hello Boom!';
  }
}

const rocketCenter = {
  __proto__: rocket,

  sayBoom() {
    return super.boom();
  }
}

console.log(rocketCenter.boom()); // 'Hello Boom!'

위에서 말했듯이 메소드를 축약형으로 쓰지않으면 이건 불가능하다. 축약형이 아닌 함수는 메소드가 아니므로 [[HomeObejct]] 를 가지지 않기 때문이다.

Arrow Function

화살표 함수는 JavaScript 를 하면 많이 사용하게 되는 함수이다.
아래와 같이 사용 가능하다.

const add = (x,y) => x + 2;

화살표 함수 또한 일급 객체로 다뤄지기에 아래와 같은 방법으로 서술이 가능하다.

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

[1,2,3].map(v => v * 2);

화살표 함수는 this 의 독특한 방식때문에 많이 쓰이는데, 그 이유는 화살표 함수의 this 는 가장 가까운 상위의 this 를 참조하기 때문이다. 예를 들면 아래와 같은 코드를 한번 보자.

class Prefixer {

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

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

}

const prefixer = new Prefixer('-webkit-');

console.log(prefixer.add(['transition', 'user-select']));

라고 썼을때 우리는 [-webkit-transition, -webkit-user-select] 를 기대하지만 결과는 그렇게 나오지 않는다. 왜냐하면 map 함수안의 this 는 map 콜백 함수를 일반 함수로 호출하기 때문에 undefined 이다.

그래서 이런 경우 const that = this; 와 같은 문법을 통해서 극복해야만 했다.
예를 들면 아래와 같이 말이다.

class Prefixer {

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

    add(arr) {
        const that = this;
        return arr.map(function (item) {
            return that.prefix + item;
        })
    }

}

이런 문제가 콜백 함수 내부의 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']));

이런식으로 적어주면 잘 동작되고 우리가 예상한 결과값이 도출된다.
위와 같이 화살표 함수의 this 는 상위 스코프의 this 를 그대로 참조한다. 이를 lexical this 라고 부른다.

728x90

'JavaScript' 카테고리의 다른 글

[JavaScript 공부용] Webworker 다뤄보기  (1) 2024.11.20
Hugo 로 Github 페이지 블로그 만들기  (0) 2023.08.16