0️⃣

(0, obj.fn)(args) 이거 왜 이렇게 쓰는 거야?

Date
2023/01/20
Tags
JavaScript
Created by
그냥 혼자 궁금했다. 특히 번들된 라이브러리 코드에서 많이 보이더라.

결론: Global this를 사용하기 위함이다

obj가 다음과 같은 객체라고 하자.
var obj = { fn: function() { return this; } }
JavaScript
복사
아래와 같이 obj.fn을 직접 호출하면 반환값은 뭐가 될까? fn을 객체의 메서드로써 호출했기 때문에 this는 해당 함수를 담고 있는 객체 obj에 바인딩된다.
obj.fn() // returns Object { fn: [Function: fn] } ❌
JavaScript
복사
함수 호출 컨텍스트에 상관없이 this가 Global 객체를 가리키도록 하려면 어떻게 해야 할까? 단순하게 다음과 같이 할 수 있다.
var temp = obj.fn temp() // returns Object [global] { ... } ✅
JavaScript
복사
당연히 브라우저에서는 window, Node에서는 global, Strict mode라면 undefined가 될 것이다.
또는, 더 단순하게 다음과 같이 할 수 있다.
(0, obj.fn)() // returns Object [global] { ... } ✅
JavaScript
복사
결국 Global this 객체를 사용하기 위해서임을 알 수 있다.

설명

이것은 Comma operator가 마지막 operand의 value를 반환하기에 가능한 일이다. Reference가 아닌, value를 반환하니까 그런 것이다.
The comma (,) operator evaluates each of its operands (from left to right) and returns the value of the last operand. (…)
그래서 다음과 같은 것들도 의도대로 된다.
(0, 1, 2, obj.fn)() // returns Object [global] { ... } ✅ ([], obj.fn)() // returns Object [global] { ... } ✅ (Math.random(), 'ahoy', obj.fn)() // returns Object [global] { ... } ✅ // ...
JavaScript
복사
맨 앞의 0은 그냥 placeholder였던 것이다.
Callee를 value로 만들 수 있는 방법은 comma operator 외에도 다양하다. 기상천외한 다른 방법들을 생각해볼 수 있다. 연산자 말고 함수를 이용해도 되고, 어찌 됐든 evaluate해서 value가 나오면 된다. (정확히는 operands에 GetValue를 수행하기만 하면 된다.)
(true ? obj.fn : 0)() // returns Object [global] { ... } ✅ (function(a) { return a })(obj.fn)() // returns Object [global] { ... } ✅ (b => b)(obj.fn)() // returns Object [global] { ... } ✅
JavaScript
복사
그러면 value를 반환하지 않고 reference를 반환하는 것은 어떤 것들이 있는가? 대표적으로 Grouping operator가 있다. ECMAScript 스펙 상으로 괄호 안에 reference가 있으면 reference를 그대로 반환한다. 그래서 다음과 같은 건 의도대로 안 된다.
(obj.fn)() // returns Object { fn: [Function: fn] } ❌ ((obj.fn))() // returns Object { fn: [Function: fn] } ❌ (((obj.fn)))() // returns Object { fn: [Function: fn] } ❌ // ...
JavaScript
복사

더 찾아볼 것: Babel에서

Babel은 위에서 설명한 기술(?)을 이용하여 왼쪽 코드를 오른쪽으로 트랜스파일한다. REPL
import { fn } from 'fns'; function x() { fn() }
JavaScript
복사
"use strict"; var _fns = require("fns"); function x() { (0, _fns.fn)(); }
JavaScript
복사
그런데 사실 굳이 (0, ...)처럼 작성하지 않고도 함수가 Global this를 쓰도록 하는 방법은 있다. .call이나 .apply도 있고… 굳이 이 메서드들을 안 쓰고 저런 문법으로 트랜스파일하는 이유는 아직 못 찾았다. 저게 더 짧아서 그런가?

References