ES6数组的扩展详解


Posted in Javascript onApril 25, 2017

本文实例讲述了ES6数组的扩展。分享给大家供大家参考,具体如下:

1. Array.from()

Array.from()将类数组(array-like)对象与可遍历的对象转化为数组并返回。

下面是一个类数组

let arr = {
  '0':'a',
  '1':'b',
  '2':'c',
  length:3
};

ES5我们可能都会这样写:

var arr1=[].slice.call(arr);

ES6可以把一个类数组转化为一个真正的数组

var arr2=Array.from(arr)

实际应用中,常见的类似数组的对象是DOM操作返回NodeList集合,以及函数内部的arguments对象。array.from将他们转换成真正的数组。

// NodeList对象
let ps = document.querySelectorAll('p');
Array.from(ps).forEach(function (p) {
 console.log(p);
});
// arguments对象
function foo() {
 var args = Array.from(arguments);
 // ...
}

上面代码中,querySelectorAll方法返回的是一个类似数组的对象,只有将这个对象转为真正的数组,才能使用forEach方法。

只要是部署了Iterator接口的数据结构,Array.from都能将其转为数组。

Array.from('hello')
// ['h', 'e', 'l', 'l', 'o']
let namesSet = new Set(['a', 'b'])
Array.from(namesSet) // ['a', 'b']

上面代码中,字符串和Set结构都具有Iterator接口,因此可以被Array.from转为真正的数组。

如果参数是一个真正的数组,Array.from会返回一个一模一样的新数组。

Array.from([1, 2, 3])
// [1, 2, 3]

值得提醒的是,扩展运算符(...)也可以将某些数据结构转为数组。

// arguments对象
function foo() {
 var args = [...arguments];
}
// NodeList对象
[...document.querySelectorAll('div')]

扩展运算符背后调用的是遍历器接口(Symbol.iterator),如果一个对象没有部署这个接口,就无法转换。

Array.from方法则是还支持类似数组的对象。所谓类似数组的对象,本质特征只有一点,即必须有length属性。因此,任何有length属性的对象,都可以通过Array.from方法转为数组,而此时扩展运算符就无法转换。

Array.from({ length: 3 });
// [ undefined, undefined, undefined ]

上面代码中,Array.from返回了一个具有三个成员的数组,每个位置的值都是undefined。扩展运算符转换不了这个对象。

对于还没有部署该方法的浏览器,可以用Array.prototype.slice方法替代。

const toArray = (() =>
 Array.from ? Array.from : obj => [].slice.call(obj)
)();

Array.from还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组。

Array.from(arrayLike, x => x * x);
// 等同于
Array.from(arrayLike).map(x => x * x);
Array.from([1, 2, 3], (x) => x * x)
// [1, 4, 9]

下面的例子是取出一组DOM节点的文本内容。

let spans = document.querySelectorAll('span.name');
// map()
let names1 = Array.prototype.map.call(spans, s => s.textContent);
// Array.from()
let names2 = Array.from(spans, s => s.textContent)

下面的例子将数组中布尔值为false的成员转为0。

Array.from([1, , 2, , 3], (n) => n || 0)
// [1, 0, 2, 0, 3]

另一个例子是返回各种数据的类型。

unction typesOf () {
 return Array.from(arguments, value => typeof value)
}
typesOf(null, [], NaN)
// ['object', 'object', 'number']

如果map函数里面用到了this关键字,还可以传入Array.from的第三个参数,用来绑定this。

Array.from()可以将各种值转为真正的数组,并且还提供map功能。这实际上意味着,只要有一个原始的数据结构,你就可以先对它的值进行处理,然后转成规范的数组结构,进而就可以使用数量众多的数组方法。

Array.from({ length: 2 }, () => 'jack')
// ['jack', 'jack']

上面代码中,Array.from的第一个参数指定了第二个参数运行的次数。这种特性可以让该方法的用法变得非常灵活。

Array.from()的另一个应用是,将字符串转为数组,然后返回字符串的长度。因为它能正确处理各种Unicode字符,可以避免JavaScript将大于\uFFFF的Unicode字符,算作两个字符的bug。

function countSymbols(string) {
 return Array.from(string).length;
}

2. Array.of()

Array.of方法用于将一组值,转换为数组。

Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1

这个方法的主要目的,是弥补数组构造函数Array()的不足。因为参数个数的不同,会导致Array()的行为有差异。

