Function.prototype.bind()

bind() 메소드는 호출될 때 그 this 키워드를 제공된 값으로 설정하고 새로운 함수가 호출될 때 제공되는 주어진 순서의 선행 인수가 있는 새로운 함수를 생성합니다.

구문

fun.bind(thisArg[, arg1[, arg2[, ...]]])

매개변수

thisArg
바인딩된(bound) 함수가 호출될 때 대상(target) 함수에 this 매개변수로서 전달되는 값. 그 값은 바인딩된 함수가 new 연산자를 사용하여 생성된 경우 무시됩니다.
arg1, arg2, ...
대상 함수를 호출할 때 바인딩된 함수에 제공되는 인수 앞에 붙이는(prepend) 인수.

반환값

지정된 this 값 및 초기 인수가 있는 주어진 함수의 복제본.

설명

bind() 함수는 새로운 바인딩된 함수(BF)를 만듭니다. BF는 원 함수 객체를 감싸는 특이(exotic, ordinary가 아닌) 함수 객체(용어 출처 ECMAScript 6)입니다. BF 호출은 보통 그 감싸인 함수의 실행으로 끝납니다.
BF는 다음 내부 속성이 있습니다:

  • [[BoundTargetFunction]] - 감싸인 함수 객체;
  • [[BoundThis]] - 감싸인 함수를 호출할 때 this 값으로 항상 전달되는 값.
  • [[BoundArguments]] - 어떤 감싸인 함수 호출에든 첫 번째 인수로 사용되는 요소를 갖는 값 목록.
  • [[Call]] - 이 객체와 관련된 코드 실행. 함수 호출 식을 통해 호출됨. 내부 메소드의 인수는 this 값 및 호출 식으로 함수에 전달되는 인수를 포함하는 목록입니다.

바인딩된 함수가 호출될 때, 다음 인수 Call(target, boundThis, args)가 있는 내부 메소드 [[Call]]을 호출합니다. 자리 target[[BoundTargetFunction]], boundThis[[BoundThis]], args[[BoundArguments]]입니다.

바인딩된 함수는 new 연산자를 사용하여 생성될 수도 있습니다: 그렇게 하면 대상 함수가 마치 대신 생성된 것처럼 행동합니다. 제공된 this 값은 무시됩니다, 앞에 붙인(prepend) 인수는 에뮬레이트된 함수에 제공되지만.

바인딩된 함수 생성

bind()의 가장 간단한 사용법은 어떻게 호출되든지 특정 this 값으로 호출되는 함수를 만드는 겁니다. 초보 JavaScript 프로그래머로서 흔한 실수는 객체로부터 메소드를 추출한 뒤 나중에 그 함수를 호출하여 원 객체를 그 함수의 this로서 사용하기를 기대하는 겁니다(가령 콜백 기반 코드에서 그 메소드를 사용하여). 그러나 특별히 주의를 하지 않으면, 원 객체는 보통 손실됩니다. 그 함수로부터 원 객체를 사용하여 바인딩된 함수를 생성하면, 깔끔하게 이 문제를 해결합니다:

this.x = 9;
var module = {
  x: 81,
  getX: function() { return this.x; }
};

module.getX(); // 81

var retrieveX = module.getX;
retrieveX();
// 9 반환 - 함수가 전역 스코프에서 호출됐음

// module과 바인딩된 'this'가 있는 새로운 함수 생성
// 신입 프로그래머는 전역 변수 x와
// module의 속성 x를 혼동할 수 있음
var boundGetX = retrieveX.bind(module);
boundGetX(); // 81

부분 적용 함수

bind()의 다음으로 간단한 사용법은 미리 지정된 초기 인수가 있는 함수를 만드는 겁니다. 이러한 인수는 (있다면) 제공된 this 값 뒤를 따르고 대상 함수에 전달된 인수의 시작부에 삽입됩니다, 바인딩된 함수에 전달되는 인수가 뒤따르는, 바인딩된 함수가 호출될 때마다.

function list() {
  return Array.prototype.slice.call(arguments);
}

