Відповіді:
Параметр контексту просто встановлює значення 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
зворотного дзвінка, або ви також можете використовувати bind безпосередньо так:
_.each(['banana', 'apple', 'kiwi'], addItem.bind(x));
наскільки ця функція може бути корисною для різних методів підкреслення?
Взагалі, якщо якийсь underscorejs
метод виконує функцію зворотного виклику, і якщо ви хочете, щоб цей зворотний виклик був викликаний функцією-членом якогось об'єкта (наприклад, функцією, яка використовує this
), ви можете прив'язати цю функцію до якогось об'єкта або передати цей об'єкт як [context]
параметр, і це первинний намір. І вгорі документації підкреслюють саме те, що вони заявляють: ітератор прив’язаний до контекстного об'єкта, якщо його передано
Як пояснено в інших відповідях, чи 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]
?