Array() // []
Array(3) // [, , ,]
Array(3, 11, 8) // [3, 11, 8]

上面代码中,Array方法没有参数、一个参数、三个参数时,返回结果都不一样。只有当参数个数不少于2个时,Array()才会返回由参数组成的新数组。参数个数只有一个时,实际上是指定数组的长度。

Array.of基本上可以用来替代Array()或new Array(),并且不存在由于参数不同而导致的重载。它的行为非常统一。

Array.of() // []
Array.of(undefined) // [undefined]
Array.of(1) // [1]
Array.of(1, 2) // [1, 2]

Array.of总是返回参数值组成的数组。如果没有参数,就返回一个空数组。

Array.of方法可以用下面的代码模拟实现。

function ArrayOf(){
 return [].slice.call(arguments);
}

3. 数组实例的copyWithin()

数组实例的copyWithin方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组。

Array.prototype.copyWithin(target, start = 0, end = this.length)

它接受三个参数:

target(必需):从该位置开始替换数据。
start(可选):从该位置开始读取数据,默认为0。如果为负值,表示倒数。
end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示倒数.

这三个参数都应该是数值,如果不是,会自动转为数值。

[1, 2, 3, 4, 5].copyWithin(0, 3)
// [4, 5, 3, 4, 5]

上面代码表示将从3号位直到数组结束的成员(4和5),复制到从0号位开始的位置,结果覆盖了原来的1和2。

// 将3号位复制到0号位
[1, 2, 3, 4, 5].copyWithin(0, 3, 4)
// [4, 2, 3, 4, 5]
// -2相当于3号位,-1相当于4号位
[1, 2, 3, 4, 5].copyWithin(0, -2, -1)
// [4, 2, 3, 4, 5]
// 将3号位复制到0号位
[].copyWithin.call({length: 5, 3: 1}, 0, 3)
// {0: 1, 3: 1, length: 5}
// 将2号位到数组结束,复制到0号位
var i32a = new Int32Array([1, 2, 3, 4, 5]);
i32a.copyWithin(0, 2);
// Int32Array [3, 4, 5, 4, 5]
// 对于没有部署TypedArray的copyWithin方法的平台
// 需要采用下面的写法
[].copyWithin.call(new Int32Array([1, 2, 3, 4, 5]), 0, 3, 4);
// Int32Array [4, 2, 3, 4, 5]

4. 数组实例的find()和findIndex()

数组实例的find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。

[1, 4, -5, 10].find((n) => n < 0)
// -5

上面代码找出数组中第一个小于0的成员。

[1, 5, 10, 15].find(function(value, index, arr) {
 return value > 9;
}) // 10

上面代码中,find方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组。

数组实例的findIndex方法的用法与find方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。(找到一个符合的就停止)

[1, 5, 10, 15].findIndex(function(value, index, arr) {
 return value > 9;
}) // 2

find()返回的是值,findIndex返回的是位置。

这两个方法都可以接受第二个参数,用来绑定回调函数的this对象。

另外,这两个方法都可以发现NaN,弥补了数组的IndexOf方法的不足。

[NaN].indexOf(NaN)
// -1
[NaN].findIndex(y => Object.is(NaN, y))
// 0

上面代码中,indexOf方法无法识别数组的NaN成员,但是findIndex方法可以借助Object.is方法做到。

5. 数组实例的fill()

fill方法使用给定值,填充一个数组。

['a', 'b', 'c'].fill(7)
// [7, 7, 7]
new Array(3).fill(7)
// [7, 7, 7]

上面代码表明,fill方法用于空数组的初始化非常方便。数组中已有的元素,会被全部抹去。

fill方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置。

['a', 'b', 'c'].fill(7, 1, 2)
// ['a', 7, 'c']

上面代码表示,fill方法从1号位开始,向原数组填充7,到2号位之前结束。

6. 数组实例的entires(),keys()和value()

ES6提供三个新的方法——entries(),keys()和values()——用于遍历数组。它们都返回一个遍历器对象,可以用for...of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。

for (let index of ['a', 'b'].keys()) {
 console.log(index);
 }
// 0
// 1
for (let elem of ['a', 'b'].values()) {
 console.log(elem);
}
// 'a'
// 'b'
for (let [index, elem] of ['a', 'b'].entries()) {
 console.log(index, elem);
}
// 0 "a"
// 1 "b"

如果不使用for...of循环,可以手动调用遍历器对象的next方法,进行遍历。

