Что такое контекст в _.each(list, iterator, [context])?

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

Ответ 1

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

Ответ 2

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

Ответ 3

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

Ответ 4

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

несколько примеров:

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