155

コードの一部を調べていたところ、空の配列に forEach が適用されたノード リストに対して関数を呼び出す複数の要素が見つかりました。

たとえば、次のようなものがあります。

[].forEach.call( document.querySelectorAll('a'), function(el) {
   // whatever with the current node
});

しかし、私はそれがどのように機能するのか理解できません。forEach の前にある空の配列の動作とその仕組みを説明できる人はcallいますか?

4

12 に答える 12

233

[]配列です。
この配列はまったく使用されません。

配列を使用すると、 などの配列プロトタイプにアクセスできるため、ページに配置されています.forEach

これは、入力するよりも高速ですArray.prototype.forEach.call(...);

次に、forEach関数を入力とする関数です...

[1,2,3].forEach(function (num) { console.log(num); });

...そしてthis(thisは配列のようなもので、 があり、 のようにlengthその部分にアクセスできますthis[1]) の各要素に対して、次の 3 つのものを渡します。

  1. 配列内の要素
  2. 要素のインデックス (3 番目の要素は を渡します2)
  3. 配列への参照

最後に、.call関数が持つプロトタイプです (他の関数から呼び出される関数です)。
.call最初の引数を取りthis、通常の関数内をcall最初の引数として渡したものに置き換えます(undefinedまたは日常のJSでnull使用するwindowか、「厳密モード」の場合は渡したものになります)。残りの引数は元の関数に渡されます。

[1, 2, 3].forEach.call(["a", "b", "c"], function (item, i, arr) {
    console.log(i + ": " + item);
});
// 0: "a"
// 1: "b"
// 2: "c"

したがって、関数を呼び出す簡単な方法を作成し、空の配列からすべてのタグのリストにforEach変更し、順序どおりに、提供された関数を呼び出しています。this<a><a>

編集

論理的結論 / クリーンアップ

以下に、関数型プログラミングの試みを破棄し、毎回手動のインライン ループに固執することを提案する記事へのリンクがあります。

.forEach、 、 、 、 、 、 、 、 、 、 、 、 、 のいずれかにアクセスしながら、外の世界 (配列ではない) を n 回変更することだけが本当に必要な場合でも、目的を.map(transformer)果たします。.filter(predicate).reduce(combiner, initialValue)arr[i]i

では、モットーは明らかに才能があり知識豊富な人物であり、自分が何をしているのか、どこに向かっているのかを知っていると想像したいと思います (時々... ...その他回は頭から先に学習します)?

答えは実際には非常に単純で、見落としのためにボブおじさんとクロックフォード卿の両方が顔をしかめるようなものです。

それをきれいにします。

function toArray (arrLike) { // or asArray(), or array(), or *whatever*
  return [].slice.call(arrLike);
}

var checked = toArray(checkboxes).filter(isChecked);
checked.forEach(listValues);

さて、これを自分で行う必要があるかどうか疑問に思っている場合、答えはノーかもしれません...
これはまさに... ...最近の高次機能を備えたすべての(?)ライブラリによって行われています。
lodash、アンダースコア、さらには jQuery を使用している場合、それらはすべて一連の要素を取得し、アクションを n 回実行する方法を備えています。
そのようなものを使用していない場合は、必ず独自のものを作成してください。

lib.array = (arrLike, start, end) => [].slice.call(arrLike, start, end);
lib.extend = function (subject) {
  var others = lib.array(arguments, 1);
  return others.reduce(appendKeys, subject);
};

ES6(ES2015) 以降のアップデート

slice( )/ /etc ヘルパー メソッドは、配列を使用するのと同じようにリストを使用したい人々の生活を楽にするだけarray( )でなく、比較的近い ES6+ ブラウザーで操作する余裕がある人々にとっても便利です。将来、または今日の Babel の「トランスパイル」の場合、言語機能が組み込まれているため、この種のことは不要になります。

function countArgs (...allArgs) {
  return allArgs.length;
}

