Respostas:
O parâmetro context apenas define o valor de this
na função do iterador.
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);
Exemplo de trabalho: http://jsfiddle.net/a6Rx4/
Ele usa o número de cada membro da matriz que está sendo iterado para obter o item no índice de someOtherArray
, que é representado porthis
desde que passamos como o parâmetro de contexto.
Se você não definir o contexto, this
fará referência ao window
objeto.
context
é onde this
se refere na sua função de iterador. Por exemplo:
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);
O contexto permite que você forneça argumentos no momento da chamada, permitindo fácil personalização de funções auxiliares pré-criadas genéricas.
alguns exemplos:
// 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);
Mesmo nos exemplos limitados, você pode ver o quão poderoso pode ser um "argumento extra" para criar código reutilizável. Em vez de criar uma função de retorno de chamada diferente para cada situação, geralmente você pode adaptar um auxiliar de baixo nível. O objetivo é ter sua lógica personalizada agrupando um verbo e dois substantivos, com um mínimo de clichê.
É certo que as funções de seta eliminaram muitas das vantagens do "código de golfe" das funções puras genéricas, mas as vantagens semânticas e de consistência permanecem.
Eu sempre adiciono "use strict"
aos auxiliares para fornecer [].map()
compatibilidade nativa ao transmitir as primitivas. Caso contrário, eles são coagidos a objetos, o que geralmente ainda funciona, mas é mais rápido e seguro ser específico ao 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>
Aqui está um exemplo simples que pode ser usado _.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();
Resultado:
items: [ 'banana', 'apple', 'kiwi' ]
Em vez de ligar addItem
várias vezes, você pode usar o sublinhado desta maneira:
_.each(['banana', 'apple', 'kiwi'], function(item) { x.addItem(item); });
que é idêntico a chamar addItem
três vezes sequencialmente com esses itens. Basicamente, itera sua matriz e para cada item chama sua função de retorno de chamada anônima que chama x.addItem(item)
. A função de retorno de chamada anônima é semelhante à addItem
função de membro (por exemplo, leva um item) e é meio inútil. Então, em vez de passar por uma função anônima, é melhor _.each
evitar esse indireto e chamar addItem
diretamente:
_.each(['banana', 'apple', 'kiwi'], x.addItem);
mas isso não funcionará, pois a addItem
função de membro da cesta this
não se refere à x
cesta que você criou. É por isso que você tem a opção de passar sua cesta x
para ser usada como [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>
Em resumo, se a função de retorno de chamada que você passar de _.each
alguma forma usar this
, será necessário especificar o que this
deve estar se referindo dentro da sua função de retorno de chamada. Pode parecer que x
é redundante no meu exemplo, mas x.addItem
é apenas uma função e pode ser totalmente sem relação com x
ou basket
ou qualquer outro objeto, por exemplo :
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>
Em outras palavras, você vincula algum valor a this
seu retorno de chamada ou pode usar o bind diretamente assim:
_.each(['banana', 'apple', 'kiwi'], addItem.bind(x));
como esse recurso pode ser útil com alguns métodos diferentes de sublinhado?
Em geral, se algum underscorejs
método usa uma função de retorno de chamada e se você deseja que essa chamada seja chamada em alguma função membro de algum objeto (por exemplo, uma função que use this
), você poderá vincular essa função a algum objeto ou passar esse objeto como [context]
parâmetro e isso é a intenção primária. E na parte superior da documentação do underscorejs, é exatamente o que eles afirmam: o iterado é vinculado ao objeto de contexto, se um for passado
Conforme explicado em outras respostas, context
é o this
contexto a ser usado dentro do retorno de chamada passado para each
.
Vou explicar isso com a ajuda do código fonte de métodos relevantes a partir do código fonte de sublinhado
A definição de _.each
ou _.forEach
é a seguinte:
_.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;
};
Segunda declaração é importante notar aqui
iteratee = optimizeCb(iteratee, context);
Aqui, context
é passado para outro método optimizeCb
e a função retornada é atribuída à iteratee
qual é chamada mais tarde.
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);
};
};
Como pode ser visto na definição do método acima optimizeCb
, se context
não for passado, func
será retornado como está. Se context
for aprovada, a função de retorno de chamada é chamada como
func.call(context, other_parameters);
^^^^^^^
func
é chamado com o call()
qual é usado para chamar um método, definindo o this
contexto dele. Então, quando this
é usado dentro func
, ele se refere 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>
Você pode considerar context
como o último parâmetro opcional forEach
no JavaScript.
someOtherArray[num]
invés dethis[num]
?