>  기사  >  웹 프론트엔드  >  JavaScript 배열 작업의 어려움(자세한 튜토리얼)

JavaScript 배열 작업의 어려움(자세한 튜토리얼)

亚连
亚连원래의
2018-06-20 13:56:302138검색

본 글에서는 코드 분석 예시를 통해 자바스크립트 배열 연산의 어려움과 주의해야 할 사항을 함께 알아보고 참고하겠습니다.

다음 내용은 자바스크립트 배열을 배울 때의 경험과 주의할 점을 정리한 내용입니다.

배열을 탐색하기 위해 for_in을 사용하지 마세요

이것은 JavaScript 초보자들이 흔히 저지르는 오해입니다. for_in은 프로토타입 체인을 포함하여 객체의 모든 열거 가능한(열거 가능한) 키를 탐색하는 데 사용됩니다. 원래는 배열 탐색을 위해 존재하지 않습니다.

for_in을 사용하여 배열을 순회하는 데에는 세 가지 문제가 있습니다.

1. 순회 순서가 고정되어 있지 않습니다.

JavaScript 엔진은 객체의 순회 순서를 보장하지 않습니다. 배열을 일반 객체로 순회하는 경우 순회 인덱스 순서도 보장되지 않습니다.

2. 객체 프로토타입 체인의 값이 순회됩니다.

enumerable: false로 설정하지 않고 배열의 프로토타입 객체(예: 폴리필)를 변경하면 for_in이 이러한 항목을 반복합니다. enumerable: false,for_in 会把这些东西遍历出来。

3、运行效率低下。

尽管理论上 JavaScript 使用对象的形式储存数组,JavaScript 引擎还是会对数组这一非常常用的内置对象特别优化。 https://jsperf.com/for-in-vs-...
可以看到使用 for_in 遍历数组要比使用下标遍历数组慢 50 倍以上

PS:你可能是想找 for_of

不要用 JSON.parse(JSON.stringify()) 深拷贝数组

有人使用 JSON 中深拷贝对象或数组。这虽然在多数情况是个简单方便的手段,但也可能引发未知 bug,因为:会使某些特定值转换为 null

NaN, undefined, Infinity 对于 JSON 中不支持的这些值,会在序列化 JSON 时被转换为 null,反序列化回来后自然也就是 null

会丢失值为 undefined 的键值对

JSON 序列化时会忽略值为 undefined 的 key,反序列化回来后自然也就丢失了

会将 Date 对象转换为字符串

JSON 不支持对象类型,对于 JS 中 Date 对象的处理方式为转换为 ISO8601 格式的字符串。然而反序列化并不会把时间格式的字符串转化为 Date 对象

运行效率低下。

作为原生函数,JSON.stringifyJSON.parse 自身操作 JSON 字符串的速度是很快的。然而为了深拷贝数组把对象序列化成 JSON 再反序列化回来完全没有必要。

我花了一些时间写了一个简单的深拷贝数组或对象的函数,测试发现运行速度差不多是使用 JSON 中转的 6 倍左右,顺便还支持了 TypedArray、RegExp 的对象的复制

https://jsperf.com/deep-clone...

不要用 arr.find 代替 arr.some

Array.prototype.find 是 ES2015 中新增的数组查找函数,与 Array.prototype.some 有相似之处,但不能替代后者。

Array.prototype.find 返回第一个符合条件的值,直接拿这个值做 if 判断是否存在,如果这个符合条件的值恰好是 0 怎么办?

arr.find 是找到数组中的值后对其进一步处理,一般用于对象数组的情况;arr.some 才是检查存在性;两者不可混用。

不要用 arr.map 代替 arr.forEach

也是一个 JavaScript 初学者常常犯的错误,他们往往并没有分清 Array.prototype.mapArray.prototype.forEach 的实际含义。

map 中文叫做 映射,它通过将某个序列依次执行某个函数导出另一个新的序列。这个函数通常是不含副作用的,更不会修改原始的数组(所谓纯函数)。

forEach 就没有那么多说法,它就是简单的把数组中所有项都用某个函数处理一遍。由于 forEach 没有返回值(返回 undefined),所以它的回调函数通常是包含副作用的,否则这个 forEach 写了毫无意义。

确实 mapforEach 更加强大,但是 map 会创建一个新的数组,占用内存。如果你不用 map 的返回值,那你就应当使用 forEach

补:心得补充

ES6 以前,遍历数组主要就是两种方法:手写循环用下标迭代,使用 Array.prototype.forEach

3. 낮은 운영 효율성.

