Odpowiedzi:
Parametr context ustawia po prostu wartość this
w funkcji iteratora.
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);
Przykład roboczy: http://jsfiddle.net/a6Rx4/
Używa liczby z każdego elementu składowego tablicy, która jest iterowana, aby uzyskać element o tym indeksie someOtherArray
, który jest reprezentowany przez, this
ponieważ przekazaliśmy go jako parametr kontekstu.
Jeśli nie ustawisz kontekstu, this
będzie odnosić się do window
obiektu.
context
gdzie this
odwołuje się do funkcji iteratora. Na przykład:
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);
Kontekst umożliwia dostarczanie argumentów w czasie wywołania, umożliwiając łatwe dostosowywanie ogólnych wstępnie zbudowanych funkcji pomocniczych.
kilka przykładów:
// 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);
Nawet na podstawie ograniczonych przykładów widać, jak potężny może być „dodatkowy argument” przy tworzeniu kodu wielokrotnego użytku. Zamiast tworzyć inną funkcję zwrotną dla każdej sytuacji, zwykle można dostosować pomocnika niskiego poziomu. Celem jest, aby Twoja niestandardowa logika obejmowała czasownik i dwa rzeczowniki przy minimalnym schemacie.
Wprawdzie funkcje strzałkowe wyeliminowały wiele zalet „kodu golfowego” ogólnych funkcji czystych, ale zalety semantyczne i spójności pozostają.
Zawsze dodaję "use strict"
do pomocników, aby zapewnić natywną [].map()
zgodność podczas przekazywania prymitywów. W przeciwnym razie są przekształcane w obiekty, co zwykle nadal działa, ale szybciej i bezpieczniej jest określić typ.
_.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>
Oto prosty przykład , którego można użyć _.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();
Wynik:
items: [ 'banana', 'apple', 'kiwi' ]
Zamiast dzwonić addItem
wiele razy, możesz użyć podkreślenia w ten sposób:
_.each(['banana', 'apple', 'kiwi'], function(item) { x.addItem(item); });
co jest identyczne z wywołaniem addItem
trzykrotnego sekwencyjnego połączenia z tymi elementami. Zasadniczo iteruje twoją tablicę i dla każdego elementu wywołuje twoją anonimową funkcję zwrotną, która wywołuje x.addItem(item)
. Anonimowa funkcja zwrotna jest podobna do addItem
funkcji składowej (np. Pobiera element) i jest trochę bezcelowa. Więc zamiast przechodzić przez funkcję anonimową, lepiej jest _.each
unikać tego pośrednictwa i wywoływać addItem
bezpośrednio:
_.each(['banana', 'apple', 'kiwi'], x.addItem);
ale to nie zadziała, ponieważ addItem
funkcja elementu członkowskiego wewnątrz koszyka this
nie będzie odnosić się do Twojego x
koszyka, który utworzyłeś. Dlatego masz możliwość przekazania swojego koszyka x
do wykorzystania jako [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>
Krótko mówiąc, jeśli funkcja zwrotna, do której _.each
w jakikolwiek sposób przekazujesz, używa this
, musisz określić, do czego this
powinno się odnosić wewnątrz funkcji zwrotnej. Może wydawać się x
to zbędne w moim przykładzie, ale x.addItem
jest po prostu funkcją i może być całkowicie niezwiązane x
lub basket
lub jakiegokolwiek innego obiektu, na przykład :
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>
Innymi słowy, możesz przypisać jakąś wartość do this
swojego wywołania zwrotnego lub równie dobrze możesz użyć bind bezpośrednio w ten sposób:
_.each(['banana', 'apple', 'kiwi'], addItem.bind(x));
jak ta funkcja może być przydatna w przypadku różnych metod podkreślania?
Ogólnie, jeśli jakaś underscorejs
metoda przyjmuje funkcję zwrotną i jeśli chcesz, aby ta funkcja zwrotna była wywoływana na jakiejś funkcji składowej jakiegoś obiektu (np. Funkcji, która używa this
), możesz powiązać tę funkcję z jakimś obiektem lub przekazać ten obiekt jako [context]
parametr i to jest pierwotny zamiar. A na początku dokumentacji podkreślenia, dokładnie to stwierdzają: iterat jest powiązany z obiektem kontekstu, jeśli taki jest przekazywany
Jak wyjaśniono w innych odpowiedziach, context
jest this
kontekst, który ma być używany w wywołaniu zwrotnym przekazywanym do each
.
Wyjaśnię to za pomocą kodu źródłowego odpowiednich metod z podkreślonego kodu źródłowego
Definicja _.each
lub _.forEach
jest następująca:
_.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;
};
Warto zwrócić uwagę na drugie stwierdzenie
iteratee = optimizeCb(iteratee, context);
Tutaj context
jest przekazywana do innej metody, optimizeCb
a zwracana przez nią funkcja jest następnie przypisywana do iteratee
której jest wywoływana później.
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);
};
};
Jak widać z powyższej definicji metody optimizeCb
, jeśli context
nie jest przekazywane, to func
jest zwracane bez zmian . Jeśli context
jest przekazywane, funkcja zwrotna jest wywoływana jako
func.call(context, other_parameters);
^^^^^^^
func
jest wywoływana, za pomocą call()
której jest używana do wywołania metody poprzez ustawienie jej this
kontekstu. Więc kiedy this
jest używany w środku func
, będzie odnosić się do 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>
Możesz rozważyć context
jako ostatni opcjonalny parametr forEach
w JavaScript.
someOtherArray[num]
zamiastthis[num]
?