Я не могу понять, как работает эта функция JavaScript
Я читал описание функции функции привязки, но я не могу на 100% понять код как написано:
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 || window,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
В частности, я не понимаю цель fNOP
, и я не понимаю, почему должен быть установлен прототип fBound
. Я также висел в части fToBind.apply
(я не могу понять, что это представляет в этом контексте).
Может ли кто-нибудь объяснить, что здесь происходит?
Ответы
Ответ 1
Ну, одна из причин, по которой должен быть установлен прототип fBound
, заключается в том, что результат вызова bind
на функцию имеет тот же прототип, что и эта функция. Здесь также появляется fNop
- он позволяет установить прототип fBound
с помощью new fNop()
без вызова исходной функции, которая может иметь побочные эффекты.
Вызов apply
позволяет вам установить this
в функцию и указать дополнительные аргументы. Поскольку bind
позволяет вам "выкармливать" аргументы функции, вы должны объединить оба аргумента, переданные при привязке функции, и аргументы, с которыми она вызвана.
Ответ 2
Необходимо убедиться, что
- (1) связанная функция может использоваться как конструктор, игнорируя привязку. (следовательно, проверка
instanceof
)
- (2) В то же время вы хотите убедиться, что
new g()
наследует цепочку прототипов f
. (отсюда часть .prototype = new fNop
)
Пример:
function f() {
this.foo = 'bar';
}
f.prototype = {
baz: 'yay!'
};
var g = f.bind({});
var o = new g();
console.log(o.foo); // 'bar' - (1)
console.log(o.baz); // 'yay!' - (2)
В тот момент, когда вы вызываете new g()
, функция fBound
вызывается как constuctor с новым объектным объектом (this
), который является экземпляром fNop
.
Изменить:
Стандарт ECMAScript5 определяет сложный алгоритм для функций привязки. Среди прочего, должны выполняться следующие утверждения:
var DateJan2042 = Date.bind(null, 2042, 0);
/*1*/ console.assert(Function.prototype.bind.length == 1, 'bind should have a length of 1');
/*2*/ console.assert(typeof DateJan2042 == 'function', 'bind() should return a function');
/*3*/ console.assert(!DateJan2042.hasOwnProperty('prototype'), 'Bound function must not have a prototype');
/*4*/ console.assert(DateJan2042.length == Math.max(Date.length - 2, 0), 'Bound function should have a proper length');
/*5*/ console.assert(typeof DateJan2042() == 'string', 'Function call should return a string');
/*6*/ console.assert({}.toString.call(new DateJan2042()).indexOf('Date') != -1, 'Constructor call should return a new Date object');
/*7*/ console.assert(new DateJan2042() instanceof DateJan2042, 'Instanceof check should pass for constructor\ return value');
/*8*/ console.assert((new DateJan2042()).getMonth() == 0, 'Constructor should be called with bound arguments');
/*9*/ console.assert((new DateJan2042(1)).getDate() == 1, 'Constructor should take additional arguments');
/*10*/ console.assert(!/^function *\( *[^ )]/.test(Function.prototype.toString.call(DateJan2042)), 'Bound function should have no formal arguments');
Так как правильно связанная функция не является вещественным объектом Function
, невозможно получить все возможное, используя polyfill (особенно цифры 2/3 и 4/10), но вы можете попытаться реализовать столько насколько это возможно.
Реализация, о которой идет речь, пытается решить число 6 и номер 7, подключаясь к цепочке прототипов, но что не достаточно.
Вот альтернативная реализация, которая работает немного лучше, но все еще не идеальна:
http://jsfiddle.net/YR6MJ/
Ответ 3
Из предыдущего комментария:
вместо использования fNop, почему вы не можете просто сказать fBound.prototype =
this.prototype
?
Насколько я могу судить, основное различие заключается в том, что когда значение this
внутри связанной функции является экземпляром исходной функции, на которую вызывается bind
, тогда значение для привязки к - первый аргумент, первоначально переданный в bind
-, игнорируется.
Например, этот код:
function Test(blah) {
console.log(this.length, blah);
}
Test.prototype.length = 77;
Test.prototype.fn = Test.bind(['a', 'b', 'c'], "testing");
new Test().fn()
... вызывает fn
для печати:
77 testing
Другими словами, значение this
внутри fn
- это экземпляр Test
, на который он был вызван. Ваше предложение будет снабжать связанный массив apply
внутри bind
, поэтому так написано, последняя строка того же кода будет печататься:
3 testing
Мне не совсем понятно, почему это важно, но он подчеркивает, что ваше предложение не даст эквивалентных результатов.