let letter = ['a', 'b', 'c'];
let entries = letter.entries();
console.log(entries.next().value); // [0, 'a']
console.log(entries.next().value); // [1, 'b']
console.log(entries.next().value); // [2, 'c']

7. 数组实例的includes()

Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似。该方法属于ES7,但Babel转码器已经支持。

[1, 2, 3].includes(2);   // true
[1, 2, 3].includes(4);   // false
[1, 2, NaN].includes(NaN); // true

该方法的第二个参数表示搜索的起始位置,默认为0。如果第二个参数为负数,则表示倒数的位置,如果这时它大于数组长度(比如第二个参数为-4,但数组长度为3),则会重置为从0开始。

[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true

没有该方法之前,我们通常使用数组的indexOf方法,检查是否包含某个值。

if (arr.indexOf(el) !== -1) {
 // ...
}

indexOf方法有两个缺点,一是不够语义化,它的含义是找到参数值的第一个出现位置,所以要去比较是否不等于-1,表达起来不够直观。二是,它内部使用严格相当运算符(===)进行判断,这会导致对NaN的误判。

[NaN].indexOf(NaN)
// -1

includes使用的是不一样的判断算法,就没有这个问题。

[NaN].includes(NaN)
// true

下面代码用来检查当前环境是否支持该方法,如果不支持,部署一个简易的替代版本。

const contains = (() =>
 Array.prototype.includes
  ? (arr, value) => arr.includes(value)
  : (arr, value) => arr.some(el => el === value)
)();
contains(["foo", "bar"], "baz"); // => false

另外,Map和Set数据结构有一个has方法,需要注意与includes区分。

Map结构的has方法,是用来查找键名的,比如Map.prototype.has(key)、WeakMap.prototype.has(key)、Reflect.has(target,propertyKey)。
Set结构的has方法,是用来查找值的,比如Set.prototype.has(value)、WeakSet.prototype.has(value)。

8. 数组的空位

数组的空位指,数组的某一个位置没有任何值。比如,Array构造函数返回的数组都是空位。

Array(3) // [, , ,]

上面代码中,Array(3)返回一个具有3个空位的数组。

注意,空位不是undefined,一个位置的值等于undefined,依然是有值的。空位是没有任何值,in运算符可以说明这一点。

0 in [undefined, undefined, undefined] // true
0 in [, , ,] // false

上面代码说明,第一个数组的0号位置是有值的,第二个数组的0号位置没有值。

ES5对空位的处理,已经很不一致了,大多数情况下会忽略空位。

forEach(), filter(), every() 和some()都会跳过空位。

map()会跳过空位,但会保留这个值

join()和toString()会将空位视为undefined,而undefined和null会被处理成空字符串。

// forEach方法
[,'a'].forEach((x,i) => console.log(i)); // 1
// filter方法
['a',,'b'].filter(x => true) // ['a','b']
// every方法
[,'a'].every(x => x==='a') // true
// some方法
[,'a'].some(x => x !== 'a') // false
// map方法
[,'a'].map(x => 1) // [,1]
// join方法
[,'a',undefined,null].join('#') // "#a##"
// toString方法
[,'a',undefined,null].toString() // ",a,,"

ES6则是明确将空位转为undefined。

Array.from方法会将数组的空位,转为undefined,也就是说,这个方法不会忽略空位。

Array.from(['a',,'b'])
// [ "a", undefined, "b" ]

扩展运算符(...)也会将空位转为undefined。

[...['a',,'b']]
// [ "a", undefined, "b" ]

copyWithin()会连空位一起拷贝。

[,'a','b',,].copyWithin(2,0) // [,"a",,"a"]

fill()会将空位视为正常的数组位置。

new Array(3).fill('a') // ["a","a","a"]

for...of循环也会遍历空位。

let arr = [, ,];
for (let i of arr) {
 console.log(1);
}
// 1
// 1

上面代码中,数组arr有两个空位,for...of并没有忽略它们。如果改成map方法遍历,空位是会跳过的。

entries()、keys()、values()、find()和findIndex()会将空位处理成undefined。

// entries()
[...[,'a'].entries()] // [[0,undefined], [1,"a"]]
// keys()
[...[,'a'].keys()] // [0,1]
// values()
[...[,'a'].values()] // [undefined,"a"]
// find()
[,'a'].find(x => true) // undefined
// findIndex()
[,'a'].findIndex(x => true) // 0

由于空位的处理规则非常不统一,所以建议避免出现空位。

希望本文所述对大家JavaScript程序设计有所帮助。

Javascript 相关文章推荐
document.execCommand()的用法小结
Jan 08 Javascript
jQuery中last()方法用法实例
Jan 06 Javascript
js简单实现竖向tab选项卡的方法
May 04 Javascript
jquery合并表格中相同文本的相邻单元格
Jul 17 Javascript
JavaScript获取浏览器信息的方法
Nov 20 Javascript
jQuery操作基本控件方法实例分析
Dec 31 Javascript
vue多级多选菜单组件开发
Sep 08 Javascript
详解vue axios中文文档
Sep 12 Javascript
Vue高版本中一些新特性的使用详解
Sep 25 Javascript
Easyui 去除jquery-easui tab页div自带滚动条的方法
May 10 jQuery
vue2之简易的pc端短信验证码的问题及处理方法
Jun 03 Javascript
vue请求数据的三种方式
Mar 04 Javascript
Angular实现一个简单的多选复选框的弹出框指令实例
Apr 25 #Javascript
如何在 Vue.js 中使用第三方js库
Apr 25 #Javascript
Javascript中click与blur事件的顺序详析
Apr 25 #Javascript
完美解决UI-Grid表格元素中多个空格显示为一个空格的问题
Apr 25 #Javascript
ES6中Math对象新增的方法实例详解
Apr 25 #Javascript
jquery.form.js异步提交表单详解
Apr 25 #jQuery
关于jQuery中fade(),show()起始位置的一点小发现
Apr 25 #jQuery
You might like
PHP n个不重复的随机数生成代码
2009/06/23 PHP
php学习之 循环结构实现代码
2011/06/09 PHP
详解php的魔术方法__get()和__set()使用介绍
2012/09/19 PHP
php对数组排序代码分享
2014/02/24 PHP
PHP+iFrame实现页面无需刷新的异步文件上传
2014/09/16 PHP
php实现图片以base64显示的方法
2016/10/13 PHP
ThinkPHP5 的简单搭建和使用详解
2018/11/15 PHP
jQuery load方法用法集锦
2011/12/06 Javascript
JS对img进行操作(换图片/切图/轮换/停止)
2013/04/17 Javascript
兼容IE和Firefox火狐的上下、左右循环无间断滚动JS代码
2013/04/19 Javascript
jquery获取自定义属性(attr和prop)实例介绍
2013/04/21 Javascript
在ASP.NET中使用JavaScript脚本的方法
2013/11/12 Javascript
jQuery使用drag效果实现自由拖拽div
2015/06/11 Javascript
JavaScript AOP编程实例
2015/06/16 Javascript
javascript实现鼠标放上后下边对应内容变换的效果
2015/08/06 Javascript
基于jQuery实现多层次的手风琴效果附源码
2015/09/21 Javascript
JavaScript 最佳实践:帮你提升代码质量
2016/12/03 Javascript
微信小程序 picker 组件详解及简单实例
2017/01/10 Javascript
基于JavaScript实现焦点图轮播效果
2017/03/27 Javascript
Vue2 使用 Echarts 创建图表实例代码
2017/05/18 Javascript
React进阶学习之组件的解耦之道
2017/08/07 Javascript
vue封装一个简单的div框选时间的组件的方法
2019/01/06 Javascript
vue input实现点击按钮文字增删功能示例
2019/01/29 Javascript
jQuery子选择器与可见性选择器实例分析
2019/06/28 jQuery
微信小程序swiper左右扩展各显示一半代码实例
2019/12/05 Javascript
vue 判断元素内容是否超过宽度的方式
2020/07/29 Javascript
[07:57]DOTA2热力大趴狂欢夜 广州站活动回顾
2013/11/27 DOTA
在Django的URLconf中进行函数导入的方法
2015/07/18 Python
使用python 写一个静态服务(实战)
2019/06/28 Python
python设计tcp数据包协议类的例子
2019/07/23 Python
对Python 中矩阵或者数组相减的法则详解
2019/08/26 Python
python Qt5实现窗体跟踪鼠标移动
2019/12/13 Python
python三引号如何输入
2020/07/06 Python
员工教育培训协议书
2014/09/27 职场文书
2015年妇联工作总结范文
2015/04/22 职场文书
Win11更新失败并提示0xc1900101
2022/04/19 数码科技