web-dev-qa-db-ja.com

_.each(list、iterator、[context])のコンテキストとは何ですか?

Underscore.jsは初めてです。 _.each()[context]の目的は何ですか?どのように使用する必要がありますか?

161
ram

コンテキストパラメータは、イテレータ関数の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で表されます。

コンテキストを設定しない場合、thiswindowオブジェクトを参照します。

219
user113716

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

このコンテキストを使用すると、呼び出し時に引数を指定できるため、事前に構築された汎用ヘルパー関数を簡単にカスタマイズできます。

いくつかの例:

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

限られた例からでさえ、再利用可能なコードを作成するために「追加の引数」がどれほど強力であるかを見ることができます。状況ごとに異なるコールバック関数を作成する代わりに、通常、低レベルのヘルパーを適応させることができます。目標は、動詞と2つの名詞を最小限の定型句で束ねたカスタムロジックを持つことです。

確かに、矢印関数は一般的な純粋関数の「コードゴルフ」の多くの利点を排除しましたが、セマンティックおよび一貫性の利点は残っています。

プリミティブを渡すときにネイティブの[].map()互換性を提供するために、常に"use strict"をヘルパーに追加します。それ以外の場合は、通常は引き続き機能しますが、型固有である方が高速で安全です。

6
dandavis

他の回答で説明したように、contextは、thisに渡されるコールバック内で使用される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;
};

2番目のステートメントはここで注意することが重要です

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

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>

JavaScriptでは、contextforEach の最後のオプションパラメーターと見なすことができます。

4
Tushar

_.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を3回連続して呼び出すことと同じです。基本的に、配列を反復処理し、各項目について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とはまったく関係がない可能性があります または他のオブジェクトなど

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]として渡すことができますパラメータであり、それが主な目的です。そして、アンダースコアjsドキュメントの一番上に、それはまさに彼らが述べるものです: 渡された場合、反復子はコンテキストオブジェクトにバインドされます

3
Pavel