Was ist der Kontext in _.each (Liste, Iterator, [Kontext])?

Antworten:

220

Der Kontextparameter legt nur den Wert von thisin der Iteratorfunktion fest.

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);

Arbeitsbeispiel: http://jsfiddle.net/a6Rx4/

Es verwendet die Nummer von jedem Mitglied des Arrays, das iteriert wird, um das Element an dem Index von zu erhalten someOtherArray, der durch dargestellt wird, thisseit wir es als Kontextparameter übergeben haben.

Wenn Sie den Kontext nicht festlegen, thiswird auf das windowObjekt verwiesen.

user113716
quelle
7
Was ist der Vorteil davon? Warum nicht someOtherArray[num]eher auf als beziehen this[num]?
csjacobs24
3
@ csjacobs24: Es ist üblich, eine Reihe wiederverwendbarer Funktionen zu haben, die keinen Zugriff auf den Bereich der lokalen Variablen haben. Hier ist ein einfaches Beispiel: jsfiddle.net/a6Rx4/745
1
Diese Antwort beantwortet die Frage, aber es wäre besser, wenn sie Beispiele dafür liefert, wie dies nützlich sein kann.
temporärer_Benutzername
50

contextHier wird thisin Ihrer Iteratorfunktion darauf verwiesen. Beispielsweise:

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
quelle
7

In diesem Kontext können Sie beim Aufruf Argumente bereitstellen und so die generischen vorgefertigten Hilfsfunktionen einfach anpassen.

einige Beispiele:

// 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); 

Selbst anhand der begrenzten Beispiele können Sie sehen, wie leistungsfähig ein "zusätzliches Argument" für die Erstellung von wiederverwendbarem Code sein kann. Anstatt für jede Situation eine andere Rückruffunktion zu erstellen, können Sie normalerweise einen Helfer auf niedriger Ebene anpassen. Das Ziel ist, dass Ihre benutzerdefinierte Logik ein Verb und zwei Substantive mit minimalem Boilerplate bündelt.

Zwar haben Pfeilfunktionen viele der "Code Golf" -Vorteile generischer reiner Funktionen beseitigt, aber die semantischen und Konsistenzvorteile bleiben bestehen.

Ich füge immer "use strict"Helfer hinzu, um native [].map()Kompatibilität beim Übergeben von Grundelementen zu gewährleisten . Andernfalls werden sie zu Objekten gezwungen, was normalerweise immer noch funktioniert, aber es ist schneller und sicherer, typspezifisch zu sein.

Dandavis
quelle
5

Einfache Verwendung von _.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>

Hier ist ein einfaches Beispiel , das Folgendes verwenden könnte _.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();

Ausgabe:

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

Anstatt anzurufen addItem mehrmals anzurufen, können Sie den Unterstrich folgendermaßen verwenden :

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

addItemDies ist identisch mit einem dreimaligen sequentiellen Aufruf mit diesen Elementen. Grundsätzlich iteriert es Ihr Array und ruft für jedes Element Ihre anonyme Rückruffunktion auf, die aufruft x.addItem(item). Die anonyme Rückruffunktion ähneltaddItem Mitgliedsfunktion (z. B. nimmt sie ein Element) und ist irgendwie sinnlos. Anstatt eine anonyme Funktion zu durchlaufen, ist es besser, _.eachdiese Indirektion zu vermeiden und addItemdirekt aufzurufen :

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

Dies addItemfunktioniert thisjedoch nicht , da sich die Mitgliederfunktion des Warenkorbs nicht auf Ihren von xIhnen erstellten Warenkorb bezieht . Deshalb haben Sie die Möglichkeit, Ihren Warenkorb weiterzugebenx wie[context] :

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

Vollständiges Beispiel, das _.each und context verwendet:

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>

Kurz gesagt, wenn die Rückruffunktion, an die Sie _.eachin irgendeiner Weise übergeben werden, verwendet thiswird, müssen Sie angeben, worauf thisin Ihrer Rückruffunktion Bezug genommen werden soll. Es mag wie xüberflüssig in meinem Beispiel ist, sondern x.addItemist nur eine Funktion und könnte völlig unabhängig von xoder basket oder einem anderen Objekt, zum Beispiel :

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>

Mit anderen Worten, Sie binden einen Wert an thisIhren Rückruf, oder Sie können bind auch direkt wie folgt verwenden:

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

Wie kann diese Funktion bei verschiedenen Unterstrichmethoden nützlich sein?

Wenn eine underscorejsMethode eine Rückruffunktion verwendet und dieser Rückruf für eine Elementfunktion eines Objekts (z. B. eine verwendete Funktion this) aufgerufen werden soll, können Sie diese Funktion im Allgemeinen an ein Objekt binden oder dieses Objekt als [context]Parameter übergeben, und das ist die primäre Absicht. Und ganz oben in der Dokumentation zu Unterstrichen steht genau das: Der Iterat ist an das Kontextobjekt gebunden, wenn eines übergeben wird

Pavel P.
quelle
4

Wie in anderen Antworten erläutert, contextwird der thisKontext, der innerhalb des Rückrufs verwendet werden soll, an übergeben each.

Ich werde dies mit Hilfe des Quellcodes der relevanten Methoden aus erklären Unterstrich

Die Definition von _.eachoder _.forEachlautet wie folgt:

_.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;
};

Die zweite Aussage ist hier wichtig zu beachten

iteratee = optimizeCb(iteratee, context);

Hier contextwird an eine andere Methode übergeben optimizeCbund die von ihr zurückgegebene Funktion wird dann zugewiesen, iterateedie später aufgerufen wird.

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);
  };
};

Wie aus der obigen Methodendefinition von ersichtlich ist optimizeCb, contextwird , wenn nicht bestanden, funczurückgegeben, wie es ist. Wenn contextübergeben, wird die Rückruffunktion als aufgerufen

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

funcwird aufgerufen, mit call()dem eine Methode aufgerufen wird, indem der thisKontext festgelegt wird. Wenn thises also im Inneren verwendet wird func, bezieht es sich auf 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>

Sie können contextals letzten optionalen Parameter forEachin JavaScript betrachten.

Tushar
quelle