2012-01-15 4 views
7

В удивительной библиотеке Underscore.js Jeremy Ashkenas я попытался понять одну вещь об исходном файле. Я не понимаю этого:Попытка понять источник underscore.js - вызов и применение, используемые в библиотеке

var slice = Array.prototype.slice; 
args = slice.call(arguments, 2); 

, так что:

args = Array.prototype.slice.call(arguments, 2); 

.call или .apply являются методы функций. Но вот, какие функции делают .call? Первым параметром должен быть контекст, но arguments - это контекст? Второй параметр должен быть параметрами, передаваемыми в функции. Вот их номер 2. Что это значит? Иногда в библиотеке используется 1 или 0. Являются ли они числом параметров, которые передаются в функции?

_.bind = function bind(func, context) { 
    var bound, args; 
    if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); 
    if (!_.isFunction(func)) throw new TypeError; 
    args = slice.call(arguments, 2); 
    return bound = function() { 
     if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); 
     ctor.prototype = func.prototype; 
     var self = new ctor; 
     var result = func.apply(self, args.concat(slice.call(arguments))); 
     if (Object(result) === result) return result; 
     return self; 
    }; 
    }; 

Вопрос 2: Я не совсем понимаю логику этой функции. Нужна помощь, чтобы понять. Пример должен быть очень полезным.

// Invoke a method (with arguments) on every item in a collection. 
    _.invoke = function(obj, method) { 
    var args = slice.call(arguments, 2); 
    return _.map(obj, function(value) { 
     return (method.call ? method || value : value[method]).apply(value, args); 
    }); 
    }; 

Благодарим за помощь.

ответ

7

Функция «slice» на прототипе Array ожидает, что this будет ссылаться на массив, на котором он должен работать. Другими словами, если у вас есть реальный массив:

var myArray = [1, 2, 3]; 

и вы называете slice():

var sliced = myArray.slice(1); 

Затем в этот призыв к slice(), this относится к массиву "MyArray". Как Raynos отмечает в комментарии:

myArray.slice(1) 

такая же, как

myArray.slice.call(myArray, 1); 

Таким образом, при использовании call() для вызова функции, и передать его arguments как объект контекста, то slice() код работает на arguments , Другие параметры, переданные через .call(), являются просто параметром или параметрами для самого slice(). В моем примере выше, обратите внимание, что я передал 1 функции.

Теперь, что касается вашего второго вопроса, функция .invoke() сначала изолирует аргументы, переданные после первых двух. Это означает, что когда вы используете _.invoke(), вы передаете ему два аргумента или более: первый - это список для работы, второй - метод, и (необязательные) последующие аргументы передаются методу для каждого элемента списка ,

Этот вызов _.map() является сложным (и на самом деле я думаю, что в нем есть немного глупостей). Он выполняет итерирование по списку, вызывая функцию для каждого значения в списке. Эта функция позволяет сначала определить, действительно ли параметр «метод» является функцией. Если это так, то она вызывает эту функцию через .apply() с элементом списка в качестве контекста. Если «метод» равен не функция, тогда она предполагает, что это имя свойства каждого элемента списка и что свойства являются функциями.

Так, например, с помощью простого списка это довольно просто:

var myList = [1, 2, 3]; 
var result = _.invoke(myList, function(n) { return this * n; }, 2); 

Это даст результат [2, 4, 6], потому что функция я прошел умножает объект контекста (this) параметром прошло, и я прошел 2 в обращении к _.invoke().

С более сложного списка, можно использовать второй аромат _.invoke() и вызвать метод по каждому объекту в списке:

var getName = function(prefix) { return prefix + " " + this.name; }; 
var list = [ 
    { name: "Bob", getName: getName }, 
    { name: "Sam", getName: getName }, 
    { name: "Lou", getName: getName } 
]; 

var result = _.invoke(list, "getName", "Congressman"); 

Это будет вызывать функцию «GetName» на каждом объекте в списке и вернуть список из результатов. Эффект будет в списке ["Congressman Bob", "Congressman Sam", "Congressman Lou"].

Теперь про эту ерунду. В коде для _.invoke():

return _.map(obj, function(value) { 
    return (method.call ? method || value : value[method]).apply(value, args); 
}); 

Это subexpresion method || value будет всегда возвращает значение «метода», или, по крайней мере, почти всегда за исключением какой-нибудь экзотический трюк. Если method.call правдиво, то ссылка на method также должна быть правдой. Кроме того, если бы это был мой код, я бы проверил methodза пределами обратный вызов _.map(), чтобы решение не требовалось повторять снова и снова. Может быть что-то вроде:

return _.map(obj, method.call ? 
    function(value) { method.apply(value, args); } : 
    function(value) { value[method].apply(value, args); } 
); 
+2

'myArray.slice (1) === slice.call (туАггау, 1)' – Raynos

+0

Да, это отличный, простой способ показать отношения. – Pointy

+2

@ Raynos, это на самом деле неверно. 'slice' создает два разных объекта массива, которые не идентичны. – katspaugh

 Смежные вопросы

  • Нет связанных вопросов^_^