Объяснение кода привязки функции prototype.js

От: http://ejohn.org/apps/learn/

Function.prototype.bind = function(){
  var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift();
  return function(){
    return fn.apply(object,
      args.concat(Array.prototype.slice.call(arguments)));
  };
};

Может кто-нибудь сказать мне, почему второй возврат необходим (до fn.apply)?

Также, кто-нибудь может объяснить, зачем нужен args.concat? Почему бы его не переписать так:

fn.apply(object, args)

вместо

return fn.apply(object,
          args.concat(Array.prototype.slice.call(arguments)));

2 ответа

Решение

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

Возможно, вы уже знаете это, но не больно упоминать. Если мы не завернем fn.apply внутри другой функции, то мы напрямую вызываем функцию fn который является неоптимальным, так как bind должен только установить контекст выполнения (что должно this обратитесь к внутренней функции), а не вызывайте ее.

Методы Javascript можно вызывать, вызывая call или же apply метод на них. Вот небольшой пример:

function example() {
    alert("useless example");
}

example.apply() // or example.call(), alerts "useless example";

Внешняя функция в bind() Prototype должна работать как невидимая оболочка вокруг связанной функции. Таким образом, любые аргументы, которые передаются в оболочку, должны также передаваться в связанную функцию, и она должна возвращать любое значение, которое возвращает связанная функция, поэтому здесь есть оператор return.

Причина для создания args.concat внутри fn.apply другая и не является обязательной. bind в Prototype позволяет добавлять аргументы к связанной функции.

args представляет аргументы, которые были переданы, когда мы позвонили bind на функции. arguments представляет аргументы, которые были переданы, когда мы вызвали связанную функцию. Мы в основном объединяем два массива там.

Из приведенного выше примера:

var obj = { x: 'prop x' };
var boundExample = example.bind(obj, 12, 23); // args => 12, 23
boundExample(36, 49); // arguments => 36, 49

// arguments that our example() function receives => 12, 23, 36, 49

Старый пост но новый подход;)

Function.prototype.bind = function(){
    var fn = this, 
    context = arguments[0], 
    args = Array.prototype.slice.call(arguments, 1);
    return function(){
        return fn.apply(context, args.concat([].slice.call(arguments)));
    }
}

obj = {'abc':'x'};
var func = function() {
  console.log(arguments);
  alert(this.abc);
}
var x = func.bind(obj);

console.log(x(1,2,3));

Это очень хороший пример для объяснения. Запустите его и проверьте журнал консоли. Затем измените код, чтобы пропустить

[].slice.call(arguments)

Вы увидите, что console.log выполнения x(1,2,3) больше не показывает аргументы. Это потому, что аргументы объектов являются локальной переменной во всех функциях. Это может показаться немного запутанным, но в основном это означает:

var x = func.bind(obj,1,2,3);

Возвращает эту функцию внутри:

function() {
    return fn.apply(obj, [1,2,3].concat([].slice.call(arguments)));
}

Так что это скорее шаблон для функции.

Когда вы сейчас запустите это как:

x(4,5,6)

Это будет запущено:

fn.apply(obj, [1,2,3].concat([].slice.call(arguments)))

со специальным аргументом object = {0:4,1:5,2:6}, который можно преобразовать в массив с помощью [].slice.call, где arguments является локальным объектом, который автоматически присваивается во время вызова функция.

Другие вопросы по тегам