2016-01-15 6 views
0

Все началось с этой QuestionПонимание Function.call.bind - шаг за шагом

Тогда ответ от @MinusFour

var slice = Function.call.bind(Array.prototype.slice); 

Я хотел понять, Что происходит под капотом, мой любопытство, следовательно, этот вопрос.

Что достичь? понимание «Function.call.bind».

Шаг за шагом подход к той же

работы с MDN

ПРИМЕЧАНИЕ: Я использую NodeJS здесь

1)

var adder = new Function('a', 'b', 'return a + b'); 
console.log(adder(2, 6)); 

** ВЫХОД **

8 

Ожидается, Nothing Fancy

2)

Это наша конечная цель, вызывая функцию myFunc из ограниченной функции (Function.call.bind(myFunc))

function myFunc(a, b) { 
    console.log(arguments.length, a, b, a + b); 
} 

3)

var adder = Function(myFunc); 
console.log(adder.toString()) 

ВЫВОД

function anonymous() { function myFunc(a, b) { console.log(a + b); } } 

Ожидаемое! выше код ничего не делает, потому что я вызываю «анонимный», и ничего не делает.

4)

var adder = Function.call(myFunc); 
console.log(adder.toString()) 

ВЫВОД

function anonymous() { 

} 

Ожидаемое !. '.call' звонки 'Function', с 'this' установлен в 'myFunc' и без какого-либо элемента или тела функции. поэтому пустой анонимной функцией является вывод.Теперь я могу сделать "var adder = Function.call(myFunc,myFunc);" создать ту же самую функцию, начиная с этапа-3

До сих пор так хорошо

5)

var adder = Function.call.bind(myFunc); 
console.log(adder.toString()) 
adder(2,6); 

ВЫВОД

function() { [native code] } 
1 6 undefined NaN 

Здесь первый Параметр не передается функции 'myFunc'. принято 'this' для функции 'adder' (ограниченный Function.call)?

Теперь я понимаю (или я неправильно понял?) До сих пор, но затем Как работает код ниже?

var slice = Function.call.bind(Array.prototype.slice); 
function fn(){ 
    var arr = slice(arguments); 
} 

в моем случае первый парам сумматор отбрасывается (или Function.call рассматривать его как 'this' для него), то же самое должно произойти с slice справа вверху?

Во всяком случае, я хотел, чтобы документировать его для справки

+1

Это абсолютно нормально (рекомендуется, даже), чтобы ответить на свой вопрос, но он должен быть как на * Jeopardy! * Игровое шоу: отправьте свой вопрос как * вопрос *, и если у вас есть ответ, отправьте это как * ответ *. Сейчас трудно сказать, в чем ваш вопрос, и трудно сказать, чувствуете ли вы, что у вас уже есть ответ. –

ответ

1

Я боюсь, ты ушел в слегка неправильном направлении. Эта линия:

var slice = Function.call.bind(Array.prototype.slice); 

никогда не называет Function и никогда не организует его можно назвать позже. Единственное, что Function используется для его call собственности. Function мог бы быть Object или Date или RegExp или любой другой функцией, или мог быть Function.prototype; не имеет значения. Function.prototype было бы более прямым и, возможно, менее запутанным.

Это немного сложно объяснить, потому что она включает в себя два слоя борьбы с this, где this разные вещи в разное время:

call функция вызывает функции с определенным this значение, которое вы даете ему, как его первый аргумент, передавая любые другие аргументы, которые вы ему даете. Например:

function foo(arg) { 
    console.log("this.name = " + this.name + ", arg = " + arg); 
} 
var obj = {name: "bar"}; 
foo.call(obj, "glarb"); // "this.name = bar, arg = glarb" 

Там, потому что мы назвали call на foo, call называется foo с this набором для obj и проходящей вдоль "glarb" argment.

