diff options
| author | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:40:17 -0500 |
|---|---|---|
| committer | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:40:17 -0500 |
| commit | 33058f2b292b3a581333bdfb21b8f671898c5060 (patch) | |
| tree | 51c3e392513ec574331b2d3f85c394445ea803c6 /files/zh-cn/web/javascript/reference/global_objects/array/foreach | |
| parent | 8b66d724f7caf0157093fb09cfec8fbd0c6ad50a (diff) | |
| download | translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.tar.gz translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.tar.bz2 translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.zip | |
initial commit
Diffstat (limited to 'files/zh-cn/web/javascript/reference/global_objects/array/foreach')
| -rw-r--r-- | files/zh-cn/web/javascript/reference/global_objects/array/foreach/index.html | 384 |
1 files changed, 384 insertions, 0 deletions
diff --git a/files/zh-cn/web/javascript/reference/global_objects/array/foreach/index.html b/files/zh-cn/web/javascript/reference/global_objects/array/foreach/index.html new file mode 100644 index 0000000000..020a7dc3cd --- /dev/null +++ b/files/zh-cn/web/javascript/reference/global_objects/array/foreach/index.html @@ -0,0 +1,384 @@ +--- +title: Array.prototype.forEach() +slug: Web/JavaScript/Reference/Global_Objects/Array/forEach +tags: + - Array + - ECMAScript5 + - JavaScript + - 参考 + - 数组 + - 方法 +translation_of: Web/JavaScript/Reference/Global_Objects/Array/forEach +--- +<div>{{JSRef}}</div> + +<p><code><strong>forEach()</strong></code> 方法对数组的每个元素执行一次给定的函数。</p> + + + +<p>{{EmbedInteractiveExample("pages/js/array-foreach.html")}}</p> + +<h2 id="语法">语法</h2> + +<pre class="syntaxbox"><var>arr</var>.forEach(<var>callback(currentValue [, index [, array]])</var>[, <var>thisArg</var>])</pre> + +<h3 id="参数">参数</h3> + +<dl> + <dt><code><var>callback</var></code></dt> + <dd>为数组中每个元素执行的函数,该函数接收一至三个参数:</dd> + <dd> + <dl> + <dt><code><var>currentValue</var></code></dt> + <dd>数组中正在处理的当前元素。</dd> + <dt><code><var>index</var></code> {{optional_inline}}</dt> + <dd>数组中正在处理的当前元素的索引。</dd> + <dt><code><var>array</var></code> {{optional_inline}}</dt> + <dd><code>forEach()</code> 方法正在操作的数组。</dd> + </dl> + </dd> + <dt><code><var>thisArg</var></code> {{optional_inline}}</dt> + <dd>可选参数。当执行回调函数 <code><var>callback</var></code> 时,用作 <code>this</code> 的值。</dd> +</dl> + +<h3 id="返回值">返回值</h3> + +<p>{{jsxref("undefined")}}。</p> + +<h2 id="描述">描述</h2> + +<p><code>forEach()</code> 方法按升序为数组中含有效值的每一项执行一次 <code><var>callback</var></code> 函数,那些已删除或者未初始化的项将被跳过(例如在稀疏数组上)。</p> + +<p>可依次向 <code><var>callback</var></code> 函数传入三个参数:</p> + +<ol> + <li>数组当前项的值</li> + <li>数组当前项的索引</li> + <li>数组对象本身</li> +</ol> + +<p>如果 <code><var>thisArg</var></code> 参数有值,则每次 <code><var>callback</var></code> 函数被调用时,<code>this</code> 都会指向 <code><var>thisArg</var></code> 参数。如果省略了 <code><var>thisArg</var></code> 参数,或者其值为 <code>null</code> 或 <code>undefined</code>,<code>this</code> 则指向全局对象。按照<a href="/zh-CN/docs/Web/JavaScript/Reference/Operators/this">函数观察到 <code>this</code> 的常用规则</a>,<code><var>callback</var></code> 函数最终可观察到 <code>this</code> 值。</p> + +<p><code>forEach()</code> 遍历的范围在第一次调用 <code><var>callback</var></code> 前就会确定。调用 <code>forEach</code> 后添加到数组中的项不会被 <code><var>callback</var></code> 访问到。如果已经存在的值被改变,则传递给 <code><var>callback</var></code> 的值是 <code>forEach()</code> 遍历到他们那一刻的值。已删除的项不会被遍历到。如果已访问的元素在迭代时被删除了(例如使用 {{jsxref("Array.prototype.shift()", "shift()")}}),之后的元素将被跳过——<a href="#如果数组在迭代时被修改了,则其他元素会被跳过。">参见下面的示例</a>。</p> + +<p><code>forEach()</code> 为每个数组元素执行一次 <code><var>callback</var></code> 函数;与 {{jsxref("Array.prototype.map()", "map()")}} 或者 {{jsxref("Array.prototype.reduce()", "reduce()")}} 不同的是,它总是返回 {{jsxref("undefined")}} 值,并且不可链式调用。其典型用例是在一个调用链的最后执行副作用(side effects,函数式编程上,指函数进行 返回结果值 以外的操作)。</p> + +<p><code>forEach()</code> 被调用时,不会改变原数组,也就是调用它的数组(尽管 <code><var>callback</var></code> 函数在被调用时可能会改变原数组)。(译注:此处说法可能不够明确,具体可参考EMCA语言规范:'<code>forEach</code> does not directly mutate the object on which it is called but the object may be mutated by the calls to <code><var>callbackfn</var></code>.',即 <code>forEach</code> 不会直接改变调用它的对象,但是那个对象可能会被 <code><var>callback</var></code> 函数改变。)</p> + +<div class="note"> +<p><strong>注意:</strong> 除了抛出异常以外,没有办法中止或跳出 <code>forEach()</code> 循环。如果你需要中止或跳出循环,<code>forEach()</code> 方法不是应当使用的工具。</p> + +<p>若你需要提前终止循环,你可以使用:</p> + +<ul> + <li>一个简单的 <a href="/zh-CN/docs/Web/JavaScript/Reference/Statements/for">for</a> 循环</li> + <li><a href="/zh-CN/docs/Web/JavaScript/Reference/Statements/for...of">for...of</a> / <a href="/zh-CN/docs/Web/JavaScript/Reference/Statements/for...in">for...in</a> 循环</li> + <li>{{jsxref("Array.prototype.every()")}}</li> + <li>{{jsxref("Array.prototype.some()")}}</li> + <li>{{jsxref("Array.prototype.find()")}}</li> + <li>{{jsxref("Array.prototype.findIndex()")}}</li> +</ul> + +<p>这些数组方法则可以对数组元素判断,以便确定是否需要继续遍历:</p> + +<ul> + <li>{{jsxref("Array.prototype.every()", "every()")}}</li> + <li>{{jsxref("Array.prototype.some()", "some()")}}</li> + <li>{{jsxref("Array.prototype.find()", "find()")}}</li> + <li>{{jsxref("Array.prototype.findIndex()", "findIndex()")}}</li> +</ul> + +<p>译者注:只要条件允许,也可以使用 {{jsxref("Array.prototype.filter()", "filter()")}} 提前过滤出需要遍历的部分,再用 <code>forEach()</code> 处理。</p> +</div> + +<h2 id="示例">示例</h2> + +<h3 id="不对未初始化的值进行任何操作(稀疏数组)">不对未初始化的值进行任何操作(稀疏数组)</h3> + +<p>如你所见,<code>3</code> 和 <code>7</code> 之间空缺的数组单元未被 <code>forEach()</code> 调用 <code><var>callback</var></code> 函数,或进行任何其他操作。</p> + +<pre class="brush: js">const arraySparse = [1,3,,7]; +let numCallbackRuns = 0; + +arraySparse.forEach(function(element){ + console.log(element); + numCallbackRuns++; +}); + +console.log("numCallbackRuns: ", numCallbackRuns); + +// 1 +// 3 +// 7 +// numCallbackRuns: 3</pre> + +<h3 id="将_for_循环转换为_forEach">将 for 循环转换为 forEach</h3> + +<pre class="brush:js">const items = ['item1', 'item2', 'item3']; +const copy = []; + +// before +for (let i=0; i<items.length; i++) { + copy.push(items[i]); +} + +// after +items.forEach(function(item){ + copy.push(item); +}); +</pre> + +<h3 id="打印出数组的内容">打印出数组的内容</h3> + +<div class="blockIndicator note"> +<p><strong>注意:</strong>为了在控制台中显示数组的内容,你可以使用 <code><a href="/zh-CN/docs/Web/API/Console/table">console.table()</a></code> 来展示经过格式化的数组。下面的例子则是另一种使用 <code>forEach()</code> 的格式化的方法。</p> +</div> + +<p>下面的代码会为每一个数组元素输出一行记录:</p> + +<pre class="brush:js">function logArrayElements(element, index, array) { + console.log('a[' + index + '] = ' + element); +} + +// 注意索引 2 被跳过了,因为在数组的这个位置没有项 +[2, 5, , 9].forEach(logArrayElements); +// logs: +// a[0] = 2 +// a[1] = 5 +// a[3] = 9 +</pre> + +<h3 id="使用_thisArg">使用 <code><var>thisArg</var></code></h3> + +<p>举个勉强的例子,按照每个数组中的元素值,更新一个对象的属性:</p> + +<pre class="brush:js">function Counter() { + this.sum = 0; + this.count = 0; +} +Counter.prototype.add = function(array) { + array.forEach(function(entry) { + this.sum += entry; + ++this.count; + }, this); + // ^---- Note +}; + +const obj = new Counter(); +obj.add([2, 5, 9]); +obj.count; +// 3 === (1 + 1 + 1) +obj.sum; +// 16 === (2 + 5 + 9) +</pre> + +<p>因为 <code><var>thisArg</var></code> 参数(<code>this</code>)传给了 <code>forEach()</code>,每次调用时,它都被传给 <code><var>callback</var></code> 函数,作为它的 <code>this</code> 值。</p> + +<div> +<div class="note"><strong>注意:</strong>如果使用<a href="/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions">箭头函数表达式</a>来传入函数参数, <code><var>thisArg</var></code> 参数会被忽略,因为箭头函数在词法上绑定了 {{jsxref("Operators/this", "this")}} 值。</div> +</div> + +<h3 id="对象复制器函数">对象复制器函数</h3> + +<p>下面的代码会创建一个给定对象的副本。 创建对象的副本有不同的方法,以下是只是一种方法,并解释了 <code>Array.prototype.forEach()</code> 是如何使用 ECMAScript 5 <code>Object.*</code> 元属性(meta property)函数工作的。</p> + +<pre class="brush: js">function copy(obj) { + const copy = Object.create(Object.getPrototypeOf(obj)); + const propNames = Object.getOwnPropertyNames(obj); + + propNames.forEach(function(name) { + const desc = Object.getOwnPropertyDescriptor(obj, name); + Object.defineProperty(copy, name, desc); + }); + + return copy; +} + +const obj1 = { a: 1, b: 2 }; +const obj2 = copy(obj1); // 现在 obj2 看起来和 obj1 一模一样了 +</pre> + +<h3 id="如果数组在迭代时被修改了,则其他元素会被跳过。">如果数组在迭代时被修改了,则其他元素会被跳过。</h3> + +<p>下面的例子会输出 "one", "two", "four"。当到达包含值 "two" 的项时,整个数组的第一个项被移除了,这导致所有剩下的项上移一个位置。因为元素 "four" 正位于在数组更前的位置,所以 "three" 会被跳过。 <code>forEach()</code> 不会在迭代之前创建数组的副本。</p> + +<pre class="brush:js">var words = ['one', 'two', 'three', 'four']; +words.forEach(function(word) { + console.log(word); + if (word === 'two') { + words.shift(); + } +}); +// one +// two +// four +</pre> + +<h3 id="扁平化数组">扁平化数组</h3> + +<p>下面的示例仅用于学习目的。如果你想使用内置方法来扁平化数组,你可以考虑使用 {{jsxref("Array.prototype.flat()")}}(预计将成为 ES2019 的一部分,并且已在主要浏览器中实现)或参考其 polyfill。</p> + +<pre class="brush: js">/** + * Flattens passed array in one dimensional array + * + * @params {array} arr + * @returns {array} + */ +function flatten(arr) { + const result = []; + + arr.forEach((i) => { + if (Array.isArray(i)) + result.push(...flatten(i)); + else + result.push(i); + }) + + return result; +} + +// Usage +const problem = [1, 2, 3, [4, 5, [6, 7], 8, 9]]; + +flatten(problem); // [1, 2, 3, 4, 5, 6, 7, 8, 9] +</pre> + +<h2 id="针对_promise_或_async_函数的使用备注">针对 promise 或 async 函数的使用备注</h2> + +<p>如果使用 promise 或 async 函数作为 <code>forEach()</code> 等类似方法的 <code><var>callback</var></code> 参数,最好对造成的执行顺序影响多加考虑,否则容易出现错误。</p> + +<pre class="brush: js">let ratings = [5, 4, 5]; + +let sum = 0; + +let sumFunction = async function (a, b) { + return a + b; +} + +ratings.forEach(async function(rating) { + sum = await sumFunction(sum, rating); +}) + +console.log(sum); +// Expected output: 14 +// Actual output: 0 +</pre> + +<h2 id="Polyfill">Polyfill</h2> + +<p><code>forEach()</code> 是在第五版本里被添加到 ECMA-262 标准的;这样它可能在标准的其他实现中不存在,你可以在你调用 <code>forEach()</code> 之前插入下面的代码,在本地不支持的情况下使用 <code>forEach()</code>。该算法是 ECMA-262 第5版中指定的算法。它假定 {{jsxref("Object")}} 和 {{jsxref("TypeError")}} 拥有它们的初始值,且 <code>callback.call</code> 等价于 {{jsxref("Function.prototype.call()")}}。</p> + +<pre class="brush: js">// Production steps of ECMA-262, Edition 5, 15.4.4.18 +// Reference: http://es5.github.io/#x15.4.4.18 +if (!Array.prototype.forEach) { + + Array.prototype.forEach = function(callback, thisArg) { + + var T, k; + + if (this == null) { + throw new TypeError(' this is null or not defined'); + } + + // 1. Let O be the result of calling toObject() passing the + // |this| value as the argument. + var O = Object(this); + + // 2. Let lenValue be the result of calling the Get() internal + // method of O with the argument "length". + // 3. Let len be toUint32(lenValue). + var len = O.length >>> 0; + + // 4. If isCallable(callback) is false, throw a TypeError exception. + // See: http://es5.github.com/#x9.11 + if (typeof callback !== "function") { + throw new TypeError(callback + ' is not a function'); + } + + // 5. If thisArg was supplied, let T be thisArg; else let + // T be undefined. + if (arguments.length > 1) { + T = thisArg; + } + + // 6. Let k be 0 + k = 0; + + // 7. Repeat, while k < len + while (k < len) { + + var kValue; + + // a. Let Pk be ToString(k). + // This is implicit for LHS operands of the in operator + // b. Let kPresent be the result of calling the HasProperty + // internal method of O with argument Pk. + // This step can be combined with c + // c. If kPresent is true, then + if (k in O) { + + // i. Let kValue be the result of calling the Get internal + // method of O with argument Pk. + kValue = O[k]; + + // ii. Call the Call internal method of callback with T as + // the this value and argument list containing kValue, k, and O. + callback.call(T, kValue, k, O); + } + // d. Increase k by 1. + k++; + } + // 8. return undefined + }; +} +</pre> + +<h2 id="规范">规范</h2> + +<table class="standard-table"> + <thead> + <tr> + <th scope="col">规范</th> + <th scope="col">状态</th> + <th scope="col">备注</th> + </tr> + </thead> + <tbody> + <tr> + <td>{{SpecName('ESDraft', '#sec-array.prototype.foreach', 'Array.prototype.forEach')}}</td> + <td>{{Spec2('ESDraft')}}</td> + <td></td> + </tr> + <tr> + <td>{{SpecName('ES6', '#sec-array.prototype.foreach', 'Array.prototype.forEach')}}</td> + <td>{{Spec2('ES6')}}</td> + <td></td> + </tr> + <tr> + <td>{{SpecName('ES5.1', '#sec-15.4.4.18', 'Array.prototype.forEach')}}</td> + <td>{{Spec2('ES5.1')}}</td> + <td>Initial definition. Implemented in JavaScript 1.6.</td> + </tr> + </tbody> +</table> + +<h2 id="浏览器兼容性">浏览器兼容性</h2> + +<div> + + +<p>{{Compat("javascript.builtins.Array.forEach")}}</p> +</div> + +<h2 id="参见">参见</h2> + +<ul> + <li>{{jsxref("Array.prototype.find()")}}</li> + <li>{{jsxref("Array.prototype.findIndex()")}}</li> + <li>{{jsxref("Array.prototype.map()")}}</li> + <li>{{jsxref("Array.prototype.filter()")}}</li> + <li>{{jsxref("Array.prototype.every()")}}</li> + <li>{{jsxref("Array.prototype.some()")}}</li> + <li>{{jsxref("Map.prototype.forEach()")}}</li> + <li>{{jsxref("Set.prototype.forEach()")}}</li> +</ul> |
