ES6学习笔记(七)数组的扩展

本文深入讲解ES6中数组的新特性,包括扩展运算符、Array.from、Array.of、copyWithin、find、findIndex、fill、flat和flatMap等方法的使用及注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

扩展运算符


扩展运算符的使用

跟rest参数刚好相反,rest参数是将多个逗号分隔的参数转为一个数组,而扩展运算符是将数组转为逗号分隔的参数

扩展运算符后面可放表达式,表达式的结果要是一个带有Iterator接口的数据结构,如果后面是一个空数组,则返回空,而如果放在括号内,则会被认为是函数调用

扩展运算符内部调用的是数据结构的 Iterator 接口只要带有Iterator接口,就可以使用扩展运算符转为数组,没有Iterator接口的对象添加Iterator接口后就可以使用扩展运算符转为数组

[...[1,2,3]]//[1,2,3]
[...(Math.random()*10>5?['big']:['small'])]//生成随机数大于5得到["big"],小于5得到["small"]
[...[]]//[]

扩展运算符的应用

1.代替apply函数

在ES5中,如果要找出一个数组的最大值,我们可以通过使用Math.max.apply(null,arr)来实现,而在ES6中,可以通过扩展运算符直接将数组作为参数传进去。

let arr=[1,2,3];
Math.max.apply(null,arr);//3      ES5的写法
Math.max(...arr);//3       ES6的写法

这里的apply第一个参数使用了null,这样会把this对象变为全局环境(严格模式下是undefined),为了避免会调用到全局下的部分内容导致一些问题,建议还是使用空对象作为第一个参数。创建该空对象可以使用Object.create(null),比起{}其不会创建Object.prototype,所以比起{}更空。(参考自《你不知道的JavaScript上》)

2.扩展Set结构和Map结构

let s=new Set([1,2,3]);
[...s];//[1,2,3]
let m=new Map([[1,2],[2,3]]);
[...m];//[[1,2],[2,3]]