var list1 = list(1, 2, 3); // [1, 2, 3]

// 미리 설정된 선행 인수가 있는 함수 생성
var leadingThirtysevenList = list.bind(undefined, 37);

var list2 = leadingThirtysevenList();
// [37]

var list3 = leadingThirtysevenList(1, 2, 3);
// [37, 1, 2, 3]

setTimeout과 함께 사용

window.setTimeout() 내에서 기본으로, this 키워드는 window (또는 global) 객체로 설정됩니다. 클래스 인스턴스를 참조하는 this를 필요로 하는 클래스 메소드로 작업하는 경우, 명시해서 this를 콜백 함수에 바인딩할 수 있습니다, 인스턴스를 유지하기 위해.

function LateBloomer() {
  this.petalCount = Math.ceil(Math.random() * 12) + 1;
}

// 1초 지체 후 bloom 선언
LateBloomer.prototype.bloom = function() {
  window.setTimeout(this.declare.bind(this), 1000);
};

LateBloomer.prototype.declare = function() {
  console.log('I am a beautiful flower with ' +
    this.petalCount + ' petals!');
};

var flower = new LateBloomer();
flower.bloom();
// 1초 뒤, 'declare' 메소드 유발

생성자로 쓰이는 바인딩된 함수

경고: 이 부분은 JavaScript 능력을 보이고 bind() 메소드의 일부 극단 상황(edge case)을 기록합니다. 아래 보이는 메소드는 일을 하는 가장 좋은 방법은 아니며 아마도 상용 환경에서 전혀 사용되지 않을 겁니다.

바인딩된 함수는 자동으로 대상 함수에 의해 생성되는 새로운 인스턴스를 생성하는 new 연산자와 함께 쓰기에 적합합니다. 바인딩된 함수가 값을 생성하는 데 쓰이는 경우, 제공된 this는 무시됩니다. 그러나, 제공된 인수는 여전히 생성자 호출에 (인수부) 앞에 붙습니다:

function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function() {
  return this.x + ',' + this.y;
};

var p = new Point(1, 2);
p.toString(); // '1,2'

// 아래 폴리필에서는 지원되지 않음,

// 원 bind와는 잘 작동:

var YAxisPoint = Point.bind(null, 0/*x*/);


var emptyObj = {};
var YAxisPoint = Point.bind(emptyObj, 0/*x*/);

var axisPoint = new YAxisPoint(5);
axisPoint.toString(); // '0,5'

axisPoint instanceof Point; // true
axisPoint instanceof YAxisPoint; // true
new Point(17, 42) instanceof YAxisPoint; // true

new와 함께 쓰기 위한 바인딩된 함수를 만들기 위해 특별한 일을 할 필요가 없음을 주의하세요. 그 결과 분명히 호출되는 바인딩된 함수를 만들기 위해 특별히 아무것도 할 필요가 없습니다, 오히려 new를 사용해서만 호출되는 바인딩된 함수를 요구하는 경우에도.

// 예는 JavaScript 콘솔에서 직접 실행될 수 있음
// ...위에서부터 이어짐

// 여전히 일반 함수로서 호출될 수 있음
// (보통 이를 원하지 않더라도)
YAxisPoint(13);

emptyObj.x + ',' + emptyObj.y;
// >  '0,13'

오로지 new를 사용하거나 호출해서만 바인딩된 함수의 사용을 지원하고 싶은 경우, 대상 함수는 그 제한을 강제해야 합니다.

바로 가기 생성

bind()는 특정 this 값을 필요로 하는 함수의 바로 가기(shortcut)를 만들고 싶은 경우에도 도움이 됩니다.

가령, 배열 같은 객체를 실제 배열로 변환하는 데 사용하고 싶은 Array.prototype.slice를 취하세요. 이와 같은 바로 가기를 만들 수 있습니다:

var slice = Array.prototype.slice;

// ...

slice.apply(arguments);

bind()로, 이는 단순화될 수 있습니다. 다음 조각 코드에서, sliceFunction.prototypeapply() 함수에 바인딩된 함수입니다, this 값을 Array.prototypeslice() 함수로 설정한 채. 이는 추가 apply() 호출은 삭제될 수 있음을 뜻합니다:

// 이전 예에서 "slice"와 같음
var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.apply.bind(unboundSlice);

// ...

slice(arguments);

폴리필

bind 함수는 ECMA-262 제5판에 추가되었습니다; 그러하기에 모든 브라우저에 없을 수 있습니다. 스크립트 시작 부분에 다음 코드를 삽입하여 이를 우회할 수 있습니다, 원래 이를 지원하지 않는 구현에서 bind() 기능 대부분을 사용할 수 있는.

if (!Function.prototype.bind) {
  Function.prototype.bind = function(oThis) {
    if (typeof this !== 'function') {
      // ECMAScript 5 내부 IsCallable 함수와
      // 가능한 가장 가까운 것
      throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }

    var aArgs   = Array.prototype.slice.call(arguments, 1),
        fToBind = this,
        fNOP    = function() {},
        fBound  = function() {
          return fToBind.apply(this instanceof fNOP
                 ? this
                 : oThis,
                 aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    if (this.prototype) {
      // Function.prototype은 prototype 속성이 없음
      fNOP.prototype = this.prototype;
    }
    fBound.prototype = new fNOP();

    return fBound;
  };
}

이 알고리즘과 스펙화된 알고리즘 간 많은 차이(충분히 다른 차이가 있을 수 있습니다, 이 목록은 정말 철저히 하지 않았기에) 중 일부는 다음입니다:

  • 부분 구현은 Array.prototype.slice(), Array.prototype.concat(), Function.prototype.call()Function.prototype.apply(), 원래 값을 갖는 내장 메소드에 의존합니다.
  • 부분 구현은 불변(immutable) "poison pill" caller 및 get, set 또는 삭제 시 TypeError가 발생하는 arguments 속성이 없는 함수를 만듭니다. (이는 구현이 Object.defineProperty를 지원하는 경우 추가 또는 구현이 __defineGetter____defineSetter__ 메소드를 지원하는 경우 [삭제 시 오류 발생(throw-on-delete) 동작(behavior) 없이] 부분 구현될 수 있습니다.)
  • 부분 구현은 prototype 속성이 있는 함수를 만듭니다. (고유 바인딩된 함수는 없습니다.)
  • 부분 구현은 length 속성이 ECMA-262에 의해 부여된(mandated) 그것과 일치하지 않는 바인딩된 함수를 만듭니다: 길이 0인 함수를 만듭니다, 반면에 전체 구현은 대상 함수의 길이 및 미리 지정된 인수의 수에 따라 0이 아닌 길이를 반환할 수 있습니다.

이 부분 구현을 쓰기로 고른 경우, 동작이 ECMA-262 제5판을 벗어난 경우에 의존하지 않아야 합니다! 그러나 주의 약간(과 아마도 특정 요구에 맞추기 위한 추가 수정)으로, 이 부분 구현은 bind()가 스펙에 따라 널리 구현될 때까지 적당한 다리가 될 수 있습니다.

스펙

스펙 상태 설명
ECMAScript 5.1 (ECMA-262)
The definition of 'Function.prototype.bind' in that specification.
Standard 초기 정의. JavaScript 1.8.5에서 구현됨.
ECMAScript 2015 (6th Edition, ECMA-262)
The definition of 'Function.prototype.bind' in that specification.
Standard  
ECMAScript 2017 Draft (ECMA-262)
The definition of 'Function.prototype.bind' in that specification.
Draft  

브라우저 호환성

Feature Chrome Firefox (Gecko) Internet Explorer Opera Safari
Basic support 7 4.0 (2) 9 11.60 5.1
Feature Android Chrome for Android Firefox Mobile (Gecko) IE Mobile Opera Mobile Safari Mobile
Basic support 4.0 1 4.0 (2) ? 11.5 6.0

참조

문서 태그 및 공헌자

 이 페이지의 공헌자: dale0713, joeunha, Netaras, sftblw, zuckay79, jjhangu
 최종 변경: dale0713,