🎜🎜이론적으로 JavaScript는 객체 형식을 사용하여 배열을 저장하지만 JavaScript 엔진은 특히 매우 일반적으로 사용되는 내장 객체인 배열에 최적화되어 있습니다. https://jsperf.com/for-in-vs-...
for_in을 사용하여 배열을 순회하는 것이 첨자를 사용하여 배열을 순회하는 것보다 50배 이상 느리다는 것을 알 수 있습니다🎜🎜PS: for_of🎜를 찾으세요 🎜JSON.parse(JSON.stringify())를 사용하여 배열을 딥 카피하지 마세요🎜🎜어떤 사람들은 JSON을 사용하여 객체를 딥 카피하거나 배열. 이는 대부분의 경우 간단하고 편리한 방법이지만 다음과 같은 이유로 알 수 없는 버그가 발생할 수도 있습니다. 일부 특정 값은 지원되지 않는 값에 대해 null🎜🎜NaN, 정의되지 않음, Infinity로 변환됩니다. JSON에서는 JSON을 직렬화할 때 값이 null로 변환됩니다. 역직렬화 후에는 자연스럽게 null이 됩니다🎜🎜정의되지 않은 값이 있는 키-값 쌍은 손실됩니다.🎜🎜정의되지 않은 값이 있는 키는 JSON 직렬화 중에 무시되고 deserialization 당연히 돌아오면 없어집니다🎜🎜Date 객체는 문자열로 변환됩니다🎜🎜JSON은 객체 유형을 지원하지 않습니다. JS에서 Date 객체를 처리하는 방법은 ISO8601 형식의 문자열로 변환하는 것입니다. 그러나 역직렬화는 시간 형식 문자열을 Date 개체로 변환하지 않으므로 비효율적입니다. 🎜🎜기본 함수인 JSON.stringifyJSON.parse는 JSON 문자열을 매우 빠르게 작동합니다. 그러나 배열을 심층 복사하기 위해 개체를 JSON으로 직렬화하고 다시 역직렬화하는 것은 전혀 필요하지 않습니다. 🎜🎜배열이나 개체 전체 복사를 위한 간단한 함수를 작성하는 데 시간을 투자한 결과 실행 속도가 JSON 전송을 사용할 때보다 거의 6배나 빠른 것으로 나타났습니다. 그런데 TypedArray 및 RegExp 개체 복사도 지원합니다. https:/ /jsperf.com/deep-clone...🎜🎜arr.some 대신 arr.find를 사용하지 마세요🎜🎜Array.prototype.find는 ES2015의 새로운 배열 검색 기능입니다. Array.prototype.some 과 동일하지만 유사하지만 후자를 대체하지는 않습니다. 🎜🎜Array.prototype.find는 첫 번째 정규화된 값을 반환합니다. 정규화된 값이 0인지 확인하려면 이 값을 직접 사용하여 if를 수행하세요. 관리하다? 🎜🎜arr.find는 배열에서 값을 찾은 후 추가로 처리하는 것입니다. 일반적으로 객체 배열의 경우에 사용됩니다. 존재; 둘 다 혼합되지 않습니다. 🎜🎜arr.forEach 대신 arr.map을 사용하지 마세요🎜🎜이 또한 JavaScript 초보자가 자주 저지르는 실수입니다. 그들은 종종 Array.prototype.map를 구분하지 않습니다. Array.prototype.forEach 실제 의미. 🎜🎜<code>map은 중국어로 map이라고 합니다. 특정 시퀀스에 대해 특정 기능을 순차적으로 실행하여 또 다른 새로운 시퀀스를 파생합니다. 이 함수는 일반적으로 부작용이 없으며 원래 배열(소위 순수 함수)을 수정하지 않습니다. 🎜🎜forEach에는 설명이 많지 않습니다. 단순히 특정 기능으로 배열의 모든 항목을 처리합니다. forEach에는 반환 값이 없으므로(정의되지 않음 반환) 콜백 함수에는 일반적으로 부작용이 포함됩니다. 그렇지 않으면 이 forEach는 의미가 없습니다. 🎜🎜mapforEach보다 더 강력한 것은 사실이지만 map은 새로운 배열을 생성하고 메모리를 차지합니다. map의 반환 값을 사용하지 않는 경우 forEach🎜🎜보충: 추가 경험을 사용해야 합니다. span>🎜 🎜ES6 이전에는 배열을 순회하는 두 가지 주요 방법이 있었습니다: 아래 첨자 반복을 사용하는 손으로 작성한 루프와 Array.prototype.forEach. 전자는 다재다능하고 가장 효율적이지만 작성하기가 더 번거롭습니다. 배열의 값을 직접 얻을 수 없습니다. 🎜🎜저자는 개인적으로 후자를 좋아합니다. 반복의 첨자와 값, 기능적 스타일을 직접 얻을 수 있습니다(FP는 불변 데이터 구조에 초점을 맞추고 forEach는 본질적으로 부작용이므로 다음 형식만 가집니다. FP는 있지만 마법은 없습니다.) 쓰기에 매우 상쾌합니다. 하지만! 여러분 중 누구든지 눈치 채셨는지 궁금합니다. 각 작업을 일단 시작하면 멈출 수 없습니다. . . 🎜

