什么是 _. each 中的上下文(列表、迭代器、[上下文]) ?

我是一个新手,在 _.each()中使用 [context]的目的是什么? 它应该如何使用?

165085 次浏览

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

工作示例: < a href = “ http://jsfiddle.net/a6Rx4/”rel = “ nofollow noReferrer”> http://jsfiddle.net/a6rx4/

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

如果不设置上下文,那么 this将引用 window对象。


附加:

为了回答在下面的评论中发现的 What's the advantage of that? Why not just refer to someOtherArray[num] rather than this[num]?反对的问题,让我们将匿名的 iteratee回调移动到一个容易重用的函数中:

const someOtherArray  = ["name","patrick","d","w"];
const yetAnotherArray = ["what","goes","here","?"];


function alertStr(num){
alert( this[num] );
}


_.each([1, 2, 3], alertStr, someOtherArray);
_.each([1, 2, 3], alertStr, yetAnotherArray);

您可以看到 this引用如何允许我们跨多个具有不同 context值的 _.each调用重用 iteratee函数。如果我们将 someOtherArray硬编码在 iteratee中,这将无法工作。

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

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

我将借助来自 下划线源代码的相关方法的源代码来解释这一点

_.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);
^^^^^^^

call()调用 funccall()用于通过设置方法的 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);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>

You can consider context as the last optional parameter to forEach in JavaScript.

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

例如:

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

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

Here's simple example that could use _.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 和上下文的完整示例:

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>

In short, if callback function that you pass to _.each in any way uses this then you need to specify what this should be referring to inside your callback function. It may seem like x is redundant in my example, but x.addItem is just a function and could be totally unrelated to x or basket 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>

In other words, you bind some value to this inside your callback, or you may as well use bind directly like this:

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

这个特性如何在不同的下划线方法中发挥作用?

一般来说,如果某个 underscorejs方法接受一个回调函数,并且如果你想对某个对象的某个成员函数(例如使用 this的函数)调用该回调函数,那么你可以将该函数绑定到某个对象,或者将该对象作为 [context]参数传递,这是主要意图。在 underscorejs 文档的顶部,正是他们所陈述的: 如果传递了上下文对象,则迭代器将绑定到该上下文对象