ECMAScript | TypeScript

ECMAScript, Function.prototype의 bind 함수(this 지정, 커링)

partner_jun 2017. 8. 17. 16:06

Function에 구현되어 있는 bind 함수는 두 가지 용도로 사용할 수 있다.



1) 함수의 수신자 객체 지정 (이펙티브 자바스크립트 아이템 25)

this는 자바스크립트에서의 기묘한 개념 중 하나다. 흔히 접하는 언어들과 달리 자바스크립트의 Scope는 코드 블록( {}로 구분되는 )이 아닌 '객체' 단위다. 때문에 아래와 같은 이상한 현상이 발생한다.

let buffer = {
entries: [],
add: function(s) {
this.entries.push(s);
}
};

buffer.add(100); // 100 추가
console.log(buffer.entries); // [100]


(function () {
this.entries = [];
let x = buffer.add;
x(300); // ?????
})();
console.log(buffer.entries); // [100]

 두번째 익명 함수 영역에서 새로운 배열 entries를 선언하고 buffer의 add 함수를 x에 할당했다. 이 시점에서 buffer.add 함수의 Scope는 buffer가 아닌 익명 함수가 되기 때문에 x 함수를 실행해도 buffer의 entries에는 값이 추가되지 않는다.


그렇다면 함수를 파라미터로 받는 고차 함수는 어떨까?

var arr = [1, 2, 3];
arr.forEach(buffer.add);
console.log(buffer.entries); // [100]

고차 함수에서도 위의 익명 함수와 마찬가지로 전달된 함수는 새로운 Scope에서 실행된다. 그렇기 때문에 buffer 객체의 entires 프로퍼티에 값이 추가되지 않는다.


이 때 bind 함수를 이용해 명시적으로 수신자 객체(this)를 지정한 새로운 함수를 만들 수 있다.

var arr = [1, 2, 3];
buffer.entries = [];

let bindAdd = buffer.add.bind(buffer); // 이 함수의 `this`는 buffer 객체를 가리킨다.
arr.forEach(bindAdd);

console.log(buffer.entries); // [1, 2, 3]

이렇게 만든 함수는 this가 명시적으로 선언되었기 때문에 더 안전하고, 의도한 결과를 얻어낼 수 있다.




2) 함수의 커링 (이펙티브 자바스크립트 아이템 26)

함수형 언어에 필수적으로 구현되어 있는 커링이 자바스크립트에서는 조금 색다르게 지원된다. 위에서 수신자 객체(this)를 지정할 때 사용한 bind 함수를 이용하는 것이다.

bind 함수를 살펴보면 bind 함수는 수신자 객체뿐만 아니라 추가적인 파라미터 배열을 입력받는데, 입력받는 파라미터 배열은 함수의 파라미터로 전달된다.


코드로 보면 훨씬 이해하기 쉽다.

let addXYZ = function (x, y, z) {
return x + y + z;
};

let add100YZ = addXYZ.bind(null, 100);
let add100200Z = add100YZ.bind(null, 200);

console.log(addXYZ(1, 2, 3)); // 6
console.log(add100YZ(10, 10)); // 120
console.log(add100200Z(100)); // 400

여기서 재미있는 점은 bind의 첫 번째 파라미터, 그러니까 수신자 객체(this)는 null로 입력된다. null로 입력된 this는 전역 변수로 선언되는데, 아래와 같은 코드로 확인해 볼 수 있다.

let thisName = function (name) {
this.name = name;
};

let bindThisName = thisName.bind(null, 'Jack');

bindThisName();
console.log(this.name); // Jack




3. bind된 함수의 toString()

bind로 커링된 함수는 toString()을 호출해도 커링된(혹은 수신자 객체가 지정된) 함수가 출력되지 않는다.

let blahblah = function (str) {
console.log(str);
};

let bindBlah = blahblah.bind(null, 'hello world!');

console.log(bindBlah.toString()); // function () { [native code] }


이펙티브 자바스크립트에서 그 답을 찾을 수 있다.


"많은 호스트 환경에서, bind 함수는 또 다른 프로그래밍 언어(일반적으로 C++)로 구현되었기 때문에, 실행 환경에 보여주기 위한 자바스크립트 코드를 전혀 가지지 않는 컴파일된 함수를 생성한다."