_.each(list, iterator, [context]) 中的上下文是啥?

Posted

技术标签:

【中文标题】_.each(list, iterator, [context]) 中的上下文是啥?【英文标题】:What is context in _.each(list, iterator, [context])?_.each(list, iterator, [context]) 中的上下文是什么? 【发布时间】:2011-06-24 04:55:33 【问题描述】:

我是 underscore.js 的新手。 _.each() 中的[context] 的用途是什么?应该怎么用?

【问题讨论】:

【参考方案1】:

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

这里是simple example,可以使用_.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 多次you could use underscore:

_.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 只是一个函数,可能与 xbasket or any other object, for example 完全无关:

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 文档的顶部,这正是他们所说的:The iteratee is bound to the context object, if one is passed

【讨论】:

【参考方案2】:

上下文允许您在调用时提供参数,从而可以轻松自定义通用的预构建帮助函数。

一些例子:

// 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() 兼容性。否则,它们会被强制转换为对象,这通常仍然有效,但特定于类型会更快更安全。

【讨论】:

【参考方案3】:

正如其他答案中所解释的,context 是在传递给each 的回调中使用的this 上下文。

我将借助underscore source code的相关方法的源代码来解释这一点

_.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 上下文来调用方法。所以,当thisfunc中使用时,它会引用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);
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"&gt;&lt;/script&gt;

您可以将context 视为javascriptforEach 的最后一个可选参数。

【讨论】:

【参考方案4】:

contextthis 在迭代器函数中所指的位置。例如:

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

【讨论】:

【参考方案5】:

上下文参数只是在迭代器函数中设置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 对象。

【讨论】:

这样做有什么好处?为什么不直接引用someOtherArray[num] 而不是this[num] @csjacobs24:拥有一组无法访问局部变量范围的可重用函数是很常见的。这是一个简单的例子:jsfiddle.net/a6Rx4/745 这个答案确实回答了这个问题,但如果它提供了如何有用的例子会更好。

以上是关于_.each(list, iterator, [context]) 中的上下文是啥?的主要内容,如果未能解决你的问题,请参考以下文章

Python48--魔法方法:迭代器&生成器

集合中的 for-Each循环

underscorejs类库之_.each(list, iteratee, [context])

list($name,$value)=each($_POST)

迭代器和生成器

each iteration和 each occurrunce的区别