объяснение кода привязки функции prototype.js - PullRequest
1 голос
/ 21 апреля 2010

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

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 ]

7 голосов
/ 21 апреля 2010

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

Возможно, вы уже знаете это, но упоминать не повредит.Если мы не обертываем 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
0 голосов
/ 25 мая 2015

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

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 - это локальный объект, который автоматически присваивается при вызове функции.

...