ホームページ > 記事 > ウェブフロントエンド > JavaScript における配列操作の基本
for_in を使用して配列を走査する場合には 3 つの問題があります:
走査順序が固定されていない
JavaScript エンジンはオブジェクトの走査順序を保証しません。通常のオブジェクトとして配列を走査する場合、走査のインデックス順序も保証されません。
は、オブジェクトプロトタイプチェーン上の値を走査します。
配列のプロトタイプ オブジェクト (ポリフィルなど) を <code><span style="font-size: 14px;">enumerable: false</span>
,for_in 会把这些东西遍历出来。
运行效率低下。
尽管理论上 JavaScript 使用对象的形式储存数组,JavaScript 引擎还是会对数组这一非常常用的内置对象特别优化。 https://jsperf.com/for-in-vs-...
可以看到使用 for_in 遍历数组要比使用下标遍历数组慢 50 倍以上
PS:你可能是想找 for_of
有人使用 JSON 中深拷贝对象或数组。这虽然在多数情况是个简单方便的手段,但也可能引发未知 bug,因为:
会使某些特定值转换为 <span style="font-size: 14px;">null</span>
NaN, undefined, Infinity 对于 JSON 中不支持的这些值,会在序列化 JSON 时被转换为 null,反序列化回来后自然也就是 null
会丢失值为 undefined 的键值对
JSON 序列化时会忽略值为 undefined 的 key,反序列化回来后自然也就丢失了
会将 Date 对象转换为字符串
JSON 不支持对象类型,对于 JS 中 Date 对象的处理方式为转换为 ISO8601 格式的字符串。然而反序列化并不会把时间格式的字符串转化为 Date 对象
运行效率低下。
作为原生函数,<span style="font-size: 14px;">JSON.stringify</span>
和 <span style="font-size: 14px;">JSON.parse</span>
自身操作 JSON 字符串的速度是很快的。然而为了深拷贝数组把对象序列化成 JSON 再反序列化回来完全没有必要。
我花了一些时间写了一个简单的深拷贝数组或对象的函数,测试发现运行速度差不多是使用 JSON 中转的 6 倍左右,顺便还支持了 TypedArray、RegExp 的对象的复制
https://jsperf.com/deep-clone...
<span style="font-size: 14px;">Array.prototype.find</span>
是 ES2015 中新增的数组查找函数,与 <span style="font-size: 14px;">Array.prototype.some</span>
有相似之处,但不能替代后者。
<span style="font-size: 14px;">Array.prototype.find</span>
返回第一个符合条件的值,直接拿这个值做 <span style="font-size: 14px;">if</span>
判断是否存在,如果这个符合条件的值恰好是 0 怎么办?
<span style="font-size: 14px;">arr.find</span>
是找到数组中的值后对其进一步处理,一般用于对象数组的情况;<span style="font-size: 14px;">arr.some</span>
enumerable: false に設定せずに変更すると、for_in はこれらのことを反復処理します。
🎜null🎜
🎜🎜🎜🎜🎜 に変換します。 NaN、未定義、Infinity。JSON でサポートされていないこれらの値は、JSON をシリアル化すると、当然 null🎜🎜🎜🎜🎜🎜 になり、キーと値のペアになります。値未定義は失われます🎜🎜🎜🎜🎜JSON はシリアル化中に値が未定義のキーを無視し、逆シリアル化後に失われます🎜🎜🎜🎜🎜🎜 は Date オブジェクトを文字列 🎜🎜🎜🎜🎜🎜 に変換します。 JSONはサポートされていないオブジェクト型です。JSのDateオブジェクトの処理方法はISO8601形式の文字列に変換します。ただし、逆シリアル化では時刻形式文字列が Date オブジェクトに変換されないため、非効率的です。 🎜🎜🎜🎜🎜🎜ネイティブ関数として、🎜🎜JSON.stringify🎜
🎜 と 🎜🎜JSON.parse🎜
🎜 は、それ自体で JSON 文字列を非常に高速に操作します。ただし、配列をディープ コピーするために、オブジェクトを JSON にシリアル化し、逆シリアル化して戻す必要はまったくありません。 🎜🎜🎜🎜配列またはオブジェクトの単純なディープコピー関数を作成するのに時間を費やしましたが、テストの結果、実行速度がJSON転送を使用する場合のほぼ6倍であることがわかりました。ちなみに、TypedArrayとRegExp オブジェクト🎜🎜🎜 🎜https://jsperf.com/deep-clone...🎜🎜🎜🎜 arr.some の代わりに arr.find を使用しないでください🎜🎜🎜🎜Array.prototype.find🎜🎜 は ES2015 の新機能です。 追加された配列検索関数は 🎜<code>🎜Array.prototype.some🎜
🎜 に似ていますが、後者を置き換えることはできません。 🎜🎜🎜🎜Array.prototype.find🎜
🎜 最初の修飾された値を返します。この値を直接使用して 🎜🎜if🎜
🎜 を実行し、それが存在するかどうかを判断します。修飾された値がちょうど 0 の場合はどうすればよいですか? 🎜🎜🎜🎜arr.find🎜
🎜は、配列内の値を見つけた後、その値をさらに処理するためのもので、通常、🎜🎜arr.some🎜の場合に使用されます。
🎜 は、この 2 つが混在できないことを確認するためのものです。 🎜🎜 は、JavaScript 初心者がよく犯す間違いでもあり、<code><span style="font-size: 14px;">Array.prototype.map</span>
和 <span style="font-size: 14px;">Array.prototype.forEach</span>
的实际含义。
<span style="font-size: 14px;">map</span>
中文叫做 <span style="font-size: 14px;">映射</span>
,它通过将某个序列依次执行某个函数导出另一个新的序列。这个函数通常是不含副作用的,更不会修改原始的数组(所谓纯函数)。
<span style="font-size: 14px;">forEach</span>
就没有那么多说法,它就是简单的把数组中所有项都用某个函数处理一遍。由于 <span style="font-size: 14px;">forEach</span>
没有返回值(返回 undefined),所以它的回调函数通常是包含副作用的,否则这个 <span style="font-size: 14px;">forEach</span>
写了毫无意义。
确实 <span style="font-size: 14px;">map</span>
比 <span style="font-size: 14px;">forEach</span>
更加强大,但是 <span style="font-size: 14px;">map</span>
会创建一个新的数组,占用内存。如果你不用 <span style="font-size: 14px;">map</span>
的返回值,那你就应当使用 <span style="font-size: 14px;">forEach</span>
ES6 以前,遍历数组主要就是两种方法:手写循环用下标迭代,使用 <span style="font-size: 14px;">Array.prototype.forEach</span>
。前者万能,效率最高,可就是写起来比较繁琐——它不能直接获取到数组中的值。
笔者个人是喜欢后者的:可以直接获取到迭代的下标和值,而且函数式风格(注意 FP 注重的是不可变数据结构,forEach 天生为副作用存在,所以只有 FP 的形而没有神)写起来爽快无比。但是!不知各位同学注意过没有:forEach 一旦开始就停不下来了。。。
forEach 接受一个回调函数,你可以提前 <span style="font-size: 14px;">return</span>
,相当于手写循环中的 <span style="font-size: 14px;">continue</span>
。但是你不能 <span style="font-size: 14px;">break</span>
——因为回调函数中没有循环让你去 <span style="font-size: 14px;">break</span>
:
<span style="font-size: 14px;">[1, 2, 3, 4, 5].forEach(x => {<br> console.log(x);<br> if (x === 3) {<br> break; // SyntaxError: Illegal break statement<br> }<br>});<br></span>
解决方案还是有的。其他函数式编程语言例如 <span style="font-size: 14px;">scala</span>
就遇到了类似问题,它提供了一个函数
break,作用是抛出一个异常。
我们可以仿照这样的做法,来实现 <span style="font-size: 14px;">arr.forEach</span>
的 <span style="font-size: 14px;">break</span>
:
<span style="font-size: 14px;">try {<br> [1, 2, 3, 4, 5].forEach(x => {<br> console.log(x);<br> if (x === 3) {<br> throw 'break';<br> }<br> });<br>} catch (e) {<br> if (e !== 'break') throw e; // 不要勿吞异常。。。<br>}<br></span>
恶心的一B对不对。还有其他方法,比如用 <span style="font-size: 14px;">Array.prototype.some</span>
代替 <span style="font-size: 14px;">Array.prototype.forEach</span>
。
考虑 Array.prototype.some 的特性,当 <span style="font-size: 14px;">some</span>
找到一个符合条件的值(回调函数返回 <span style="font-size: 14px;">true</span>
)时会立即终止循环,利用这样的特性可以模拟 <span style="font-size: 14px;">break</span>
Array.prototype.map
🎜Array.prototype.forEach🎜
🎜 の実際の意味。 🎜🎜🎜🎜map🎜
🎜は中国語では🎜🎜map🎜
🎜と呼ばれ、シーケンスに対して関数を順番に実行することで別の新しいシーケンスを導出します。通常、この関数には副作用はなく、元の配列は変更されません (いわゆる純粋関数)。 🎜🎜🎜🎜forEach🎜
🎜 説明はあまりありませんが、配列内のすべての項目を特定の関数で処理するだけです。 🎜🎜forEach🎜
🎜 には戻り値がない (未定義を返す) ため、通常、そのコールバック関数には副作用が含まれます。それ以外の場合、この 🎜🎜forEach🎜
🎜 には意味がありません。 🎜🎜🎜🎜は確かに🎜🎜map🎜
🎜よりも🎜🎜forEach🎜
🎜より強力ですが、🎜🎜map🎜
🎜は新しい配列がメモリを占有しています。 🎜🎜map🎜
🎜の戻り値を使用しない場合は、🎜🎜forEach🎜
を使用する必要があります🎜🎜🎜補足: forEachとbreak🎜🎜🎜🎜 ES6 より前では、配列を走査するための主な方法が 2 つあります。1 つは添字を使用して反復する手書きのループ、もう 1 つは 🎜🎜Array.prototype.forEach🎜
🎜 を使用する方法です。前者は多用途で最も効率的ですが、配列内の値を直接取得できないため、記述がより面倒になります。 🎜🎜🎜🎜著者は個人的に後者が好きです。反復の添字と値、および関数スタイルを直接取得できます (FP は不変のデータ構造に焦点を当てており、forEach は本質的に副作用であるため、 FP No Godの形式)を書くのはとても新鮮でした。しかし!学生の皆さんは気づいたでしょうか。forEach は一度始めると止まらないのです。 。 。 🎜🎜🎜🎜forEach は、事前に 🎜🎜return🎜
🎜 を実行できるコールバック関数を受け入れます。これは、手書きループの 🎜🎜 continue🎜
🎜 に相当します。しかし、🎜🎜break🎜
🎜はできません - 🎜🎜break🎜
🎜を実行するためのループがコールバック関数にないためです: 🎜🎜<span style="font-size: 14px;">[1, 2, 3, 4, 5].some(x => {<br> console.log(x);<br> if (x === 3) {<br> return true; // break<br> }<br> // return undefined; 相当于 false<br>});<br></span>🎜🎜まだ解決策です。 🎜
🎜scala🎜
🎜 などの他の関数型プログラミング言語でも、例外をスローする関数🎜arr.forEach🎜
🎜/🎜🎜break🎜
🎜: 🎜🎜<span style="max-width:90%">for (const x of [1, 2, 3, 4, 5]) {<br> console.log(x);<br> if (x === 3) {<br> break;<br> }<br>}<br></span>🎜🎜気持ち悪いペア。 🎜
🎜Array.prototype.forEach🎜
🎜の代わりに🎜🎜Array.prototype.some🎜
🎜を使用するなど、他の方法もあります。 🎜🎜🎜🎜🎜🎜some🎜
🎜が修飾された値を見つけたときのArray.prototype.someの特性を考えてみましょう(コールバック関数は🎜🎜true🎜
🎜を返します)。ループは即座に終了します。この機能は 🎜🎜break🎜
🎜 をシミュレートするために使用できます: 🎜🎜<span style="font-size: 14px;">[1, 2, 3, 4, 5].some(x => {<br> console.log(x);<br> if (x === 3) {<br> return true; // break<br> }<br> // return undefined; 相当于 false<br>});<br></span>
<span style="font-size: 14px;">some</span>
的返回值被忽略掉了,它已经脱离了判断数组中是否有元素符合给出的条件这一原始的含义。
在 ES6 前,笔者主要使用该法(其实因为 Babel 代码膨胀的缘故,现在也偶尔使用),ES6 不一样了,我们有了 for...of。<span style="font-size: 14px;">for...of</span>
是真正的循环,可以 <span style="font-size: 14px;">break</span>
:
<span style="font-size: 14px;">for (const x of [1, 2, 3, 4, 5]) {<br> console.log(x);<br> if (x === 3) {<br> break;<br> }<br>}<br></span>
但是有个问题,<span style="font-size: 14px;">for...of</span>
似乎拿不到循环的下标。其实 JavaScript 语言制定者想到了这个问题,可以如下解决:
<span style="font-size: 14px;">for (const [index, value] of [1, 2, 3, 4, 5].entries()) {<br> console.log(`arr[${index}] = ${value}`);<br>}<br></span>
Array.prototype.entries
<span style="font-size: 14px;">for...of</span>
和 <span style="font-size: 14px;">forEach</span>
的性能测试:https://jsperf.com/array-fore... Chrome 中 <span style="font-size: 14px;">for...of</span>
要快一些哦
以上がJavaScript における配列操作の基本の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。