Ответы:
Параметр context просто устанавливает значение this
в функции итератора.
var someOtherArray = ["name","patrick","d","w"];
_.each([1, 2, 3], function(num) {
// In here, "this" refers to the same Array as "someOtherArray"
alert( this[num] ); // num is the value from the array being iterated
// so this[num] gets the item at the "num" index of
// someOtherArray.
}, someOtherArray);
Рабочий пример: http://jsfiddle.net/a6Rx4/
Он использует число от каждого члена массива, для которого выполняется итерация, чтобы получить элемент с таким индексом someOtherArray
, который представлен, this
поскольку мы передали его в качестве параметра контекста.
Если вы не установите контекст, то this
будете ссылаться на window
объект.
context
где this
ссылка в вашей функции итератора. Например:
var person = {};
person.friends = {
name1: true,
name2: false,
name3: true,
name4: true
};
_.each(['name4', 'name2'], function(name){
// this refers to the friends property of the person object
alert(this[name]);
}, person.friends);
Контекст позволяет вам предоставлять аргументы во время вызова, что позволяет легко настраивать стандартные встроенные вспомогательные функции.
Некоторые примеры:
// stock footage:
function addTo(x){ "use strict"; return x + this; }
function pluck(x){ "use strict"; return x[this]; }
function lt(x){ "use strict"; return x < this; }
// production:
var r = [1,2,3,4,5,6,7,8,9];
var words = "a man a plan a canal panama".split(" ");
// filtering numbers:
_.filter(r, lt, 5); // elements less than 5
_.filter(r, lt, 3); // elements less than 3
// add 100 to the elements:
_.map(r, addTo, 100);
// encode eggy peggy:
_.map(words, addTo, "egg").join(" ");
// get length of words:
_.map(words, pluck, "length");
// find words starting with "e" or sooner:
_.filter(words, lt, "e");
// find all words with 3 or more chars:
_.filter(words, pluck, 2);
Даже из ограниченных примеров вы можете увидеть, насколько мощным может быть «дополнительный аргумент» для создания кода, который можно использовать повторно. Вместо того, чтобы делать разные функции обратного вызова для каждой ситуации, вы обычно можете адаптировать помощника низкого уровня. Цель состоит в том, чтобы ваша пользовательская логика связывала глагол и два существительных с минимальным образцом.
По общему признанию, функции стрелок устранили многие преимущества «кодового гольфа» общих чистых функций, но семантические и согласованные преимущества остаются.
Я всегда добавляю "use strict"
к помощникам для обеспечения нативной [].map()
совместимости при передаче примитивов. В противном случае они преобразуются в объекты, которые обычно все еще работают, но быстрее и безопаснее быть привязанными к типу.
_.each(['Hello', 'World!'], function(word){
console.log(word);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
Вот простой пример, который можно использовать _.each
:
function basket() {
this.items = [];
this.addItem = function(item) {
this.items.push(item);
};
this.show = function() {
console.log('items: ', this.items);
}
}
var x = new basket();
x.addItem('banana');
x.addItem('apple');
x.addItem('kiwi');
x.show();
Вывод:
items: [ 'banana', 'apple', 'kiwi' ]
Вместо addItem
нескольких вызовов вы можете использовать подчеркивание следующим образом:
_.each(['banana', 'apple', 'kiwi'], function(item) { x.addItem(item); });
что идентично addItem
трехкратному последовательному вызову с этими предметами. По сути, он повторяет ваш массив и для каждого элемента вызывает вашу анонимную функцию обратного вызова, которая вызывает x.addItem(item)
. Функция анонимного обратного вызова похожа на addItem
функцию-член (например, она принимает элемент) и является бессмысленной. Таким образом, вместо прохождения анонимной функции лучше _.each
избегать этого косвенного обращения и вызывать addItem
напрямую:
_.each(['banana', 'apple', 'kiwi'], x.addItem);
но это не сработает, так как внутри addItem
функции члена корзины не this
будет ссылаться на x
созданную вами корзину. Вот почему у вас есть возможность передать свою корзину x
для использования в качестве [context]
:
_.each(['banana', 'apple', 'kiwi'], x.addItem, x);
function basket() {
this.items = [];
this.addItem = function(item) {
this.items.push(item);
};
this.show = function() {
console.log('items: ', this.items);
}
}
var x = new basket();
_.each(['banana', 'apple', 'kiwi'], x.addItem, x);
x.show();
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
Короче говоря, если функция обратного вызова, которую вы передаете _.each
каким-либо образом, использует, this
то вам нужно указать, что this
должно ссылаться внутри вашей функции обратного вызова. Может показаться , что x
является излишним в моем примере, но x.addItem
это просто функция и может быть совершенно не связан с x
или basket
или любого другого объекта, например :
function basket() {
this.items = [];
this.show = function() {
console.log('items: ', this.items);
}
}
function addItem(item) {
this.items.push(item);
};
var x = new basket();
_.each(['banana', 'apple', 'kiwi'], addItem, x);
x.show();
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
Другими словами, вы привязываете некоторое значение к this
своему обратному вызову, или вы можете использовать привязку напрямую, например так:
_.each(['banana', 'apple', 'kiwi'], addItem.bind(x));
Как эта функция может быть полезна с некоторыми другими методами подчеркивания?
В общем, если какой-то underscorejs
метод принимает функцию обратного вызова и если вы хотите, чтобы этот обратный вызов вызывался для некоторой функции-члена некоторого объекта (например, функции, которая использует this
), то вы можете связать эту функцию с каким-либо объектом или передать этот объект в качестве [context]
параметра, и это основное намерение. И в верхней части документации underscorejs, это именно то, что они заявляют: итерируемый связывается с объектом контекста, если он был передан
Как объяснялось в других ответах, context
является this
контекст для использования внутри обратного вызова передается each
.
Я объясню это с помощью исходного кода соответствующих методов из подчеркивания исходного кода
Определение _.each
или _.forEach
следующее:
_.each = _.forEach = function(obj, iteratee, context) {
iteratee = optimizeCb(iteratee, context);
var i, length;
if (isArrayLike(obj)) {
for (i = 0, length = obj.length; i < length; i++) {
iteratee(obj[i], i, obj);
}
} else {
var keys = _.keys(obj);
for (i = 0, length = keys.length; i < length; i++) {
iteratee(obj[keys[i]], keys[i], obj);
}
}
return obj;
};
Второе утверждение важно отметить здесь
iteratee = optimizeCb(iteratee, context);
Здесь context
передается другой метод, optimizeCb
и затем присваивается возвращаемая ему функция, iteratee
которая вызывается позже.
var optimizeCb = function(func, context, argCount) {
if (context === void 0) return func;
switch (argCount == null ? 3 : argCount) {
case 1:
return function(value) {
return func.call(context, value);
};
case 2:
return function(value, other) {
return func.call(context, value, other);
};
case 3:
return function(value, index, collection) {
return func.call(context, value, index, collection);
};
case 4:
return function(accumulator, value, index, collection) {
return func.call(context, accumulator, value, index, collection);
};
}
return function() {
return func.apply(context, arguments);
};
};
Как видно из приведенного выше определения метода optimizeCb
, если context
не передано, то func
возвращается как есть. Если context
передано, функция обратного вызова вызывается как
func.call(context, other_parameters);
^^^^^^^
func
вызывается с помощью call()
которого используется для вызова метода путем установки его this
контекста. Таким образом, когда this
используется внутри func
, это будет относиться к context
.
// Without `context`
_.each([1], function() {
console.log(this instanceof Window);
});
// With `context` as `arr`
var arr = [1, 2, 3];
_.each([1], function() {
console.log(this);
}, arr);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
Вы можете считать context
последним необязательным параметром forEach
в JavaScript.
someOtherArray[num]
а неthis[num]
?