0、一句话总结
ECMAScript6(11)——数组的扩展。可以将一个有Iterator接口的数据类型(arraylike)转为真正的数组; 将变量作为数组成员,返回一个新数组; 把数组内某些位置元素,复制在数组内的某些位置; 用某个值填充满整个数组; 遍历数组的key、value和key + value; 以函数为筛选条件,找到数组中第一个符合条件的元素; 告诉你数组中有没有某个元素;1、Array扩展方法
以下方法只能通过Array这个全局对象来访问,而不能通过例如以下几个方法来调用
Array.prototype.from; //undefined [].from; //undefined var a = [1]; a.from; //undefined var a = new Array(); a.from; //undefined var a = []; a.__proto__.from; //undefined
正确方法是:
typeof Array.from; //"function"
1.1、基于已有对象创建数组
Array.from(arrayLike[, mapFn[, thisArg]])
效果是从一个类似数组或可迭代对象创建一个数组的实例;
参数1:被用于转换为数组的对象;不能
参数2:可选,处理函数,将数组的每个元素通过本函数处理后再返回。如果没有return内容,数组该位置元素的值将是undefined(而不是跳过这个元素);
参数3:参数2函数里this指向的目标,默认是window对象;
arrayLike
简单来说(不涉及极端和特殊情况),几种常见类型会如下处理:
1. undefined和null会抛出TypeError; 2. number会返回一个空数组; 3. boolean类型返回空数组; 4. 字符串会以单个字符为单位分拆为数组,例如”ab”变为[“a”, “b”]; 5. 数组还是数组(是一个新的数组,但是浅复制,而不是深度复制,即按引用传递的元素保持不变); 6. 类似数组的对象,会被转为数组,比如Array.from({'0':"1",length:2})输出["1", undefined],注意,必须有length属性才成立。其他情况会返回空数组; 7. 函数返回空数组(无论函数有没有返回值); 8. NodeList转为数组,如代码:
var list = document.querySelectorAll(".a"); console.log(Object.prototype.toString.call(list)); //[object NodeList] console.log(Object.prototype.toString.call(Array.from(list))); //[object Array]
9. 其他有Iterator接口的类型。
在ES6中,有三类数据结构原生具备Iterator接口:数组、某些类似数组的对象、Set和Map结构。
所谓类似数组的对象,本质特征只有一点,即必须有length属性。
因此,任何有length属性的对象,都可以通过Array.from方法转为数组,而此时扩展运算符就无法转换。
但转换后,填充的时候就需要看key了,key符合才会被填充到对应位置;如果该位置key为空,那么值就是undefined。
更多内容等写了Iterator相关的内容后再回来补吧(如果我能想起来……)
mapFn
可选 处理函数,有点类似数组的map,有两个参数,分别是item(数组当前元素)和index(从0开始),没有第三个参数; 返回值将作为当前索引的值填充到数组中; 没有return的话当前位置为undefined;
thisArg
可选 mapFn里,this指向的目标
如果你有一个可以转换为数组的数据结构,比如NodeList,又想使用数组的各种api,那么就可以通过这个方法将该数据结构先转为数组,就可以方便的调用数组的各种API了。
1.2、生成数组
Array.of(element0[, element1[, …[, elementN]]])
说明:
生成一个新数组,然后将参数依次添加到该数组中,返回该数组。
如代码:
Array.of(1); //[1] Array.of("a", "c", "b"); //["a", "c", "b"] Array.of(); //[]
注意:
假如某个参数是一个按引用传递类型,那么将按引用传递给新数组(而不是创建一个全新的按引用传递对象)。如:
var a = {}; var b = Array.of(a) b; //[{}] b[0] === a; //true
与new Array的区别:
new Array(); //[] new Array(1); //[undefined] new Array(2); //[undefined, undefined] new Array(1, 2); //[1, 2] new Array("1"); //["1"]
也就是说,new Array假如参数为1个,并且参数类型为number,比如该参数为n时,他的效果是生成一个length为n,然后每个位置值都为undefined的数组。
而Array.of()无论参数是什么,是几个,都会将参数依次添加到一个新数组中并返回该数组;
2、数组实例的扩展方法
与1中不同,这里的方法不能直接通过Array.copyWithin来访问,而是需要通过prototype来访问。例如:
Array.copyWithin; //undefined typeof Array.prototype.copyWithin; //"function"
2.1、数组元素在数组内复制
arr.copyWithin(target)
arr.copyWithin(target, start)
arr.copyWithin(target, start, end)
简单来说,这个api是这么做事的:
为了方便理解,以[1, 2, 3, 4, 5].copyWithin(a, b, c)为例
准备复制调用这个方法的数组,即[1,2,3,4,5] 找到参数2,该参数决定了从数组哪个索引位置开始复制(包含该位置),默认值为0。比如b=2, c默认时,复制的数组内容就是[3,4,5] 找到参数3,该参数决定了从数组哪个索引位置结束复制(但不包含该索引位置)。默认值为数组的长度。上面数组中,c默认为5。假如b=1,c=2,那么复制数组内容就是[1] 参数2和参数3如果是负数,则从末尾开始数,例如-1则表示数组最后一个元素的索引,注意参数2包含,参数3不包含该位置 通过参数2和参数3,从数组中找到了要被复制的部分。然后现在要决定将数组复制在哪里。 查看参数1,确定将数组从哪个索引位置开始替换,默认值为0。替换对应位置的数组元素。 不会超出数组原有长度 返回被修改后的数组
流程解释:
a=1,b,c默认值。于是b=0,c=5,复制内容为[1,2,3,4,5],从下标1开始替换。于是数组变为[1,1,2,3,4]。注意,不会超出
[1, 2, 3, 4, 5].copyWithin(1); //[1, 1, 2, 3, 4]
a=1,b=3,c默认。于是c=5,复制内容为]4,5]。从下标1开始替换,于是数组变为[1,4,5,4,5]
[1, 2, 3, 4, 5].copyWithin(1,3); //[1, 4, 5, 4, 5]
a=1,b=2,c=-2。于是c=3,复制内容为[3]。从下标1开始替换,于是数组变为[1,3,3,4,5]
[1, 2, 3, 4, 5].copyWithin(1,2,-2); //[1, 3, 3, 4, 5]
2.2、数组的填充
arr.fill(value)
arr.fill(value, start)
arr.fill(value, start, end)
效果是将数组用数值填充。
参数1表示用于填充的值;
参数2表示填充的起始索引(含);
参数3表示填充的末尾索引(不含);
如果数组原位置有值,会覆盖原位置的值。
new Array(3).fill(3); //[3,3,3],填充 [1,2].fill(3); //[3,3],会覆写 [0, 0, 0].fill(1, 1); //[0,1,1],可以设置起始填充位置 [0, 0, 0].fill(1, 0, 1); //[1,0,0],也可以设置终止填充位置
如果按引用传递类型的话,填充的时候是同一个值。
var a = {}; var arr = new Array(3).fill(a); arr; //[{}, {}, {}] arr[0] === arr[1]; //true
2.3、数组的遍历
arr.keys()
arr.values()
arr.entries()
用于遍历数组,分别可以取index,value和index + value。
注意:chrome、IE、火狐(至少53.0版本)不支持arr.values(),而edge支持
用法是通过for…of方法来使用,比较类似for…in方法。
如示例代码:
var arr = ["a", "b", "c"]; for (let key of arr.keys()) { console.log(key); } //0 //1 //2
//注意兼容性问题 var arr = ["a", "b", "c"]; for (let val of arr.values()) { console.log(val); } //"a" //"b" //"c"
var arr = ["a", "b", "c"]; //let kv又可以写成let [key, val]这种形式 for (let kv of arr.entries()) { console.log(kv); } //[0, "a"] //[1, "b"] //[2, "c"]
另外,以上方法返回的是一个Iterator(遍历器/迭代器)对象,可以通过该对象来遍历整个数组。具体来说请看以下示例代码:
var arr = ["a", "b", "c"]; var obj = arr.entries(); //obj通过next方法来遍历,此时obj指向该数组的起始位置(数组的第一个元素之前) var first = obj.next(); //first:{done:false, value:[0,"a]} //此时first是obj遍历到第一个对象后的结果,obj对象已经迭代到第一个元素 var second = obj.next(); //second:{done:false, value:[1,"b"]} var third = obj.next(); //third:{done:false, value:[2,"c"]} var end = obj.next(); //end:{done:true, value:undefined} //此时遍历结束,因此done为true,并且value为undefined
另外,这个遍历器对象,只有next方法,而next方法会更新他自身状态。
也只有通过next方法返回的值(而非遍历器对象),才能获知当前是否遍历结束,以及遍历到当前目标的值是什么。
2.4、找到元素和元素的索引
arr.find(callback[, thisArg])
通过回调函数,查找到符合条件的第一个元素,并返回。
参数1是一个函数,这个函数会遍历整个数组。它有三个参数,分别是:当前元素,当前元素的索引,整个数组。有些类似arr.forEach,当返回值为true的时候,筛选成功,终止其后继续执行的代码。
参数2是回调函数中this指向的对象,参考其他此类api理解即可。
示例代码。
var a = { num: 1 } var b = { num: 2 } var c = { num: 3 } var arr = [a, b, c]; var result = arr.find(function (item, index, array) { console.log(item.num, index); //会依次输出1 0和2 1 if (item.num > 1) { return true; } else { return false; } }) console.log(result); //{num: 2} console.log(b === result); //true,说明不是创建一个全新的元素并返回
arr.findIndex(callback[, thisArg])
和arr.find几乎一样,唯一区别是返回的是索引,而非数组元素。
将上面代码修改为如下后,返回值为1
..... var result = arr.find(function (item, index, array) { ... }) console.log(result); //1
类似:
... [a, b, c].indexOf(b); //1
但相对来说,find和findIndex,扩展性更强(因为可以用回调函数),并且支持对NaN的查找(因为逻辑可以自己写)。
2.5、查看数组是否包含
arr.includes(searchElement)
arr.includes(searchElement, fromIndex)
2.4的方法,是通过作为参数的函数的返回值,来返回你需要的元素、或元素的索引,比较类似arr.indexOf()。
而这个更加类似indexOf(),这个方法直接传你要查找的元素,他只告诉你有、或没有。
indexOf()当有的时候返回非负整数,没有的时候返回-1。
使用这个方法需要注意以下几点:
可以判断NaN是否包含(indexOf()不行); 永远记住按引用传递类型,查看的是引用的对象是否一致。例如:[{}].includes({}); //false; 虽然兼容性对于现代浏览器还可以,但小心某些低版本的现代浏览器不支持;2.6、数组的空位
具体去查看阮一峰的博客吧。
总而言之,良好的代码习惯是不要写类似
var arr = [,,]; //或 var arr = new Array(10);
这样的声明代码。而是写
var arr = [undefined, undefined, undefined]; //或 var arr = new Array(10); arr.fill(undefined);
可以避免很多莫名其妙出现的bug。