MDN’s new design is in Beta! A sneak peek: https://blog.mozilla.org/opendesign/mdns-new-design-beta/

Function.prototype.bind()

Переклад не закінчено. Будь ласка, допоможіть перекласти цю статтю з англійської.

Метод bind() створює нову функцію, яка при виклику присвоює значенню this значення, яке було передано. А також передається послідовність аргументів, які будуть присвоєні перед аргументами, які були передані в зв'язану функцію перед її викликом.

Синтаксис

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

Параметри

thisArg
Значення, яке буде передаватися як параметр this до даної функції, коли зв'язана функція викликається. Це значення буде проігноровано, якщо зв'язана функція утворена викликом оператора new.
arg1, arg2, ...
Аргументи, які будуть передувати аргументам, що були передані в зв'язану функцію коли викликається дана функція.

Значення, яке повертається

Копія заданої функції з визначеним значенням this і початковими аргументами.

Опис

Функція bind() створює нову зв'язану функцію (bound function) ЗФ (BF). ЗФ - це екзотичний об'єкт функції (термін з ECMAScript 2015), що огортає початковий об'єкт функції. Взагалі результатом виклику ЗФ є результат виконання функції обгортки.
ЗФ має наступні внутрішні поля:

  • [[BoundTargetFunction]] - об'єкт загорнутої функції;
  • [[BoundThis]] - значення, яке завжди передається як значення this, коли викликається загорнута функція.
  • [[BoundArguments]] - список значень, елементи якого використовуються як перші аргументи будь-якого виклику загорнутої функції.
  • [[Call]] - виконує код, який пов'язаний з цим об'єктом. Виконується за допомогою виразу виклику функції. Аргументами внутрішнього методу є значення this і список, який містить аргументи, передані в дану функцію за допомогою виразу виклику.

Коли зв'язана функція викликається, вона викликає внутрішній метод [[Call]] з наступними аргументами Call(target, boundThis, args). Де, target - це [[BoundTargetFunction]], boundThis - це [[BoundThis]], args - це [[BoundArguments]].

Зв'язана функція може також бути створена за допомогою оператора new: це працює так, якби замість неї створювалася б дана функція. Передане значення this ігнорується, доки prepended arguments передані в emulated function.

Приклади

Створення зв'язаної функції

Найпростіше використання методу bind() - це зробити функцію, яка незалежно від того, як вона викликається, буде викликатися з конкретним значенням this. Загальною помилкою початківців JavaScript розробників є витягування методу з об'єкта, тоді надто пізно викликати цю функцію і очікувати, що вона буде використовувати початковий об'єкт в якості значення this (наприклад використовуючи цей метод в коді з колбеками). Без особливого догляду, однак початковий об'єкт зазвичай втрачається. Створення зв'язаної функції з функції, яка використовує початковий об'єкт, обережно вирішує цю проблему:

this.x = 9;    // тут значення this посилається на глобальний об'єкт "window" в браузері
var module = {
  x: 81,
  getX: function() { return this.x; }
};

module.getX(); // 81

var retrieveX = module.getX;
retrieveX();   
// returns 9 - Дана функція виконується в глобальній області видимості

// Створіть нову функцію, з зв'язаним значенням 'this' до об'єкта module
// Розробників початківців це може трохи збивати з толку
// глобальна змінна var x з параметром x об'єкта module
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(null, 37);

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

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

З setTimeout

По замовчуванню в window.setTimeout(), значення this буде встановлено window (або глобальний) об'єкт. Коли працювати з методами класу, що вимагають змінну this вказувати на екземпляр класу, ви можете відповідно зв'язувати this з функцією колбеком для того, щоб зберегти екземпляр.

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

// Declare bloom after a delay of 1 second
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();  
// after 1 second, triggers the 'declare' method

Зв'язувати функції, які використовуються в якості конструкторів

Warning: This section demonstrates JavaScript capabilities and documents some edge cases of the bind() method. The methods shown below are not the best way to do things and probably should not be used in any production environment.

Зв'язані функції автоматично підходять для використання з оператором 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 консолі
// ...продовжуючи зверху

// Може залишатися для виклику як звичайна функція 
// (хоча зазвичай значення this є небажаним)
YAxisPoint(13);

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

Якщо ви бажаєте підтримувати використання зв'язаної функції лише використовуючи оператор new, або лише викликати її, то дана функція мусить забезпечити дотримання цього обмеження.

Створення ярликів

bind() є також корисною у випадках де ви хочете створити ярлик до функції, котра вимагає особливе значення this.

Візьміть Array.prototype.slice, наприклад, котрий ви хочете використати для конвертації масиво-подібного об'єкту в справжній масив. Ви можете створити ярлик на подобі цього:

var slice = Array.prototype.slice;

// ...

slice.apply(arguments);

З bind(), це може бути спрощено. В наступній частині коду, slice є зв'язаною функцією з функцією apply() прототипу Function.prototype, з значенням this, яке встановлене в функції slice() прототипу Array.prototype. Це означає що додатковий виклик методу apply() може бути усунено:

// те ж, що й "slice" в попередньому прикладі
var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.apply.bind(unboundSlice);

// ...

slice(arguments);

Polyfill

You can partially work around this by inserting the following code at the beginning of your scripts, allowing use of much of the functionality of bind() in implementations that do not natively support it.

if (!Function.prototype.bind) {
  Function.prototype.bind = function(oThis) {
    if (typeof this !== 'function') {
      // closest thing possible to the ECMAScript 5
      // internal IsCallable function
      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 doesn't have a prototype property
      fNOP.prototype = this.prototype; 
    }
    fBound.prototype = new fNOP();

    return fBound;
  };
}

Some of the many differences (there may well be others, as this list does not seriously attempt to be exhaustive) between this algorithm and the specified algorithm are:

  • The partial implementation relies on Array.prototype.slice(), Array.prototype.concat(), Function.prototype.call() and Function.prototype.apply(), built-in methods to have their original values.
  • The partial implementation creates functions that do not have immutable "poison pill" caller and arguments properties that throw a TypeError upon get, set, or deletion. (This could be added if the implementation supports Object.defineProperty, or partially implemented [without throw-on-delete behavior] if the implementation supports the __defineGetter__ and __defineSetter__ extensions.)
  • The partial implementation creates functions that have a prototype property. (Proper bound functions have none.)
  • The partial implementation creates bound functions whose length property does not agree with that mandated by ECMA-262: it creates functions with length 0, while a full implementation, depending on the length of the target function and the number of pre-specified arguments, may return a non-zero length.

If you choose to use this partial implementation, you must not rely on those cases where behavior deviates from ECMA-262, 5th edition! With some care, however (and perhaps with additional modification to suit specific needs), this partial implementation may be a reasonable bridge to the time when bind() is widely implemented according to the specification.

Please check https://github.com/Raynos/function-bind for a more thorough solution!

Специфікації

Специфікація Статус Коментар
ECMAScript 5.1 (ECMA-262)
The definition of 'Function.prototype.bind' in that specification.
Standard Початкове визначення. Реалізовано в 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  

Сумісність із браузерами

Особливість Chrome Firefox (Gecko) Internet Explorer Opera Safari
Базова підтримка 7 4.0 (2) 9 11.60 5.1
Особливість Android Chrome for Android Firefox Mobile (Gecko) IE Mobile Opera Mobile Safari Mobile
Базова підтримка 4.0 1 4.0 (2) ? 11.5 6.0

Дивитися також

Мітки документа й учасники

 Зробили внесок у цю сторінку: piton13
 Востаннє оновлена: piton13,