提问人:Koen Morren 提问时间:2/23/2016 最后编辑:MogsdadKoen Morren 更新时间:9/22/2022 访问量:32227
带有对象的 Array.prototype.fill() 传递引用而不是新实例
Array.prototype.fill() with object passes reference and not new instance
问:
我正在玩弄一点,并试图实例化一个新的长度数组,其中该数组的所有元素都被初始化为一个值:x
y
var arr = new Array(x).fill(y);
如果 的值不是对象,则此方法很有效。
这意味着,如果是一个对象,则为真:y
y
var arr = new Array(2).fill({});
arr[0] === arr[1]; //is true;
arr[0].test = 'string';
arr[1].test === 'string'; //is also true;
有没有办法说明在使用 fill 函数时应该为每个元素创建一个新对象?还是我应该把它转换成一个循环?
答:
您可以先使用任何值(例如)创建数组,然后您将能够使用:fill
undefined
map
var arr = new Array(2).fill().map(u => ({}));
var arr = new Array(2).fill().map(Object);
评论
Array.from({length:2}, u => ({}))
new
Array.from
公认的答案是好的,并且在 90% 的情况下都有效。
但是,如果你正在制作高性能的 JS 应用程序,并且如果你使用大/巨大的数组,Array.map(..) 会在内存和处理器使用方面产生很大的过载,因为它会创建一个数组的副本。
我建议使用经典的for循环:
a = new Array(ARRAY_SIZE);
for (var i = 0; i < ARRAY_SIZE; i++) {
a[i] = [];
}
// or it's one line alternative
for (var i = 0, a = []; i < ARRAY_SIZE; a[i++] = []);
我测试了六个替代方案,得到了这个:
Array.map(),如上所述(慢 11 倍!!):
a = new Array(ARRAY_SIZE).fill().map(u => { return []; });
对于循环,最好的(最快):
// Standard multi-line way a = new Array(ARRAY_SIZE); for (var i = 0; i < ARRAY_SIZE; i++) { a[i] = []; } // One line syntax for (var i = 0, a = []; i < ARRAY_SIZE; a[i++] = []);
forEach(慢 6 倍):
a = new Array(ARRAY_SIZE).fill(); a.forEach((val, i) => { a[i] = []; })
[更新 2020-08-27]下面伊利亚斯·卡里姆(Ilias Karim)提出的另一种方式
Array.from(慢 30 倍!! - 尽管语法:(最好,但性能显然更差
a = Array.from({ length: ARRAY_SIZE }, () => []);
[..数组(..)](5 倍!!慢)
a = [...Array(ARRAY_SIZE)].map(_=>([]))
Array.push(..),性能第二(2 倍!!慢)
let a = [], total = ARRAY_SIZE; while(total--) a.push([]);
我用这把小提琴进行测试。
评论
一种高性能解决方案:Array.from({ length: 5 }, () => new Object())
评论
Array.from({length:5}, _=>{})
_=>({})
undefined
Array.from({ length: 5 }, Object)
也可以,但更短更快。所以很多答案只是在没有任何证据的情况下说“这是性能的”。请凭经验展示您的基准。[...Array(5)].map(Object)
伊利亚斯·卡里姆(Ilias Karim)的回答非常出色。我只是做了以下事情:
a = Array.from({length:l}, () => new Array(c).fill(prefix));
创建一个指定大小的预填充 2D 数组,l x c,填充前缀。现在,我的代码可以填充 2D 矩阵中需要非前缀值的插槽。
评论
new
prefix
最短的可能:
let node = [...Array(2)].map(_=>({}))
console.log(node)
评论
我写了一篇关于这个的博文:http://www.samhenderson.xyz/posts/12
但 TLDR 是,如果您想避免链接多个功能,例如 , .并且想要避免编写循环,那么你可以使用:fill
map
const array = Array.from({ length: 2 },()=>({}))
对于数组的数组:
const array = Array.from({ length: 2 },()=>([]))
评论
Array.from({length: n}, () => {})
或者字符也比它多,似乎值得忽略。Array.from(Array(n), () => {})
[...Array(n)].map(() => {})
为了补充解释别名问题以及如何解决它的答案,这里有一个方便的函数,可用于为调用方创建语法更简洁的数组:
const array = (length, fill) =>
[...Array(length)].map((_, i) =>
typeof fill === "function" ? fill(i) : fill
);
// usage:
const a = array(3, i => array(3, j => [i, j]));
a[0][0][0] = -42;
console.log(a);
请注意,您仍然需要对非基元值使用回调函数。这实际上是一项功能,因为它公开了索引,并允许您提供任意逻辑来填充元素。如果您担心意外地将非基元、非函数对象作为填充值传递,则可能会引发错误。
如果你真的希望能够直接传递一个对象,并在后台复制它,这里有一个几乎禁止混叠的调整:
const array = (length, fill) =>
[...Array(length)].map((x, i) =>
typeof fill === "function" ? fill(i) :
typeof fill === "object" ? _.cloneDeep(fill) : fill
);
// usage:
const a = array(2, array(2, {foo: 3}));
a[0][0].foo = 42;
console.log(a);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
一般来说,我建议几乎完全避免使用传播语法,因为很容易忘记别名行为并最终出现令人沮丧的错误。.fill()
[...Array()]
如果速度很重要,请使用传统循环:for
const array = (length, fill) => {
const a = [];
for (let i = 0; i < length; i++) {
a[i] = typeof fill === "function" ? fill(i) : fill;
}
return a;
};
// usage:
const a = array(2, () => array(2, Object));
a[0][0].foo = 42;
console.log(a);
上一个:多重分配混淆
评论