Che cos'è il contesto in _.each (elenco, iteratore, [contesto])?


Risposte:


220

Il parametro di contesto imposta semplicemente il valore di thisnella 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 thismomento che lo abbiamo passato come parametro di contesto.

Se non si imposta il contesto, si thisfarà riferimento windowall'oggetto.


7
Qual è il vantaggio di questo? Perché non riferirsi someOtherArray[num]piuttosto che this[num]?
csjacobs24,

3
@ csjacobs24: è comune avere una serie di funzioni riutilizzabili che non avrebbero accesso all'ambito della variabile locale. Ecco un semplice esempio: jsfiddle.net/a6Rx4/745

1
Questa risposta risponde alla domanda, ma sarebbe meglio se fornisse esempi di come ciò possa essere utile.
temporaneo

50

contextè dove si thisriferisce 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);

7

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.


5

Uso semplice di _.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>

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 addItempiù volte è possibile utilizzare la sottolineatura in questo modo:

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

che è identico a chiamare addItemtre 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 addItemfunzione membro (ad esempio, prende un oggetto) ed è in qualche modo inutile. Quindi, invece di passare attraverso la funzione anonima, è meglio _.eachevitare questa indiretta e chiama addItemdirettamente:

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

ma questo non funzionerà, poiché la addItemfunzione membro all'interno del carrello thisnon farà riferimento al xcarrello che hai creato. Ecco perché hai un'opzione per passare il tuo carrello xda utilizzare come [context]:

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

Esempio completo che utilizza _.each e il contesto:

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 _.eachin qualsiasi modo, thisè necessario specificare a cosa si thisdeve fare riferimento all'interno della funzione di callback. Può sembrare xridondante nel mio esempio, ma x.addItemè solo una funzione e potrebbe essere totalmente estranea a xo 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 thisall'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 underscorejsmetodo 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


4

Come spiegato in altre risposte, contextè il thiscontesto 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 _.eacho _.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, contextviene passato a un altro metodo optimizeCbe la funzione restituita da esso viene quindi assegnata alla iterateequale 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 contextnon viene passato, funcviene restituito così com'è. Se contextviene passato, la funzione di richiamata viene chiamata come

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

funcviene chiamato con il call()quale viene utilizzato per invocare un metodo impostandone il thiscontesto. Quindi, quando thisviene 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 contextl'ultimo parametro facoltativo su forEachin JavaScript.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.