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


Answers:


220

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/

它使用要迭代的Array每个成员的编号来获取位于的索引处的项目,由于我们将其作为上下文参数传递了someOtherArray,因此该数字由表示this

如果未设置上下文,this则将引用该window对象。


7
这有什么好处?为什么不只是提及someOtherArray[num]而不是this[num]
csjacobs15年

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和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仅仅是一个功能,可以完全无关xbasket 或任何其他对象,例如

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文档的顶部,这正是他们声明的内容:如果传递了iteratee,则它绑定到上下文对象。


4

如其他答案所述,contextthis要在传递给的回调内使用的上下文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视为forEachJavaScript中的最后一个可选参数。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.