Що таке контекст у _.each (список, ітератор, [контекст])?


Відповіді:


220

Параметр контексту просто встановлює значення thisфункції ітератора.

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

Приклад роботи: http://jsfiddle.net/a6Rx4/

Він використовує число кожного ітераційного члена масиву, щоб отримати елемент у тому індексі someOtherArray, який представлений символомthis оскільки ми передали його як параметр контексту.

Якщо ви не встановите контекст, тоді thisбуде посилатися на windowоб’єкт.


7
У чому перевага? Чому б не просто посилатися, someOtherArray[num]а не this[num]?
csjacobs24

3
@ csjacobs24: звичайно мати набір функцій багаторазового використання, які не мали б доступу до локальної змінної області. Ось простий приклад: jsfiddle.net/a6Rx4/745

1
Ця відповідь відповідає на питання, але було б краще, якби вона наводила приклади, як це може бути корисним.
тимчасовий_користувач

50

contextтам, де thisпосилається на вашу функцію ітератора. Наприклад:

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

Контекст дозволяє наводити аргументи під час виклику, що дозволяє легко налаштувати загальні попередньо вбудовані допоміжні функції.

кілька прикладів:

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

Навіть з обмежених прикладів ви можете бачити, наскільки потужним може бути "додатковий аргумент" для створення коду, який може бути повторно використаний. Замість того, щоб робити різні функції зворотного виклику для кожної ситуації, зазвичай можна адаптувати помічника низького рівня. Мета полягає у тому, щоб мати власну логічну зв’язку дієслова та двох іменників з мінімальними табличками.

Справді, функції стрілок усунули багато переваг «кодового гольфу» загальних чистих функцій, але переваги семантичності та послідовності залишаються.

Я завжди додаю "use strict"помічників, щоб забезпечити [].map()сумісність при передачі примітивів. В іншому випадку їх примушують до об'єктів, які зазвичай все ще працюють, але швидше і безпечніше бути типовими.


5

Просте використання _.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>

Ось простий приклад, який можна використати _.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();

Вихід:

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

Замість того, щоб дзвонити addItemкілька разів, ви можете використовувати підкреслення таким чином:

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

що ідентично дзвонити addItemтри рази послідовно з цими предметами. В основному це повторює ваш масив і для кожного елемента викликає вашу анонімну функцію зворотного дзвінка, яка дзвонить x.addItem(item). Функція зворотного анонімного виклику схожа на addItemфункцію члена (наприклад, вона займає елемент) і є свого роду безглуздою. Отже, замість того, щоб переходити через анонімну функцію, краще _.eachуникати цієї непрямості та телефонувати addItemбезпосередньо:

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

але це не спрацює, оскільки addItemфункція члена всередині кошика thisне буде посилатися на ваш xкошик, який ви створили. Ось чому у вас є можливість передавати ваш кошик, xщоб використовувати його як [context]:

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

Повний приклад, що використовує _.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();
_.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>

Коротше кажучи, якщо функція зворотного дзвінка, якою ви передаєте _.eachбудь-який спосіб, використовує, thisтоді вам потрібно вказати, на що thisслід посилатися на функцію зворотного дзвінка. Може здатися , що xє зайвим в моєму прикладі, але x.addItemце просто функція і може бути абсолютно не пов'язані з xабо basket або будь-яким іншим об'єктом, наприклад :

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>

Іншими словами, ви прив'язуєте деяке значення до thisзворотного дзвінка, або ви також можете використовувати bind безпосередньо так:

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

наскільки ця функція може бути корисною для різних методів підкреслення?

Взагалі, якщо якийсь underscorejsметод виконує функцію зворотного виклику, і якщо ви хочете, щоб цей зворотний виклик був викликаний функцією-членом якогось об'єкта (наприклад, функцією, яка використовує this), ви можете прив'язати цю функцію до якогось об'єкта або передати цей об'єкт як [context]параметр, і це первинний намір. І вгорі документації підкреслюють саме те, що вони заявляють: ітератор прив’язаний до контекстного об'єкта, якщо його передано


4

Як пояснено в інших відповідях, чи contextє thisконтекст, до якого слід використовувати внутрішній зворотний виклик each.

Я поясню це за допомогою вихідного коду відповідних методів із підкреслення вихідного коду

Визначення _.eachабо _.forEachтаке:

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

Тут важливо зазначити друге твердження

iteratee = optimizeCb(iteratee, context);

Тут contextпередається інший метод, optimizeCbі повертається функція з нього призначається потім, iterateeяка викликається пізніше.

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

Як видно з наведеного вище визначення методу optimizeCb, якщо contextвін не пройде, то funcповертається таким, яким він є. Якщо contextпередано, функція зворотного дзвінка називається як

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

funcназивається, за допомогою call()якого використовується для виклику методу шляхом встановлення його thisконтексту. Отже, коли thisвикористовується всередині func, він буде посилатися на 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>

Ви можете вважати contextостаннім необов'язковим параметром forEachв JavaScript.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.