¿Qué es el contexto en _.each (list, iterator, [context])?

Respuestas:

220

El parámetro de contexto solo establece el valor de thisen la función de iterador.

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

Ejemplo de trabajo: http://jsfiddle.net/a6Rx4/

Utiliza el número de cada miembro de la matriz que se itera para obtener el elemento en ese índice de someOtherArray, que se representa thisdesde que lo pasamos como parámetro de contexto.

Si no establece el contexto, thisse referirá al windowobjeto.

usuario113716
fuente
77
¿Cuál es la ventaja de eso? ¿Por qué no simplemente referirse a someOtherArray[num]más que this[num]?
csjacobs24
3
@ csjacobs24: es común tener un conjunto de funciones reutilizables que no tendrían acceso al alcance de la variable local. Aquí hay un ejemplo simple: jsfiddle.net/a6Rx4/745
1
Esta respuesta responde a la pregunta, pero sería mejor si proporcionara ejemplos de cómo esto puede ser útil.
temporary_user_name
50

contextes donde se thisrefiere en su función de iterador. Por ejemplo:

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

El contexto le permite proporcionar argumentos en el momento de la llamada, lo que permite una fácil personalización de las funciones auxiliares genéricas preconstruidas.

algunos ejemplos:

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

Incluso a partir de los ejemplos limitados, puede ver cuán poderoso puede ser un "argumento adicional" para crear código reutilizable. En lugar de hacer una función de devolución de llamada diferente para cada situación, generalmente puede adaptar un asistente de bajo nivel. El objetivo es tener su lógica personalizada agrupando un verbo y dos sustantivos, con un mínimo repetitivo.

Es cierto que las funciones de flecha han eliminado muchas de las ventajas de "golf de código" de las funciones genéricas puras, pero las ventajas de semántica y consistencia permanecen.

Siempre agrego "use strict"a los ayudantes para proporcionar [].map()compatibilidad nativa al pasar primitivas. De lo contrario, se convierten en objetos, que por lo general todavía funcionan, pero es más rápido y seguro ser específico para cada tipo.

dandavis
fuente
5

Uso simple de _.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>

Aquí hay un ejemplo simple que podría usar _.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();

Salida:

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

En lugar de llamar addItemvarias veces , puede usar subrayado de esta manera:

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

que es idéntico a llamar addItemtres veces secuencialmente con estos elementos. Básicamente, itera su matriz y para cada elemento llama a su función de devolución de llamada anónima que llama x.addItem(item). La función de devolución de llamada anónima es similar a la addItemfunción miembro (por ejemplo, toma un elemento) y no tiene sentido. Entonces, en lugar de pasar por una función anónima, es mejor que _.eachevite esta indirección y llame addItemdirectamente:

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

pero esto no funcionará, ya que la addItemfunción miembro dentro de la cesta thisno se referirá a la xcesta que creó. Es por eso que tiene la opción de pasar su canasta xpara usarla como [context]:

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

Ejemplo completo que usa _.each y 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>

En resumen, si la función de devolución de llamada a la que pasa de _.eachalguna manera usa, thisentonces debe especificar a qué thisdebería referirse dentro de su función de devolución de llamada. Puede parecer que xes redundante en mi ejemplo, pero x.addItemes sólo una función y podría ser totalmente ajenos a xo basket o cualquier otro objeto, por ejemplo :

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>

En otras palabras, enlaza algún valor thisdentro de su devolución de llamada, o bien puede usar el enlace directamente de esta manera:

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

¿Cómo puede ser útil esta característica con algunos métodos diferentes de subrayado?

En general, si algún underscorejsmétodo toma una función de devolución de llamada y si desea que esa devolución de llamada se invoque en alguna función miembro de algún objeto (por ejemplo, una función que usa this), puede vincular esa función a algún objeto o pasar ese objeto como [context]parámetro y eso es La intención principal. Y en la parte superior de la documentación de subrayado, eso es exactamente lo que dicen: el iterado está vinculado al objeto de contexto, si se pasa uno

Pavel P
fuente
4

Como se explica en otras respuestas, contextes el thiscontexto que se utilizará dentro de la devolución de llamada que se pasa each.

Explicaré esto con la ayuda del código fuente de métodos relevantes del código fuente subrayado

La definición de _.eacho _.forEaches la siguiente:

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

Es importante tener en cuenta la segunda declaración aquí

iteratee = optimizeCb(iteratee, context);

Aquí, contextse pasa a otro método optimizeCby la función devuelta desde allí se asigna a la iterateeque se llama más tarde.

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

Como se puede ver en la definición de método anterior optimizeCb, si contextno se pasa, funcse devuelve como está. Si contextse pasa, la función de devolución de llamada se llama como

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

funcSe llama con el call()que se utiliza para invocar un método estableciendo el thiscontexto del mismo. Entonces, cuando thisse usa adentro func, se referirá a 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>

Puede considerarlo contextcomo el último parámetro opcional forEachen JavaScript.

Tushar
fuente