Function.prototype.bind()
=========================

Сводка
------

Метод **`bind()`** создаёт новую функцию, которая при вызове устанавливает в качестве контекста выполнения `this` предоставленное значение. В метод также передаётся набор аргументов, которые будут установлены перед переданными в привязанную функцию аргументами при её вызове.

Синтаксис
---------
```

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

### Параметры

`thisArg`

Значение, передаваемое в качестве `this` в целевую функцию при вызове привязанной функции. Значение игнорируется, если привязанная функция конструируется с помощью оператора `new`.

`arg1, arg2, ...`

Аргументы целевой функции, передаваемые перед аргументами привязанной функции при вызове целевой функции.

Описание
--------

Метод `bind()` создаёт новую "**привязанную функцию**" (**ПФ**). **ПФ** - это "необычный функциональный объект" ( термин из **ECMAScript 6** ), который является обёрткой над исходным функциональным объектом. Вызов **ПФ** приводит к исполнению кода обёрнутой функции.

**ПФ** имеет следующие внутренние ( скрытые ) свойства:

* **[[BoundTargetFunction]]** - оборачиваемый (целевой ) функциональный объект
* **[[BoundThis]]** - значение, которое всегда передаётся в качестве значения **this** при вызове обёрнутой функции.
* **[[BoundArguments]]** - список значений, элементы которого используются в качестве первого аргумента при вызове оборачиваемой функции.
* **[[Call]]** - внутренний метод. Выполняет код (функциональное выражение), связанный с функциональным объектом.

Когда **ПФ** вызывается, исполняется её внутренний метод **[[Call]]** со следующими аргументами **Call(*target*, *boundThis*, *args*).**

* ***target*** - **[[BoundTargetFunction]]**;
* ***boundThis*** - **[[BoundThis]]**;
* ***args*** - **[[BoundArguments]].**

Привязанная функция также может быть сконструирована с помощью оператора `new`: это работает так, как если бы вместо неё конструировалась целевая функция. Предоставляемое значение `this` в этом случае игнорируется, хотя ведущие аргументы всё ещё передаются в эмулируемую функцию.

Примеры
-------

### Пример: создание привязанной функции

Простейшим способом использования `bind()` является создание функции, которая, вне зависимости от способа её вызова, вызывается с определённым значением `this`. Обычным заблуждением для новичков в JavaScript является извлечение метода из объекта с целью его дальнейшего вызова в качестве функции и ожидание того, что он будет использовать оригинальный объект в качестве своего значения `this` (например, такое может случиться при использовании метода как колбэк-функции). Однако, без специальной обработки, оригинальный объект зачастую теряется. Создание привязанной функции из функции, использующей оригинальный объект, изящно решает эту проблему:


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

module.getX(); // 81

var getX = module.getX;
getX(); // 9, поскольку в этом случае this ссылается на глобальный объект

// создаём новую функцию с this, привязанным к module
var boundGetX = getX.bind(module);
boundGetX(); // 81
```

### Пример: частичные функции

Следующим простейшим способом использования `bind()` является создание функции с предопределёнными аргументами. Эти аргументы (если они есть) передаются после значения `this` и вставляются перед аргументами, передаваемыми в целевую функцию при вызове привязанной функции.


```js
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()` (en-US) контекст `this` устанавливается в объект `window` (или `global`). При работе с методами класса, требующими `this` для ссылки на экземпляры класса, вы можете явно привязать `this` к колбэк-функции для сохранения экземпляра.


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

// Объявляем цветение с задержкой в 1 секунду
LateBloomer.prototype.bloom = function () {
  window.setTimeout(this.declare.bind(this), 1000);
};

LateBloomer.prototype.declare = function () {
  console.log("Я прекрасный цветок с " + this.petalCount + " лепестками!");
};
```

### Пример: привязывание функций, используемых в качестве конструкторов

**Предупреждение:** этот раздел демонстрирует возможности JavaScript и документирует некоторые граничные случаи использования метода `bind()`. Показанные ниже методы не являются лучшей практикой и, вероятно, их не следует использовать в рабочем окружении.

Привязанные функции автоматически подходят для использования вместе с оператором `new` для конструирования новых экземпляров, создаваемых целевой функцией. Когда привязанная функция используется для конструирования значения, предоставляемое значение `this` игнорируется. Однако, предоставляемые аргументы всё так же вставляются перед аргументами конструктора:


```js
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'

