Co to jest kontekst w _.each (lista, iterator, [kontekst])?


Odpowiedzi:


220

Parametr context ustawia po prostu wartość thisw funkcji iteratora.

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

Przykład roboczy: http://jsfiddle.net/a6Rx4/

Używa liczby z każdego elementu składowego tablicy, która jest iterowana, aby uzyskać element o tym indeksie someOtherArray, który jest reprezentowany przez, thisponieważ przekazaliśmy go jako parametr kontekstu.

Jeśli nie ustawisz kontekstu, thisbędzie odnosić się do windowobiektu.


7
Jaka jest tego zaleta? Dlaczego po prostu nie odnosić się do someOtherArray[num]zamiast this[num]?
csjacobs24

3
@ csjacobs24: Powszechne jest posiadanie zestawu funkcji wielokrotnego użytku, które nie miałyby dostępu do zakresu zmiennej lokalnej. Oto prosty przykład: jsfiddle.net/a6Rx4/745

1
Ta odpowiedź odpowiada na pytanie, ale byłoby lepiej, gdyby zawierała przykłady, w jaki sposób może to być przydatne.
temporary_user_name

50

contextgdzie thisodwołuje się do funkcji iteratora. Na przykład:

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

Kontekst umożliwia dostarczanie argumentów w czasie wywołania, umożliwiając łatwe dostosowywanie ogólnych wstępnie zbudowanych funkcji pomocniczych.

kilka przykładów:

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

Nawet na podstawie ograniczonych przykładów widać, jak potężny może być „dodatkowy argument” przy tworzeniu kodu wielokrotnego użytku. Zamiast tworzyć inną funkcję zwrotną dla każdej sytuacji, zwykle można dostosować pomocnika niskiego poziomu. Celem jest, aby Twoja niestandardowa logika obejmowała czasownik i dwa rzeczowniki przy minimalnym schemacie.

Wprawdzie funkcje strzałkowe wyeliminowały wiele zalet „kodu golfowego” ogólnych funkcji czystych, ale zalety semantyczne i spójności pozostają.

Zawsze dodaję "use strict"do pomocników, aby zapewnić natywną [].map()zgodność podczas przekazywania prymitywów. W przeciwnym razie są przekształcane w obiekty, co zwykle nadal działa, ale szybciej i bezpieczniej jest określić typ.


5

Proste użycie _.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>

Oto prosty przykład , którego można użyć _.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();

Wynik:

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

Zamiast dzwonić addItemwiele razy, możesz użyć podkreślenia w ten sposób:

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

co jest identyczne z wywołaniem addItemtrzykrotnego sekwencyjnego połączenia z tymi elementami. Zasadniczo iteruje twoją tablicę i dla każdego elementu wywołuje twoją anonimową funkcję zwrotną, która wywołuje x.addItem(item). Anonimowa funkcja zwrotna jest podobna do addItemfunkcji składowej (np. Pobiera element) i jest trochę bezcelowa. Więc zamiast przechodzić przez funkcję anonimową, lepiej jest _.eachunikać tego pośrednictwa i wywoływać addItembezpośrednio:

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

ale to nie zadziała, ponieważ addItemfunkcja elementu członkowskiego wewnątrz koszyka thisnie będzie odnosić się do Twojego xkoszyka, który utworzyłeś. Dlatego masz możliwość przekazania swojego koszyka xdo wykorzystania jako [context]:

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

Pełny przykład wykorzystujący _.each i kontekst:

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>

Krótko mówiąc, jeśli funkcja zwrotna, do której _.eachw jakikolwiek sposób przekazujesz, używa this, musisz określić, do czego thispowinno się odnosić wewnątrz funkcji zwrotnej. Może wydawać się xto zbędne w moim przykładzie, ale x.addItemjest po prostu funkcją i może być całkowicie niezwiązane xlub basket lub jakiegokolwiek innego obiektu, na przykład :

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>

Innymi słowy, możesz przypisać jakąś wartość do thisswojego wywołania zwrotnego lub równie dobrze możesz użyć bind bezpośrednio w ten sposób:

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

jak ta funkcja może być przydatna w przypadku różnych metod podkreślania?

Ogólnie, jeśli jakaś underscorejsmetoda przyjmuje funkcję zwrotną i jeśli chcesz, aby ta funkcja zwrotna była wywoływana na jakiejś funkcji składowej jakiegoś obiektu (np. Funkcji, która używa this), możesz powiązać tę funkcję z jakimś obiektem lub przekazać ten obiekt jako [context]parametr i to jest pierwotny zamiar. A na początku dokumentacji podkreślenia, dokładnie to stwierdzają: iterat jest powiązany z obiektem kontekstu, jeśli taki jest przekazywany


4

Jak wyjaśniono w innych odpowiedziach, contextjest thiskontekst, który ma być używany w wywołaniu zwrotnym przekazywanym do each.

Wyjaśnię to za pomocą kodu źródłowego odpowiednich metod z podkreślonego kodu źródłowego

Definicja _.eachlub _.forEachjest następująca:

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

Warto zwrócić uwagę na drugie stwierdzenie

iteratee = optimizeCb(iteratee, context);

Tutaj contextjest przekazywana do innej metody, optimizeCba zwracana przez nią funkcja jest następnie przypisywana do iterateektórej jest wywoływana później.

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

Jak widać z powyższej definicji metody optimizeCb, jeśli contextnie jest przekazywane, to funcjest zwracane bez zmian . Jeśli contextjest przekazywane, funkcja zwrotna jest wywoływana jako

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

funcjest wywoływana, za pomocą call()której jest używana do wywołania metody poprzez ustawienie jej thiskontekstu. Więc kiedy thisjest używany w środku func, będzie odnosić się do 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>

Możesz rozważyć contextjako ostatni opcjonalny parametr forEachw JavaScript.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.