_.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
只是一个函数,可能与 x
或 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>
换句话说,你可以在你的回调中绑定一些值到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
上下文来调用方法。所以,当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
视为javascript 中forEach
的最后一个可选参数。
【讨论】:
【参考方案4】: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);
【讨论】:
【参考方案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]) 中的上下文是啥?的主要内容,如果未能解决你的问题,请参考以下文章
underscorejs类库之_.each(list, iteratee, [context])