(关于Set结构和Map结构可见我的另一篇博客ES6学习笔记(五)Set结构和Map结构

3.复制数组

用赋值符号“=”来复制数组,实际上是复制值所在的地址,若修改了其中一个的值,另一个也会发生改变,所以在ES5中,要通过concat来复制数组,而在ES6中,通过扩展运算符就可以直接复制数组了

var arr1=[1,2,3];
var arr2=arr1;
arr2[0]=2;
arr1;//[2,2,3];


arr2=arr1.concat();
arr2[0]=3;
arr1;//[2,2,3];
arr2;//[3,2,3];


arr2=[...arr1];
arr2[0]=4;
arr1;//[2,2,3]
arr2;//[4,2,3]

4.合并数组

使用扩展运算符可以很方便地合并数组,将多个带扩展运算符的数组放入[]内赋值给另一个数组即合并了数组,要注意的是,这里用到的是浅拷贝,如果数组成员是对象的话,原数组对象中的属性发生改变,合并数组中的属性也会发生改变。

var arr1=[1,2,3];
var arr2=[4,5,6];
var arr3=[...arr1,...arr2];//[1,2,3,4,5,6]


var person1=[{name:"john",age:18}];
var person2=[{name:"Bob",age:20}];
var person3=[...person1,...person2];//[{name:"john",age:18},{name:"Bob",age:20}]
person1[0].name="jack";
person3;//[{name:"jack",age:18},{name:"Bob",age:20}]

5.将数组成员作为函数参数

let arr=[1,2,3,4]
function fn(){
    for(let i of arguments){
        console.log(i);
    }
}//1   2   3   4
fn(...arr)

Array.from


Array.from用于将类似数组的对象和可遍历的对象转化为数组

相对于只能转化有Iterator接口的数据结构的扩展运算符,Array.from可转化的范围更大,任何有length属性的对象都可以转为数组(如果传入的是一个数组的话,则生成一个相同的新数组)

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

没有部署Array.from的浏览器可以用Array.prototype.slice代替

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

传入的第二个参数其实等同于只传入第一个参数转为数组后调用数组的map方法,把第二个参数作为调用的map方法的参数,对生成的数组中的每个值执行第二个参数的操作后返回操作后的数组(如果map中使用了this,可以使用第三个参数来指定this的指向)

Array.from([1,2,3,4],val=>val=val*2);//[2,4,6,8]


var person={name:"john",age:18}
function fn(val){
    return val>this.age;
}
Array.from([10,20,30,40],fn,person);//[false,true,true,true];

这里要注意的是如果要使用this,第二个参数就不要使用箭头函数来表示,因为箭头函数没有this,如果在这里使用了箭头函数,函数体里的this就会指向外部的this。

Array.of


Array.of用于将传入的参数作为数组成员生成一个数组,这个方法用于解决ES5中用new Array生成数组时传入不同的参数会有不同的处理机制的问题,如果使用new Array生成数组,传入的参数为一个时生成长度为该参数的空位数组,而其他情况则将传入参数作为数组成员生成一个数组。而Array.of不管传入几个参数,都和new Array传入非1参数的情况相同。(使用Array来构造数组时可以省略new,结果是一样的)

new Array();//[]   等同于Array()
new Array(3);//[,,]   等同于Array(3)
new Array(3,4,5);//[3,4,5]   等同于Array(3,4,5)


Array.of();//[]
Array.of(3);//[3]
Array.of(3,4,5);//[3,4,5]

在不支持Array.of的场合,可以声明一个函数,将传入的arguments用[].slice.call(arguments)处理后返回,与使用Array.of结果相同

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

方法构造数组的指向


Array.of和Array.from最后都生成一个数组,在类继承中,使用一个子类来继承Array,创建的实例会指向子类的实例

class childArray extends Array{
    // ... 
}

childArray.from([1,2]) instanceof childArray; // true
childArray.of(1,2) instanceof childArray; // true

Array.from(childArray.from([1,2])) instanceof childArray; // false
Array.from(childArray.of(1,2)) instanceof childArray; // false

copyWithin


copyWithin将数组中的一部分覆盖另一部分,该方法会修改原数组,返回新的数组

copyWithin(target,start=0,end=length-1)

target:覆盖的起始点,必需的

start:用于覆盖的部分的起点,可选的,默认值为0

end:用于覆盖部分的终点,可选的,默认值为length-1

三个参数的数值必须为数值,如果不是数值,会被强行转为数值,如果是浮点数,会向下取整(后面有用到start和end的参数的地方也一样)该方法不会增加数组的长度,到达数组结尾复制就会停止。

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

find和findIndex


find用于找出数组中第一个符合条件的数组成员,如果没找到则返回undefined。

find的回调函数可以接受三个参数,依次为当前位置的值,当前位置的下标,原数组

[1,2,3].find(function(value,index,arr){

return value>1;

}//2

find可以接收第二个参数,用于指定回调函数中的this

function f(value){
    return value>this.age;
}
var person={name:"jack",age:20};
[13,24,44].find(f,person);//24

find可以利用isNaN()或者Object.is()来找到NaN,弥补了indexof的不足

[NaN].find(value=>isNaN(value))//NaN
[NaN].find(value=>Object.is(NaN,value));//NaN

findIndex用法和find一样,但找到的时候返回的是找到的位置的下标,找不到的时候返回-1。

fill


fill用于用一个值填充整个数组或者数组的部分位置

fill(value,start=0,end=length-1)

value:用于填充数组的值

start:开始填充的位置,默认为0

end:结束填充的位置,默认为length-1

该方法的填充为浅拷贝,若用于填充的是对象,那么修改其中一个对象的属性,其他都会跟着改变

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

new Array(3).fill(7)
// [7, 7, 7]

let arr = new Array(3).fill({name: "Mike"});
arr[0].name = "Ben";
arr
// [{name: "Ben"}, {name: "Ben"}, {name: "Ben"}]

遍历方法keys,values,entries


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"

Includes


includes用于查找当前数值有没有和传入的参数相等的值,有则返回true,没有则返回false

includes(value,start)

value:要查找的值

start:开始查找的位置,为负数时表示倒数的位置。如果是正数且大于数组长度,就只查找最后一个数,如果是负数且绝对值大于数组长度,则从0开始查起,默认值为0

includes同样可以找到NaN

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

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

[1,2,3,4].includes(5,0);//false

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

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

[NaN].includes(NaN);//true

flat和flatMap


flat用于将数组降低维数,传入的参数为降低的维数,默认为1,如果要保证降到一维,可以传入Infinity,如果原数组有空位,flat会跳过空位。(TC39将该方法加入到ES2019中,有些地方可能还没法正常使用)

[1,2,3,[4,5]].flat();//[1,2,3,4,5]

[1,2,3,[[4],5]].flat(2);//[1,2,3,4,5]

[1, [2, [3]]].flat(Infinity)// [1, 2, 3]

flatMap对每个数组成员执行一个类似map函数,执行后再对该数组执行flat(),但是只能展开一层数组,其第二个参数可用于绑定第一个参数中的函数的this。

对数组空位的处理


ES5对数组空位的处理

forEach(), filter(), reduce(), 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

// reduce方法
[1,,2].reduce((x,y) => x+y) // 3

// 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,即不会忽略空位,在用copyWithin拷贝时会拷贝空位

将空位视为undefined的方法:Array.from,扩展运算符,entries(),keys(),values(),find()和findIndex()

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

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

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

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

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

// 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

参考自阮一峰的《ECMAScript6入门》

           Kyle Simpson的《你不知道的JavaScript 下卷》

 


ES6学习笔记目录(持续更新中)

 

ES6学习笔记(一)let和const

ES6学习笔记(二)参数的默认值和rest参数

ES6学习笔记(三)箭头函数简化数组函数的使用

ES6学习笔记(四)解构赋值

ES6学习笔记(五)Set结构和Map结构

ES6学习笔记(六)Iterator接口

ES6学习笔记(七)数组的扩展

ES6学习笔记(八)字符串的扩展

ES6学习笔记(九)数值的扩展

ES6学习笔记(十)对象的扩展

ES6学习笔记(十一)Symbol

ES6学习笔记(十二)Proxy代理

ES6学习笔记(十三)Reflect对象

ES6学习笔记(十四)Promise对象

ES6学习笔记(十五)Generator函数

ES6学习笔记(十六)asnyc函数

ES6学习笔记(十七)类class

ES6学习笔记(十八)尾调用优化

ES6学习笔记(十九)正则的扩展

ES6学习笔记(二十)ES Module的语法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值