澳门新萄京官方网站-www.8455.com-澳门新萄京赌场网址

给初学者,分析JavaScript数组操作难点

2019-06-29 作者:澳门新萄京赌场网址   |   浏览(56)

给初学者:JavaScript 中数组操作注意点

2017/12/27 · JavaScript · 数组

原文出处: CarterLi   

以下内容是学习JavaScript数组的时候总结的经验以及需要注意的点。

    当我们要对数组或者是集合进行读取数据的时候,就会用到遍历,那么,各种遍历都有什么特点呢,今天小编就带领大家一探javaScript中的遍历.

当我们要对数组或者是集合进行读取数据的时候,就会用到遍历,那么,各种遍历都有什么特点呢,今天小编就带领大家一探javaScript中的遍历.首先介绍数组的定义方法

</br>

  • 第一种:构造函数法 ,通过 new 关键字 定义数组
    var array = new Array();
  • 第二种:直接定义法,这里 [] 里面的值,可有 可无,这种定义方式下的数组 长度是可扩展的
    //注意:若[]中有元素时,需要用"," 分隔,否则 就是 一个元素了
    var array2 = [];
  • 声明一系列数据结构
  • 构造函数
    function Person ( name ){
    this.name = name;
    }
    //数组:数组元素是构造函数生成的对象
    var arr = [ new Person("小王") , new Person ("小红") , newPerson("小李") ];
    //字面量对象
    //注意:字面量对象中有多个元素的时候 用"," 逗号隔开
    var game = {
    "name" : "lili",
    "type" : "USA"
    }

不要用 for_in 遍历数组


这是 JavaScript 初学者常见的误区。for_in 用于遍历对象中包括原型链上的所有可枚举的(enumerable)的 key,本来不是为遍历数组而存在。

使用 for_in 遍历数组有三点问题:

遍历顺序不固定

JavaScript 引擎不保证对象的遍历顺序。当把数组作为普通对象遍历时同样不保证遍历出的索引顺序。

会遍历出对象原型链上的值。

如果你改变了数组的原型对象(比如 polyfill)而没有将其设为 enumerable: false,for_in 会把这些东西遍历出来。

运行效率低下。

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

PS:你可能是想找 for_of

不要用 for_in 遍历数组

首先介绍数组的定义方法

1.普通的 for 循环遍历

第一种:最常用的 for 循环遍历 ,适合 所有的情况 ( 有Length 属性)
//这种遍历需要通过下标的方式读取对象
for ( var i =0 ; i < arr.length ; i ) {
console.log( arr[i]);
}

不要用 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.stringify 和 JSON.parse 自身操作 JSON 字符串的速度是很快的。然而为了深拷贝数组把对象序列化成 JSON 再反序列化回来完全没有必要。

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

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

这是 JavaScript 初学者常见的误区。for_in 用于遍历对象中包括原型链上的所有可枚举的(enumerable)的 key,本来不是为遍历数组而存在。

//第一种:构造函数法 ,通过 new  关键字 定义数组

2.for ..in 循环遍历

//遍历数组注意: 以后 能不用 for..in 遍历数组,就不用, for..in性能不好,而且容易造成下标错乱问题
//tempIdx 表示数组的下标
//arr 表示要遍历的数组
for( var tempIdx in arr ){
console.log( arr[tempIdx].name);
}
//遍历对象:for..in非常适合去遍历字面量对象
//左边变量:是这个对象的每一个属性
//右边变量:要遍历的对象
for ( var tempProp in game ){
//这里要注意:当属性为变量的时候,要用方括号[]去访问!!!
//game.tempProp 访问game下的 tempProp属性 undefined
console.log( game[tempProp] );
}

注意:用for..in遍历 字面的对象的时候 , 假如key是 数值 或者 数值字符串, 这个时候 for..in遍历是会优先输出的, 优先级如下:
数值 -> 数值字符串 -> 正常的key

不要用 arr.find 代替 arr.some


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

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

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

使用 for_in 遍历数组有三点问题:

var array = new Array();