function logArgs (...allArgs) {
  return allArgs.forEach(arg => console.log(arg));
}

function extend (subject, ...others) { /* return ... */ }


var nodeArray = [ ...nodeList1, ...nodeList2 ];

とてもきれいで、とても便利です。
RestおよびSpread演算子を検索します。BabelJS サイトで試してみてください。技術スタックが整っている場合は、Babel とビルドステップを使用して本番環境で使用してください。


非配列から配列への変換を使用できない正当な理由はありません... ...同じ醜い行をどこにでも貼り付けるだけで、コードを台無しにしないでください。

于 2013-04-17T06:58:20.423 に答える
59

このquerySelectorAllメソッドは配列に似た を返しますがNodeList、完全な配列ではありません。したがって、forEachメソッドはありません (配列オブジェクトは を介し​​て継承されますArray.prototype)。

aNodeListは配列に似ているため、実際には配列メソッドが機能します。したがって、 を使用することで、単に を実行できたかのように、 のコンテキストでメソッドを[].forEach.call呼び出すことができます。Array.prototype.forEachNodeListyourNodeList.forEach(/*...*/)

空の配列リテラルは、展開されたバージョンへの単なるショートカットであることに注意してください。これもおそらく頻繁に目にするでしょう。

Array.prototype.forEach.call(/*...*/);
于 2013-04-17T06:52:45.440 に答える
21

他の回答はこのコードを非常によく説明しているので、提案を追加します。

これは、簡潔さと明確さのためにリファクタリングする必要があるコードの良い例です。これを行うたびに[].forEach.call()orを使用する代わりに、単純な関数を作成します。Array.prototype.forEach.call()

function forEach( list, callback ) {
    Array.prototype.forEach.call( list, callback );
}

これで、より複雑でわかりにくいコードの代わりに、この関数を呼び出すことができます。

forEach( document.querySelectorAll('a'), function( el ) {
   // whatever with the current node
});
于 2013-04-17T07:00:33.950 に答える
6

を使用してより適切に記述することができます

Array.prototype.forEach.call( document.querySelectorAll('a'), function(el) {

});

document.querySelectorAll('a')配列に似たオブジェクトを返しますが、型から継承しませんArray。そのため、返される値としてコンテキストを使用しforEachてオブジェクトからメソッドを呼び出しますArray.prototypedocument.querySelectorAll('a')

于 2013-04-17T06:55:29.580 に答える
2

空の配列のforEachプロトタイプには、関数オブジェクトであるプロパティがあります。forEach(空の配列は、すべてのオブジェクトが持つ関数への参照を取得する簡単な方法Arrayです。) 関数オブジェクトはcall、関数でもあるプロパティを持ちます。関数の関数を呼び出すcallと、指定された引数で関数が実行されます。最初の引数thisは、呼び出された関数になります。

call関数のドキュメントはこちらにあります。のドキュメントforEachこちらです。

于 2013-04-17T06:51:34.490 に答える
1

1行追加するだけです:

NodeList.prototype.forEach = HTMLCollection.prototype.forEach = Array.prototype.forEach;

そして出来上がり!

document.querySelectorAll('a').forEach(function(el) {
  // whatever with the current node
});

楽しみ :-)

警告: NodeList はグローバル クラスです。公共ライブラリを作成する場合は、この推奨事項を使用しないでください。ただし、ウェブサイトや node.js アプリで作業する場合、自己効力感を高めるには非常に便利な方法です。

于 2015-09-18T05:42:34.430 に答える
0

[]常に新しい配列を返します。これは同等ですがnew Array()、配列を返すことが保証されています。これはArray、ユーザーによって上書きされる可能性があるため[]です。したがって、これは のプロトタイプを取得する安全な方法でありArray、説明callしたように、配列のようなノードリスト (this) で関数を実行するために使用されます。

指定された this 値と個別に提供された引数を使用して関数を呼び出します。mdn

于 2013-04-17T07:00:03.120 に答える