Risposte:
Il parametro di contesto imposta semplicemente il valore di this
nella funzione iteratore.
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);
Esempio di lavoro: http://jsfiddle.net/a6Rx4/
Utilizza il numero di ciascun membro dell'array in fase di iterazione per ottenere l'elemento in quell'indice di someOtherArray
, che è rappresentato dal this
momento che lo abbiamo passato come parametro di contesto.
Se non si imposta il contesto, si this
farà riferimento window
all'oggetto.
context
è dove si this
riferisce nella tua funzione iteratore. Per esempio:
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);
Il contesto consente di fornire argomenti al momento della chiamata, consentendo una facile personalizzazione delle funzioni di supporto predefinite generiche.
qualche esempio:
// 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);
Anche dagli esempi limitati, puoi vedere quanto potente può essere un "argomento in più" per la creazione di codice riutilizzabile. Invece di creare una diversa funzione di richiamata per ogni situazione, di solito è possibile adattare un helper di basso livello. L'obiettivo è quello di avere la tua logica personalizzata raggruppando un verbo e due sostantivi, con un minimo boilerplate.
Certo, le funzioni freccia hanno eliminato molti dei vantaggi del "codice golf" delle funzioni pure generiche, ma i vantaggi semantici e di coerenza rimangono.
Aggiungo sempre "use strict"
agli aiutanti per fornire [].map()
compatibilità nativa quando si passano le primitive. Altrimenti, sono costretti in oggetti, che di solito funzionano ancora, ma è più veloce e più sicuro essere specifici del tipo.
_.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>
Ecco un semplice esempio che potrebbe usare _.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();
Produzione:
items: [ 'banana', 'apple', 'kiwi' ]
Invece di chiamare addItem
più volte è possibile utilizzare la sottolineatura in questo modo:
_.each(['banana', 'apple', 'kiwi'], function(item) { x.addItem(item); });
che è identico a chiamare addItem
tre volte in sequenza con questi elementi. Fondamentalmente itera l'array e per ogni elemento chiama la tua funzione di callback anonima che chiama x.addItem(item)
. La funzione di richiamata anonima è simile alla addItem
funzione membro (ad esempio, prende un oggetto) ed è in qualche modo inutile. Quindi, invece di passare attraverso la funzione anonima, è meglio _.each
evitare questa indiretta e chiama addItem
direttamente:
_.each(['banana', 'apple', 'kiwi'], x.addItem);
ma questo non funzionerà, poiché la addItem
funzione membro all'interno del carrello this
non farà riferimento al x
carrello che hai creato. Ecco perché hai un'opzione per passare il tuo carrello x
da utilizzare come [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>
In breve, se si utilizza la funzione di callback a cui si passa _.each
in qualsiasi modo, this
è necessario specificare a cosa si this
deve fare riferimento all'interno della funzione di callback. Può sembrare x
ridondante nel mio esempio, ma x.addItem
è solo una funzione e potrebbe essere totalmente estranea a x
o basket
o qualsiasi altro oggetto, ad esempio :
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>
In altre parole, si associa un certo valore this
all'interno del callback o si può anche utilizzare il bind direttamente in questo modo:
_.each(['banana', 'apple', 'kiwi'], addItem.bind(x));
come questa funzione può essere utile con alcuni diversi metodi di sottolineatura?
In generale, se un underscorejs
metodo accetta una funzione di callback e se si desidera che tale callback venga richiamata su una funzione membro di un oggetto (ad esempio una funzione che utilizza this
), è possibile associare quella funzione a un oggetto o passare quell'oggetto come [context]
parametro e questo è l'intenzione primaria. E nella parte superiore della documentazione underscorejs, è esattamente quello che affermano: l'iterato è associato all'oggetto contesto, se ne viene passato uno
Come spiegato in altre risposte, context
è il this
contesto da utilizzare all'interno del callback passato each
.
Spiegherò questo con l'aiuto del codice sorgente dei metodi pertinenti dal carattere di sottolineatura
La definizione di _.each
o _.forEach
è la seguente:
_.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;
};
La seconda affermazione è importante notare qui
iteratee = optimizeCb(iteratee, context);
Qui, context
viene passato a un altro metodo optimizeCb
e la funzione restituita da esso viene quindi assegnata alla iteratee
quale viene chiamata in seguito.
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);
};
};
Come si può vedere dalla definizione del metodo precedente di optimizeCb
, se context
non viene passato, func
viene restituito così com'è. Se context
viene passato, la funzione di richiamata viene chiamata come
func.call(context, other_parameters);
^^^^^^^
func
viene chiamato con il call()
quale viene utilizzato per invocare un metodo impostandone il this
contesto. Quindi, quando this
viene utilizzato all'interno func
, farà riferimento a 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>
Puoi considerare context
l'ultimo parametro facoltativo su forEach
in JavaScript.
someOtherArray[num]
piuttosto chethis[num]
?