📦

Method Shorthand Definitions Are Not Constructable!

Date
2021/10/30
Tags
JavaScript
Tutorial
Created by
JavaScript Object의 프로퍼티 값으로 함수가 들어갈 경우 이를 일반 함수와 구분하기 위해 메서드(Method)라 부른다. 이를 정의할 때 ES6에서 새로 추가된 'Method shorthand definition' 문법을 사용할 수 있는데, 완전히 똑같은 동작을 하는 Syntactic sugar일 뿐일까? 아니면 미묘한 차이가 있을까?
Table of Contents

Method Shorthand Definition

기존에는 Object에 메서드를 정의하기 위해서 다음과 같은 코드를 작성해야 했다.
const person = { nickname: 'Dogdriip', getNickname1: function() { return this.nickname; }, getNickname2: () => { return this.nickname; }, };
JavaScript
복사
단순히 프로퍼티의 value에 함수 정의문을 넣어주는 방법이다. 이렇게 하고 나면, person.getNickname1() 또는 person.getNickname2() 와 같은 방법으로 오브젝트 내의 메서드를 호출할 수 있었다.
눈치챘겠지만, getNickname1getNickname2의 동작은 다르다. 이는 Arrow function expression의 this 바인딩 차이에 의한 것으로, 이 글에서는 이에 대해 자세히 다루지 않는다. 조금만 설명하자면, getNickname1 메서드의 this는 해당 메서드가 속한 Object인 person을 가리키고, Arrow function expression을 사용해 선언한 getNickname2 메서드의 this는 해당 메서드가 속한 Object가 선언되었을 당시의 스코프의 this를 가리키기 때문이다.
ECMAScript 2015 (ES6)에서는 메서드를 정의하기 위한 새로운 문법이 추가되었다. 사용하는 방법은 다음과 같다.
const person = { nickname: 'Dogdriip', getNickname3() { return this.nickname; }, };
JavaScript
복사
이렇게 정의한 getNickname3 메서드는 위에서 정의한 getNickname1와 똑같이 작동하며, 얼핏 보면 두 메서드는 완전히 동일한 것이라고 착각할 수 있다. 하지만 둘 사이에는 미묘한 차이가 존재한다.

[[Construct]] 에 따라 달라지는 Construct 가능 여부

이미 알고 있을지도 모르겠지만, JS Object 내에는 숨겨진 Internal method들이 존재한다. 이들은 JavaScript 런타임 상황에 따라 달라지는, Runtime behaviour를 정의하는(또는 구현하는) 메서드들이다. 내부적으로 사용하는 것들로, 일반적으로는 이들을 직접 사용할 일은 없다.
그리고, Function object(함수도 객체다!)에 한해서 두 개의 추가적인 Internal method들이 존재하는데, 바로 [[Call]][[Construct]]라고 부르는 녀석들이다. [[Call]] Internal method가 존재하는 Object는 Function object가 되고, 그 중에서도 [[Construct]] Internal method가 존재하는 Function object는 Constructor가 된다.
이를 통해 간접적으로나마 모든 Function object에 [[Construct]]가 존재하는 것은 아니라는 사실을 알 수 있다. 그렇다면 어떤 Function들이 Constructor이고, 어떤 Function들이 그렇지 않은 걸까?

FunctionCreate를 보면 알 수 있다

Abstract operation 'FunctionCreate' 명세를 보면 단번에 알 수 있다.
FunctionCreate (kind, ParameterList, Body, Scope, Strict, prototype) ... 2. If kind is not Normal, let allocKind be "non-constructor". ...
FunctionCreate 연산의 인자로 받는 kind가 Normal이 아니면(kind는 Normal, Method, Arrow 셋 중 하나의 값이기 때문에, 즉 kind가 Method이거나 Arrow이면) allocKind는 non-constructor가 된다.
FunctionCreate가 이후 호출하는 연산을 설명하지는 않겠지만, 적어도 Normal kind의 Function만 Constructor가 될 수 있으며, 나머지 Method, Arrow kind의 함수는 Constructor가 될 수 없다는 것을 유추할 수 있다. 그리고 명세를 더 뒤져보면, Arrow function expression으로 선언한 함수는 Arrow, Method shorthand definition으로 정의한 메서드는 Method kind임을 알 수 있다.

결론

결국은 Constructor가 될 수 있느냐 없느냐의 차이이다. 사실 이 차이는 MDN에 적혀 있긴 하지만, 어떤 근거로, 어떤 과정을 통해 Method shorthand definition으로 정의한 메서드가 Constructor가 될 수 없는지에 대해서는 적혀 있지 않다.
그래서 다음 코드를 다시 보자.
const person = { nickname: 'Dogdriip', getNickname1: function() { return this.nickname; }, getNickname2: () => { return this.nickname; }, getNickname3() { return this.nickname; }, };
JavaScript
복사
getNickname1, getNickname2, getNickname3 각각에 대해 Constructor인지 아닌지 여부를 new 키워드로 체크해보면 다음과 같이 Arrow function expression과 Method shorthand definition으로 정의한 메서드는 Constructor가 될 수 없음을 알 수 있다.

References