3. forEach( ) 遍历

//遍历数组
//如果遍历数组,这个方法其实是Array.prototype.forEach( )
//数组的原型方法
//forEach 的参数 是一种匿名函数
//这个匿名函数的参数
//第一个参数: 数组的每个元素
//第二个参数: 数组的下标
//第三个参数: 当前数组对象
//注意:forEach 遍历有一个致命的 缺陷, 不能使用 break continue return
arr.forEach ( function( ele , idx , array ){
console.log ( ele) ;//每个元素
console.log ( idx );//下标
console.log ( array );//当前数组
});
//遍历Map
//如果遍历 Map,这个方法其实是 Map.prototype.forEach
//forEach 的参数: 是一个匿名回调 函数
//这个匿名 函数的参数
//第一个参数: value值
//第二个参数: key值
//第三个参数: 当前的对象
map.forEach( function( value , key , map ){
console.log( key ":" value );
}

不要用 arr.map 代替 arr.forEach


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

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

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

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

1、遍历顺序不固定

//第二种:直接定义法,这里 [] 里面的值,可有 可无,这种定义方式下的数组 长度是可扩展的

4. for..of 遍历

//遍历数组
//第一个变量: 数组中的每一个元素
//第二个遍历: 要遍历的数组对象
for( var temple of arr ) {
console.log( tempEle.name );
}
//遍历map
//第一个参数: 是一个数组,数组中有两个元素 ,分别是key 和 value
//第二个参数: 要遍历的map对象
for( var [ key , value ] of map ){
console.log( key "--" value );
}
//另一种方式
for( var keyAndValue of map ){
console.log( keyAndValue[0] "--" keyAndValue[1] );
}
注意:for..of 不能遍历 object 集合,假如要遍历 object集合,需要先用普通的for循环遍历object集合,添加到数组,再对其进行下一步操作
for..of 兼容性很差,很多 浏览器不兼容 , 所以还是乖乖使用 基本的 for 循环吧

补:forEach 与 break


ES6 以前,遍历数组主要就是两种方法:手写循环用下标迭代,使用 Array.prototype.forEach。前者万能,效率最高,可就是写起来比较繁琐——它不能直接获取到数组中的值。

笔者个人是喜欢后者的:可以直接获取到迭代的下标和值,而且函数式风格(注意 FP 注重的是不可变数据结构,forEach 天生为副作用存在,所以只有 FP 的形而没有神)写起来爽快无比。但是!不知各位同学注意过没有:forEach 一旦开始就停不下来了。。。

forEach 接受一个回调函数,你可以提前 return,相当于手写循环中的 continue。但是你不能 break——因为回调函数中没有循环让你去 break:

JavaScript

[1, 2, 3, 4, 5].forEach(x => { console.log(x); if (x === 3) { break; // SyntaxError: Illegal break statement } });

1
2
3
4
5
6
[1, 2, 3, 4, 5].forEach(x => {
  console.log(x);
  if (x === 3) {
    break;  // SyntaxError: Illegal break statement
  }
});

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

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

JavaScript

try { [1, 2, 3, 4, 5].forEach(x => { console.log(x); if (x === 3) { throw 'break'; } }); } catch (e) { if (e !== 'break') throw e; // 不要勿吞异常。。。 }

1
2
3
4
5
6
7
8
9
10
try {
  [1, 2, 3, 4, 5].forEach(x => {
    console.log(x);
    if (x === 3) {
      throw 'break';
    }
  });
} catch (e) {
  if (e !== 'break') throw e; // 不要勿吞异常。。。
}

恶心的一B对不对。还有其他方法,比如用 Array.prototype.some 代替 Array.prototype.forEach。

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

JavaScript

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

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

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

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

JavaScript

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

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

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

JavaScript

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

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

Array.prototype.entries

for…of 和 forEach 的性能测试: Chrome 中 for…of 要快一些哦

本文由澳门新萄京官方网站发布于澳门新萄京赌场网址,转载请注明出处:给初学者,分析JavaScript数组操作难点

关键词: