Object.assign和对象扩展(用[...]语法)的区别是什么?

3

我这里有一些代码,我想知道它们是相同的还是不同的。我相信它们应该是相同的,但我不确定我是否做得对。

let zoneComment = updatedMap[action.comment.zone]
        ? [...updatedMap[action.comment.zone]] : [];

let zoneComment = updatedMap[action.comment.zone]
          ? Object.assign([], updatedMap[action.comment.zone]) : [];

如果这些是相同的,那么我应该使用哪个?或者这有什么关系吗?我想采用最佳实践,如果你认为哪个更好,请说明您的意见。

另外,如果你需要支持IE11(呃),那么Object.assign和展开运算符都不可用。否则,它们在所有主要浏览器中都得到支持。在处理对象时,“最佳实践”是使用对你/你的团队更清晰的方法。 - zfrisch
三元运算符和 action.comment.zone 索引与您的问题有什么关联?为什么不询问 [...arr]Object.assign([], arr) 的区别呢? - trincot
3个回答

4
在您的情况下,它们并不相同。原因是您有一个数组,而不是一个对象。
在数组上执行...会展开数组中的所有元素(但不包括属性)。
执行Object.assign时,需要一个对象,因此它将把数组视为对象,并将所有可枚举的自有属性复制到其中,而不仅仅是元素:

const a = [1, 2, 3];
a.test = 'example';

const one = [...a] // [1, 2, 3];
const two = Object.assign([], a); // { '0': 1, '1': 2, '2': 3, 'test': 'example' }

console.log('\none');
for (let prop in one) {
  console.log(prop);
}

console.log('\ntwo');
for (let prop in two) {
  console.log(prop);
}

然而,如果你将...操作符应用于一个对象和使用Object.assign进行比较,它们本质上是相同的。

// same result
const a = { name: 'test' }
console.log({ ...a })
console.log(Object.assign({}, a))

除了...总是创建一个新对象,而Object.assign还允许您改变现有对象。

// same result
const a = { name: 'test' }
const b = { ...a, name: 'change' };

console.log(a.name); // test
Object.assign(a, { name: 'change'})
console.log(a.name); // change

请记住,Object.assign已经是语言的一部分,而对象展开仍然只是一个提案,需要使用类似babel这样的工具进行预处理(转译)。


惊人的答案!我正在使用React,因此我确实有Webpack运行来转译代码!是否有更好的时机使用扩展运算符? - user7366497
很高兴能帮忙。就哪个更好而言,我个人更喜欢扩展运算符,因为在我的主观看法中,它的语法更清晰,但除此之外,这取决于你自己的个人偏好或团队的偏好 :) - nem035

3
简而言之,在数组中始终使用...扩展语法,永远不要在数组上使用Object.assignObject.assign适用于对象。虽然数组也是对象,但它会对数组产生某种实际上很少有用的影响。 Object.assign(obj1, obj2)获取obj2中所有可枚举键的值,并将它们分配给obj1。事实上,数组就是对象,数组索引就是对象键。 [...[1, 2, 3], ...[4, 5]]的结果是[1, 2, 3, 4, 5]数组。 Object.assign([1, 2, 3], [4, 5])的结果是[4, 5, 3]数组,因为第一个数组中索引01的值被第二个数组中的值覆盖。
当第一个数组为空时,Object.assign([], arr)[...arr]结果相似。但是,[...arr]的适当ES5替代品是[].concat(arr),而不是Object.assign([], arr)

2
您的问题实际上可以归结为:
当 arr 是一个数组时,[...arr] 和 Object.assign([], arr) 是否提供相同的结果?
答案是:通常情况下是相同的,但是:
- 如果 arr 是一个稀疏数组且其最后一个元素没有值,则两种方法得到的结果的 length 属性将不相同:展开语法将保持 length 属性的相同值,但 Object.assign 将生成一个长度等于最后一个使用的元素的索引加一的数组。 - 如果 arr 是一个稀疏数组(例如 Array(10)),则展开语法将在这些索引处创建具有 undefined 值的数组,因此它将不是一个稀疏数组。而 Object.assign 则会保留那些空槽(不存在的)。 - 如果 arr 具有自定义可枚举属性,则 Object.assign 会复制它们,但展开语法不会。
以下是第一和第二种差异的演示:

var arr = ["abc"]
arr[2] = "def"; // leave slot 1 empty
arr.length = 4; // empty slot at index 3

var a = [...arr];
var b = Object.assign([], arr);
    
console.log(a.length, a instanceof Array); // 4, true
console.log(b.length, b instanceof Array); // 3, true
console.log('1' in arr); // false
console.log('1' in a); // true (undefined)
console.log('1' in b); // false

如果arr是一个标准数组(没有额外的属性)并且所有槽都被填满,那么两种方法会产生相同的结果:
两种方法都返回一个数组。 [...arr]按照定义这样做,而Object.assign之所以这样做是因为它的第一个参数是一个数组,并且它将返回该对象:被改变了,但它的原型不会更改。尽管length不是可枚举的属性,Object.assign不会复制它,但是第一个参数数组的行为是它将调整其length属性,因为其他属性分配给它。
两种方法都进行浅复制。
结论:
如果您的数组具有要复制的自定义属性,并且末尾没有空槽:请使用Object.assign
如果您的数组没有自定义属性(或者您不关心它们),并且没有空槽,请使用扩展语法。
如果您的数组具有要复制的自定义属性和要维护的空槽,则两种方法都无法完成。但使用Object.assign更容易实现:
a = Object.assign([], arr, { length: arr.length });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接