Je suis nouveau sur underscore.js. Quel est le but de [context]
dans _.each()
? Comment devrait-il être utilisé?
Le paramètre context définit uniquement la valeur de this
dans la fonction itérateur.
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);
Exemple de travail: http://jsfiddle.net/a6Rx4/
Il utilise le numéro de chaque membre du tableau en cours d'itération pour obtenir l'élément à l'indice de someOtherArray
, représenté par this
, car nous l'avons passé en tant que paramètre context.
Si vous ne définissez pas le contexte, alors this
fera référence à l'objet window
.
context
est l'endroit où this
fait référence à votre fonction itérateur. Par exemple:
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);
Le contexte vous permet de fournir des arguments au moment de l'appel, ce qui permet de personnaliser facilement des fonctions d'assistance génériques prédéfinies.
quelques exemples:
// 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);
Même à partir d'exemples limités, vous pouvez voir à quel point un "argument supplémentaire" peut être puissant pour créer du code réutilisable. Au lieu de créer une fonction de rappel différente pour chaque situation, vous pouvez généralement adapter un assistant de bas niveau. Le but est que votre logique personnalisée comprenne un verbe et deux noms, avec un passe-partout minimal.
Certes, les fonctions fléchées ont éliminé de nombreux avantages des fonctions pures génériques "Code Golf", mais les avantages sémantiques et de cohérence demeurent.
J'ajoute toujours "use strict"
aux aides pour fournir une compatibilité native [].map()
lors du passage de primitives. Sinon, ils sont forcés de pénétrer dans des objets, ce qui fonctionne généralement toujours, mais il est plus rapide et plus sûr d'être spécifique à un type.
Comme expliqué dans d'autres réponses, context
est le contexte this
à utiliser dans le rappel transmis à each
.
Je vais expliquer cela avec l'aide du code source des méthodes pertinentes de code source souligné
La définition de _.each
ou _.forEach
est la suivante:
_.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 deuxième déclaration est importante à noter ici
iteratee = optimizeCb(iteratee, context);
Ici, context
est passé à une autre méthode optimizeCb
et la fonction renvoyée est assignée à iteratee
qui sera appelée plus tard.
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);
};
};
Comme le montre la définition ci-dessus de la méthode optimizeCb
, si context
n'est pas transmis, alors func
est renvoyé tel quel. Si context
est passé, la fonction de rappel est appelée comme
func.call(context, other_parameters);
^^^^^^^
func
est appelé avec call()
qui est utilisé pour appeler une méthode en définissant son contexte this
. Ainsi, lorsque this
est utilisé dans func
, il fera référence à 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>
Vous pouvez considérer context
comme dernier paramètre facultatif de forEach
en JavaScript.
_.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>
Voici exemple simple qui pourrait utiliser _.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();
Sortie:
items: [ 'banana', 'Apple', 'kiwi' ]
Au lieu d'appeler addItem
plusieurs fois vous pouvez utiliser un trait de soulignement de cette façon:
_.each(['banana', 'Apple', 'kiwi'], function(item) { x.addItem(item); });
ce qui est identique à l'appel de addItem
trois fois de manière séquentielle avec ces éléments. Fondamentalement, il itère votre tableau et pour chaque élément appelle votre fonction de rappel anonyme qui appelle x.addItem(item)
. La fonction de rappel anonyme est similaire à la fonction membre addItem
(elle prend par exemple un élément) et est en quelque sorte sans objet. Ainsi, au lieu de passer par une fonction anonyme, il est préférable que _.each
évite cet indirection et appelle directement addItem
:
_.each(['banana', 'Apple', 'kiwi'], x.addItem);
mais cela ne fonctionnera pas, car la fonction membre addItem
à l'intérieur du panier this
ne fera pas référence à votre panier x
que vous avez créé. C'est pourquoi vous avez la possibilité de transmettre votre panier x
à utiliser en tant que [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>
En bref, si la fonction de rappel que vous transmettez à _.each
utilise de quelque manière que ce soit this
, vous devez spécifier le nom this
qui doit faire référence à l'intérieur de votre fonction de rappel. Il peut sembler que x
soit redondant dans mon exemple, mais x.addItem
n’est qu’une fonction et n’a aucun rapport avec x
ou basket
ou n’importe quel autre objet, par exemple :
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>
En d'autres termes, vous liez une valeur à this
dans votre rappel, ou vous pouvez aussi utiliser bind directement comme ceci:
_.each(['banana', 'Apple', 'kiwi'], addItem.bind(x));
en quoi cette fonctionnalité peut-elle être utile avec différentes méthodes de soulignement?
En général, si une méthode underscorejs
prend une fonction de rappel et si vous souhaitez que ce rappel soit appelée sur une fonction membre d'un objet (par exemple, une fonction qui utilise this
), vous pouvez lier cette fonction à un objet ou transmettre cet objet en tant que [context]
paramètre et c'est l'intention principale. Et en haut de la documentation de underscorejs, c’est exactement ce qu’ils indiquent: L’itéré est lié à l’objet context, s’il est passé