var emptyObj = {};
var YAxisPoint = Point.bind(emptyObj, 0 /\*x\*/);
// не поддерживается полифилом, приведённым ниже,
// но отлично работает с родным bind:
var YAxisPoint = Point.bind(null, 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`.


```js
// Пример может быть запущен прямо в вашей консоли JavaScript
// ...продолжение примера выше

// Всё ещё можно вызывать как нормальную функцию
// (хотя обычно это не предполагается)
YAxisPoint(13);

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


Если вы хотите поддерживать использование привязанной функции только с помощью оператора `new`, либо только с помощью прямого вызова, целевая функция должна предусматривать такие ограничения.

### Пример: создание сокращений

Метод `bind()` также полезен в случаях, если вы хотите создать сокращение для функции, требующей определённое значение `this`.

Возьмём, например, метод `Array.prototype.slice`, который вы можете использовать для преобразования массивоподобного объекта в настоящий массив. Вы можете создать подобное сокращение:


```js
var slice = Array.prototype.slice;

// ...

slice.call(arguments);
```


С помощью метода `bind()`, это сокращение может быть упрощено. В следующем куске кода `slice` является функцией, привязанной к функции `call()` объекта `Function.prototype` (en-US), со значением `this`, установленным в функцию `slice()` объекта `Array.prototype`. Это означает, что дополнительный вызов `call()` может быть устранён:


```js
// Тоже самое, что и slice в предыдущем примере
var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.call.bind(unboundSlice);

// ...

slice(arguments);
```

Полифил
-------

Функция `bind` является дополнением к стандарту ECMA-262 5-го издания; поэтому она может присутствовать не во всех браузерах. Вы можете частично обойти это ограничение, вставив следующий код в начало ваших скриптов, он позволяет использовать большую часть возможностей `bind()` в реализациях, не имеющих его родной поддержки.


```js
if (!Function.prototype.bind) {
  Function.prototype.bind = function (oThis) {
    if (typeof this !== "function") {
      // ближайший аналог внутренней функции
      // IsCallable в ECMAScript 5
      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 && oThis ? this : oThis,
          aArgs.concat(Array.prototype.slice.call(arguments)),
        );
      };

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();

    return fBound;
  };
}
```


Некоторые из многих отличий (так же могут быть и другие, данный список далеко не исчерпывающий) между этой реализацией и реализацией по умолчанию:

* Частичная реализация предполагает, что методы `Array.prototype.slice()`, `Array.prototype.concat()`, `Function.prototype.call()` и `Function.prototype.apply()` являются встроенными, имеют своё первоначальное значение.
* Частичная реализация создаёт функции, не имеющие неизменяемых свойств «отравленной пилюли» — `caller` и `arguments` — которые выбрасывают исключение `TypeError` при попытке получить, установить или удалить эти свойства. (Такие свойства могут быть добавлены, если реализация поддерживает `Object.defineProperty`, либо частично реализованы [без поведения исключение-при-попытке-удаления], если реализация поддерживает расширения `Object.prototype.__defineGetter__()` и `Object.prototype.__defineSetter__()`.)
* Частичная реализация создаёт функции, имеющие свойство `prototype`. (Правильная привязанная функция его не имеет.)
* Частичная реализация создаёт привязанные функции, чьё свойство `length` не соответствует с определением в ECMA-262; оно равно 0, в то время, как полная реализация, в зависимости от значения свойства `length` целевой функции и количества предопределённых аргументов, может вернуть значение, отличное от нуля.

Если вы решили использовать частичную реализацию, **не рассчитывайте на корректную работу в тех случаях, когда реализация отклоняется от спецификации ECMA-262 5-го издания!** Однако, в определённых случаях (и, возможно, с дополнительными модификациями для отдельных нужд), применение данной частичной реализации может быть вполне оправданным до тех пор, пока `bind()` не станет широко реализован в соответствии со спецификацией.

Спецификации
------------

| Specification |
| --- |
| ECMAScript Language Specification # sec-function.prototype.bind |

Совместимость с браузерами
--------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

Смотрите также
--------------

* `Function.prototype.apply()`
* `Function.prototype.call()`
* Функции и их область видимости (en-US)