Qu'est-ce que le contexte dans _.each (liste, itérateur, [contexte])?

Réponses:

220

Le paramètre context définit simplement la valeur de thisdans la fonction d'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 à cet index de someOtherArray, qui est représenté par thispuisque nous l'avons passé en tant que paramètre de contexte.

Si vous ne définissez pas le contexte, alors thisfera référence à l' windowobjet.

utilisateur113716
la source
7
Quel est l'avantage de cela? Pourquoi ne pas simplement faire référence à someOtherArray[num]plutôt que this[num]?
csjacobs24
3
@ csjacobs24: Il est courant d'avoir un ensemble de fonctions réutilisables qui n'auraient pas accès à la portée de la variable locale. Voici un exemple simple: jsfiddle.net/a6Rx4/745
1
Cette réponse répond à la question, mais il serait préférable qu'elle fournisse des exemples de la façon dont cela peut être utile.
temporary_user_name
50

contextest où thisfait référence dans votre fonction d'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);
Harmen
la source
7

Le contexte vous permet de fournir des arguments au moment de l'appel, permettant une personnalisation facile des fonctions d'assistance génériques pré-construites.

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 des 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. L'objectif est d'avoir votre logique personnalisée regroupant un verbe et deux noms, avec un minimum de passe-partout.

Certes, les fonctions fléchées ont éliminé de nombreux avantages de «code golf» des fonctions pures génériques, mais les avantages sémantiques et de cohérence demeurent.

J'ajoute toujours "use strict"aux helpers pour fournir une [].map()compatibilité native lors du passage des primitives. Sinon, ils sont forcés en objets, ce qui fonctionne généralement toujours, mais il est plus rapide et plus sûr d'être spécifique au type.

dandavis
la source
5

Utilisation simple de _.each

_.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 un 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();

Production:

items:  [ 'banana', 'apple', 'kiwi' ]

Au lieu d'appeler addItemplusieurs fois, vous pouvez utiliser le soulignement de cette façon:

_.each(['banana', 'apple', 'kiwi'], function(item) { x.addItem(item); });

ce qui revient à appeler addItemtrois 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 addItemfonction membre (par exemple, elle prend un élément) et est un peu inutile. Donc, au lieu de passer par une fonction anonyme, il vaut mieux _.eachéviter cette indirection et appeler addItemdirectement:

_.each(['banana', 'apple', 'kiwi'], x.addItem);

mais cela ne fonctionnera pas, car la addItemfonction membre de l' intérieur du panier thisne fera pas référence à votre xpanier que vous avez créé. C'est pourquoi vous avez la possibilité de passer votre panier xpour être utilisé comme [context]:

_.each(['banana', 'apple', 'kiwi'], x.addItem, x);

Exemple complet utilisant _.each et le contexte:

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 à laquelle vous passez d' _.eachune manière ou d'une autre utilise, thisvous devez spécifier ce à quoi vous devez thisfaire référence dans votre fonction de rappel. Cela peut sembler xredondant dans mon exemple, mais ce x.addItemn'est qu'une fonction et pourrait être totalement indépendante de xou basket ou de tout 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 à l' thisintérieur de votre rappel, ou vous pouvez également utiliser bind directement comme ceci:

_.each(['banana', 'apple', 'kiwi'], addItem.bind(x));

comment cette fonctionnalité peut être utile avec certaines méthodes de soulignement différentes?

En général, si une underscorejsméthode prend une fonction de rappel et si vous voulez que ce rappel soit appelé sur une fonction membre d'un objet (par exemple, une fonction qui utilise this), vous pouvez lier cette fonction à un objet ou passer cet objet comme [context]paramètre et c'est l'intention première. Et en haut de la documentation de underscorejs, c'est exactement ce qu'ils déclarent: l'iteratee est lié à l'objet de contexte, s'il est passé

Pavel P
la source
4

Comme expliqué dans d'autres réponses, contextle thiscontexte à utiliser dans le rappel est-il passé each.

Je vais vous expliquer cela à l'aide du code source des méthodes pertinentes à partir du code source de soulignement

La définition de _.eachou _.forEachest 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, contextest passé à une autre méthode optimizeCbet la fonction renvoyée par celle-ci est ensuite affectée à iterateelaquelle est appelée ultérieurement.

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 on peut le voir à partir de la définition de méthode ci-dessus optimizeCb, si contextn'est pas passé, il funcest renvoyé tel quel. Si contextest passé, la fonction de rappel est appelée comme

func.call(context, other_parameters);
          ^^^^^^^

funcest appelé avec call()lequel est utilisé pour invoquer une méthode en définissant son thiscontexte. Donc, quand il thisest utilisé à l'intérieur 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 contextcomme le dernier paramètre facultatif forEachdans JavaScript.

Tushar
la source