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

发布于 2024-10-16 12:25:07 字数 92 浏览 5 评论 0原文

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

I am new to underscore.js. What is the purpose of [context] in _.each()? How should it be used?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

行至春深 2024-10-23 12:25:07

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/

它使用正在迭代的数组中每个成员的编号,以获取 someOtherArray 索引处的项目,该索引由 this 表示,因为我们将其作为上下文参数传递。

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


附加:

回答这样做的好处是什么?为什么不直接引用 someOtherArray[num] 而不是 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 中,那么这将不起作用。

The context parameter just sets the value of this in the iterator function.

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

Working Example: http://jsfiddle.net/a6Rx4/

It uses the number from each member of the Array being iterated to get the item at that index of someOtherArray, which is represented by this since we passed it as the context parameter.

If you do not set the context, then this will refer to the window object.


Extras:

To answer the What's the advantage of that? Why not just refer to someOtherArray[num] rather than this[num]? upvoted question found in the comments below, let's move the anonymous iteratee callback into a function for easy re-use:

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

You can see how the this reference allows us to re-use the iteratee function across multiple _.each calls with different context values. This would not work if we had the someOtherArray hardcoded inside the iteratee.

柠檬色的秋千 2024-10-23 12:25:07

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

context is where this refers to in your iterator function. For example:

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);
一身骄傲 2024-10-23 12:25:07

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

一些示例:

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

The context lets you provide arguments at call-time, allowing easy customization of generic pre-built helper functions.

some examples:

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

Even from the limited examples, you can see how powerful an "extra argument" can be for creating re-usable code. Instead of making a different callback function for each situation, you can usually adapt a low-level helper. The goal is to have your custom logic bundling a verb and two nouns, with minimal boilerplate.

Admittedly, arrow functions have eliminated a lot of the "code golf" advantages of generic pure functions, but the semantic and consistency advantages remain.

I always add "use strict" to helpers to provide native [].map() compatibility when passing primitives. Otherwise, they are coerced into objects, which usually still works, but it's faster and safer to be type-specific.

温柔一刀 2024-10-23 12:25:07

_.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 只是一个函数,可能与 x完全无关>basket 或任何其他对象,例如

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 会绑定到上下文对象< /a>

Simple use of _.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();

Output:

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

Instead of calling addItem multiple times you could use underscore this way:

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

which is identical to calling addItem three times sequentially with these items. Basically it iterates your array and for each item calls your anonymous callback function that calls x.addItem(item). The anonymous callback function is similar to addItem member function (e.g. it takes an item) and is kind of pointless. So, instead of going through anonymous function it's better that _.each avoids this indirection and calls addItem directly:

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

but this won't work, as inside basket's addItem member function this won't refer to your x basket that you created. That's why you have an option to pass your basket x to be used as [context]:

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

Full example that uses _.each and 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>

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

how this feature can be useful with some different underscore methods?

In general, if some underscorejs method takes a callback function and if you want that callback be called on some member function of some object (e.g. a function that uses this) then you may bind that function to some object or pass that object as the [context] parameter and that's the primary intention. And at the top of underscorejs documentation, that's exactly what they state: The iteratee is bound to the context object, if one is passed

比忠 2024-10-23 12:25:07

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

我将借助 underscore 源代码 中相关方法的源代码来解释这

一点_.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() 用于通过设置 this 上下文来调用方法它的。因此,当在 func 中使用 this 时,它将引用 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 视为 forEach 在 JavaScript 中。

As explained in other answers, context is the this context to be used inside callback passed to each.

I'll explain this with the help of source code of relevant methods from underscore source code

The definition of _.each or _.forEach is as follows:

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

Second statement is important to note here

iteratee = optimizeCb(iteratee, context);

Here, context is passed to another method optimizeCb and the returned function from it is then assigned to iteratee which is called later.

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

As can be seen from the above method definition of optimizeCb, if context is not passed then func is returned as it is. If context is passed, callback function is called as

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

func is called with call() which is used to invoke a method by setting this context of it. So, when this is used inside func, it'll refer to 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文