Подтвердить что ты не робот

Я не могу понять, как работает эта функция 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 (я не могу понять, что это представляет в этом контексте).

Может ли кто-нибудь объяснить, что здесь происходит?

4b9b3361

Ответ 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

Мне не совсем понятно, почему это важно, но он подчеркивает, что ваше предложение не даст эквивалентных результатов.