forEach는 콜백 함수를 허용하므로 미리 return할 수 있습니다. 이는 직접 작성한 루프의 continue와 같습니다. 하지만 중단할 수는 없습니다. 콜백 함수에 중단할 수 있는 루프가 없기 때문입니다.

[1, 2, 3, 4, 5].forEach(x => {
 console.log(x);
 if (x === 3) {
  break; // SyntaxError: Illegal break statement
 }
});
return,相当于手写循环中的 continue。但是你不能 break——因为回调函数中没有循环让你去 break
try {
 [1, 2, 3, 4, 5].forEach(x => {
  console.log(x);
  if (x === 3) {
   throw &#39;break&#39;;
  }
 });
} catch (e) {
 if (e !== &#39;break&#39;) throw e; // 不要勿吞异常。。。
}

解决方案还是有的。其他函数式编程语言例如 scala 就遇到了类似问题,它提供了一个函数
break,作用是抛出一个异常。

我们可以仿照这样的做法,来实现 arr.forEachbreak

[1, 2, 3, 4, 5].some(x => {
 console.log(x);
 if (x === 3) {
  return true; // break
 }
 // return undefined; 相当于 false
});

还有其他方法,比如用 Array.prototype.some 代替 Array.prototype.forEach

考虑 Array.prototype.some 的特性,当 some 找到一个符合条件的值(回调函数返回 true)时会立即终止循环,利用这样的特性可以模拟 break

for (const x of [1, 2, 3, 4, 5]) {
 console.log(x);
 if (x === 3) {
  break;
 }
}

some 的返回值被忽略掉了,它已经脱离了判断数组中是否有元素符合给出的条件这一原始的含义。

在 ES6 前,笔者主要使用该法(其实因为 Babel 代码膨胀的缘故,现在也偶尔使用),ES6 不一样了,我们有了 for...of。for...of 是真正的循环,可以 break

for (const [index, value] of [1, 2, 3, 4, 5].entries()) {
 console.log(`arr[${index}] = ${value}`);
}

但是有个问题,for...of 似乎拿不到循环的下标。其实 JavaScript 语言制定者想到了这个问题,可以如下解决:

rrreee

Array.prototype.entries

for...offorEach 的性能测试:https://jsperf.com/array-fore... Chrome 中 for...of아직 해결책이 있습니다. scala와 같은 다른 함수형 프로그래밍 언어에서도 비슷한 문제가 발생했습니다. 이 언어는 예외를 발생시키는
break 함수를 제공합니다.

🎜🎜우리는 모방할 수 있습니다 이런 방식으로 arr.forEachbreak를 구현할 수 있습니다.rrreee🎜 Array.prototype.some <code>Array.prototype.forEach를 대체합니다. 🎜Array.prototype.some의 특성을 고려하세요. some이 조건을 충족하는 값을 찾으면(콜백 함수가 true를 반환함) 루프는 다음과 같습니다. 즉시 종료됩니다. break의 특성을 시뮬레이션할 수 있습니다.rrreee🎜 some의 반환 값은 무시되며, 판단에서 분리되었습니다. 주어진 조건을 충족하는 요소가 배열에 있는지 여부. 🎜ES6 이전에는 이 방법을 주로 사용했습니다(사실 바벨 코드의 확장으로 인해 지금도 가끔 사용하고 있습니다). ES6는 다릅니다. for...of가 있습니다. for...of는 실제 루프이므로 break할 수 있습니다.rrreee🎜하지만 문제가 있습니다. for...of code> 루프의 첨자를 얻을 수 없는 것 같습니다. 실제로 JavaScript 언어 개발자는 이 문제를 생각하고 다음과 같이 해결할 수 있습니다: rrreee🎜Array.prototype.entries🎜<code>for...of forEach 성능 테스트: https://jsperf.com/array-fore... Chrome의 for...of가 더 빠릅니다.

위 내용은 JavaScript 배열 작업의 어려움(자세한 튜토리얼)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.