Что такое контекст в _.each (список, итератор, [контекст])? - PullRequest
161 голосов
/ 09 февраля 2011

Я новичок в underscore.js. Какова цель [context] в _.each()? Как его использовать?

Ответы [ 5 ]

219 голосов
/ 09 февраля 2011

Параметр context просто устанавливает значение 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.

49 голосов
/ 09 февраля 2011

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);
6 голосов
/ 27 апреля 2017

Контекст позволяет вам предоставлять аргументы во время вызова, что позволяет легко настраивать стандартные встроенные вспомогательные функции.

некоторые примеры:

// 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() совместимость при передаче примитивов. В противном случае они преобразуются в объекты, которые обычно работают, но быстрее и безопаснее быть привязанными к типу.

4 голосов
/ 24 апреля 2017

Как объяснено в других ответах, 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.

3 голосов
/ 30 апреля 2017

Простое использование _.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 и context:

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], и это является основным намерением. И в верхней части документации underscorejs, это именно то, что они заявляют: Итерируемый привязывается к объекту контекста, если он передан

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...