call знает, какую функцию он должен вызывать, исходя из того, что this во время вызова call. foo.callthis в течение call до foo.Это может привести к путанице, так что давайте диаграмме:

  • foo.call(obj, "glarb") звонки call:
    • call видит this = foo и аргументы obj и "glarb"
    • call вызовы this (который foo):
      • foothis = obj и один аргумент "glarb"

Что касается slice, вы обычно видите call используется вместе с ним используется для создания массива из чего-то array- как, что на самом деле не является массивом:

var divArray = Array.prototype.slice.call(document.querySelectorAll("div")); 

или

var divArray = [].slice.call(document.querySelectorAll("div")); 

Здесь мы указываем call с this, установленным в Array.prototype.slice (или [].slice, что является той же функцией) и передает в коллекцию, возвращаемую querySelectorAll в качестве первого аргумента. call вызывает функцию, которую он видит как this, используя свой первый аргумент как this для этого вызова и проходящий через любой другой.

Итак, это первый слой this.

bind другая функция, что функции есть, и это похоже на call но разные: Где callвызовов целевой функция с заданной this и аргументами, bind создает и возвращает новой функции, которая будет делать, если вы назови это. Возвращаясь к нашему примеру foo:

function foo(arg) { 
    console.log("this.name = " + this.name + ", arg = " + arg); 
} 
var obj = {name: "bar"}; 
var fooWithObjAndGlarb = foo.bind(obj, "glarb"); 
fooWithObjAndGlarb(); // "this.name = bar, arg = glarb" 

Это называется связывания вещи (obj и "glarb" аргумент) в foo.

В отличие от call, поскольку bind создает новую функцию, мы можем добавить аргументы позже:

Хорошо, теперь у нас есть все наши рабочие части. Так что происходит в вашем коде? Давайте разбить его на части:

// Get a reference to the `call` function from the `call` property 
// on `Function`. The reason `Function` has a `call` property is that 
// `Function` is, itself, a function, which means its prototype is 
// `Function.prototype`, which has `call` on it. 
var call = Function.call; 

// Get a reference to the `slice` function from `Array.prototype`'s `slice` property: 
var rawSlice = Array.prototype.slice; 

// Create a *bound* copy of `call` that, when called, will call 
// `call` with `this` set to `rawSlice` 
var callBoundToSlice = call.bind(rawSlice); 

(. callBoundToSlice просто называется slice в вашем вопросе, но я использую callBoundToSlice, чтобы избежать путаницы) Переплет rawSlice к call был первый слой this обработки, определения того, что call будет см. как this.Вызов callBoundToSlice позвонит call с this, установленным в rawSlice. Затем call вызовет функцию, которую он видит, как (rawSlice), используя свой первый аргумент как значение для this во время вызова (второй уровень обработки this) и передает любые дальнейшие аргументы.

Так наш forEach с коллекцией из querySelectorAll теперь может выглядеть следующим образом:

callBoundToSlice(document.querySelectorAll("div")).forEach(function(div) { 
    // Each div here 
}); 

Это проходит collecton возвращенное querySelectorAll в callBoundToSlice, который вызывает call с this в rawSlice, который вызывает Array.prototype.slice с this набором для Коллекция. Array.prototype.slice использует this для копирования массива.


Все, что сказал, используя slice превратить массив подобных объектов в истинные массивы немного устарели. ES2015 представляет Array.from метод, который может быть shimmed/polyfilled на двигателях JavaScript, которые не имеют его еще:

var divArray = Array.from(document.querySelectorAll("div")); 
+1

теперь этот последний Редактировать очищает мой запрос. небольшая коррекция в вашем ответе - вместо того, «который вызывает вызов с этим в качестве коллекции« он должен быть », который вызывает вызов с этим как Array.prototype.slice». Большое спасибо – Oxi

+1

@ Oxi: Хорошая добыча! Исправлена. Отлично, что вы получили объяснение настолько, что вы смогли найти ошибку в нем! :-) –