es6中数组实例的 fill()填充,fill后在fill为什么会有问题?

场景:
开发中想实现把一个一维数组变成一个二维数组,代码如下:

let arr = new Arrary(3).fill([])
let brr = ['1','2','3']
brr.forEach((item,index)=>{
    arr[index].push(item)
})
console.log(arr)
复制代码

想实现效果如下:

image.png

经过一顿看上去“高大上”操作,输出却为

image.png

分析:
这里构造函数:let arr = new Array(3)声明了一个长度为3的数组,使用fill方法往新生成的数组里填充三个空数组,达到二维数组的效果js数组fill()方法。然后新定义一个数组里面三个字符串1,2,3,遍历brr,使得brr中的每个元素push到二维数组的每一个空数组中,期望达到理想效果,现实却是骨感的,最终变成了二维数组中的每个数组对象都被push进去三个元素。

复现逻辑没有问题,往上看是用构造函数创建数组,使用了fill,你想去动态改变其中某一项的值,你会发现整个数组里面的值全都变化了,这是因为你忽略了这个方法的一个注意事项:

如果填充的类型为对象,那么被赋值的是同一个内存地址的对象,而不是深拷贝对象。

意思就是说填充的是对象,那么所有被填充的对象都指向同一个地址。所以这里我们需要将arr深拷贝一下,再修改其值。

// 深拷贝处理
let arr = new Array(3).fill([])
let brr = ['1', '2', '3']
let crr = JSON.parse(JSON.stringify(arr))
brr.forEach((item, index) => {
   crr[index].push(item)
})
console.log(arr,'arr')
console.log(crr,'crr')
复制代码

image.png

要是表达不清楚这里借用大佬的一段解释:

在创建二维空数组,fill 把第一个参数当做 value ,填充到数组每个项。言外之意,这个 value 不会每次都创建,仅仅是arr[0]=value ,arr[1]=value 等等一直到最后一项目。(当然第二个,第三个参数可以控制起始位置)map 是把函数每次运行的结果,组成新数组。返回新数组。要问为什么,这就是语言特性。是函数式编程,给你的错觉,让你觉得括号里面的一定会被多次运行。当然 fill 也可以多次创建这个 value 。然而实际上更直观的,是创建一次,其他的都直接等过来(指针引用)。为的是性能。你想想,如果是嵌套形式的对象,第一层做了复制,第二层要不要做。第三层呢?如果是环形结构呢?而且,fill 函数也不知道你传什么,说白了就是因为数组是引用类型。再说白了,因为 fill接收的是 value ,不是 function。

总结:
fill填充时若是引用数据类型可看成是数据的浅拷贝,每次填充的是目标对象地址的引用,使用foreach遍历push的值都是堆内存中指向的同一个对象,解决的办法是通过深拷贝,在堆内存开辟不同的对象。总之Array.fill还是比较适用于简单数据类型。

对于深拷贝和浅拷贝可参考:

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享