假设我有一个选项变量我想设置一个默认值。

这两种选择的优点/缺点是什么?

使用对象扩展

options = {...optionsDefault, ...options};

或者使用Object.assign

options = Object.assign({}, optionsDefault, options);

这是让我疑惑的承诺。


当前回答

注意:Spread不仅仅是Object.assign的语法糖。它们在幕后的运作方式大不相同。

对象。assign对新对象应用setter,而Spread则不会。此外,对象必须是可迭代的。

复制 如果您需要对象当前的值,并且不希望该值反映该对象的其他所有者所做的任何更改,则使用此选项。

使用它创建对象的浅拷贝 始终将不可变属性设置为copy的好做法——因为可变版本可以传递到不可变属性中,copy将确保您始终处理不可变对象

分配 赋值在某种程度上与复制相反。 Assign将生成一个setter,它将值直接赋给实例变量,而不是复制或保留它。 当调用赋值属性的getter时,它返回一个对实际数据的引用。

其他回答

当你必须使用Object.assign时,我想添加这个简单的例子。

class SomeClass {
  constructor() {
    this.someValue = 'some value';
  }

  someMethod() {
    console.log('some action');
  }
}


const objectAssign = Object.assign(new SomeClass(), {});
objectAssign.someValue; // ok
objectAssign.someMethod(); // ok

const spread = {...new SomeClass()};
spread.someValue; // ok
spread.someMethod(); // there is no methods of SomeClass!

使用JavaScript时可能不清楚。但是如果你想创建一些类的实例,使用TypeScript会更容易

const spread: SomeClass = {...new SomeClass()} // Error

正如其他人所提到的,在撰写本文时,object .assign()需要一个polyfill和object spread…需要一些蒸煮(也许是一个填充)为了工作。

考虑下面的代码:

// Babel wont touch this really, it will simply fail if Object.assign() is not supported in browser.
const objAss = { message: 'Hello you!' };
const newObjAss = Object.assign(objAss, { dev: true });
console.log(newObjAss);

// Babel will transpile with use to a helper function that first attempts to use Object.assign() and then falls back.
const objSpread = { message: 'Hello you!' };
const newObjSpread = {...objSpread, dev: true };
console.log(newObjSpread);

它们都产生相同的输出。

下面是从Babel到ES5的输出:

var objAss = { message: 'Hello you!' };
var newObjAss = Object.assign(objAss, { dev: true });
console.log(newObjAss);

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

var objSpread = { message: 'Hello you!' };
var newObjSpread = _extends({}, objSpread, { dev: true });
console.log(newObjSpread);

这是我目前的理解。object .assign()实际上是标准化的,其中随着对象扩展…还没有。唯一的问题是浏览器对前者以及未来对后者的支持。

玩一下这里的代码

希望这能有所帮助。

太多的错误答案……

我搜索了一下,发现了很多关于这方面的错误信息。

总结

既不……扩散也不反对。赋值更快。视情况而定。 对象。如果不考虑副作用/对象突变,Assign几乎总是更快。 除了性能之外,使用这两种方法通常都没有优点或缺点,直到遇到极限情况,例如复制包含getter /setter的对象或只读属性。点击这里阅读更多。

性能

是否对象。Assign或…传播速度更快,这取决于你想要组合的东西,以及你正在使用的运行时(实现和优化不时发生)。对于小的对象,这无关紧要。

对于较大的对象,请使用Object。Assign通常更好。特别是在不需要关心副作用的情况下,只需向第一个对象添加属性就可以节省时间,而不是复制两个对象并创建一个全新的对象。看到的:

async function retrieveAndCombine() {
    const bigPayload = await retrieveData()
    const smallPayload = await retrieveData2()
    
    // only the properties of smallPayload is iterated through
    // whereas bigPayload is mutated.
    return Object.assign(bigPayload, smallPayload)
}

如果有副作用的话

在副作用很重要的情况下,例如将一个对象与另一个对象组合作为参数传入。

在下面的例子中,bigPayload将被改变,如果retrieveAndCombine之外的其他函数/对象依赖于bigPayload,这是一个坏主意。在这种情况下,可以交换传递给Object的参数。赋值,或者直接使用{}作为第一个参数来创建一个新对象:

async function retrieveAndCombine(bigPayload) {
    const smallPayload = await retrieveData2()

    // this is slightly more efficient
    return Object.assign(smallPayload, bigPayload)

    // this is still okay assuming `smallPayload` only has a few properties
    return Object.assign({}, smallPayload, bigPayload)
}

Test

试试下面的代码片段。注意:运行需要一段时间。

const rand = () => (Math.random() + 1).toString(36).substring(7) function combineBigObjects() { console.log('Please wait...creating the test objects...') const obj = {} const obj2 = {} for (let i = 0; i < 100000; i++) { const key = rand() obj[rand()] = { [rand()]: rand(), [rand()]: rand(), } obj2[rand()] = { [rand()]: rand(), [rand()]: rand(), } } console.log('Combine big objects using spread:') console.time() const result1 = { ...obj, ...obj2, } console.timeEnd() console.log('Combine big objects using assign:') console.time() Object.assign({}, obj, obj2) console.timeEnd() console.log('Combine big objects using assign, but mutates first obj:') console.time() Object.assign(obj, obj2) console.timeEnd() } combineBigObjects() function combineSmallObjects() { const firstObject = () => ({ [rand()]: rand() }) const secondObject = () => ({ [rand()]: rand() }) console.log('Combine small objects using spread:') console.time() for (let i = 0; i < 500000; i++) { const finalObject = { ...firstObject(), ...secondObject(), } } console.timeEnd() console.log('Combine small objects using assign:') console.time() for (let i = 0; i < 500000; i++) { const finalObject = Object.assign({}, firstObject(), secondObject()) } console.timeEnd() console.log('Combine small objects using assign, but mutates first obj:') console.time() for (let i = 0; i < 500000; i++) { const finalObject = Object.assign(firstObject(), secondObject()) } console.timeEnd() } combineSmallObjects()

我认为展开运算符和对象的最大区别。当前答案中似乎没有提到的赋值是,展开操作符不会将源对象的原型复制到目标对象。如果你想给一个对象添加属性,而你又不想改变它的实例,那么你必须使用object .assign。

编辑:实际上我已经意识到我的例子是有误导性的。展开操作符糖化为Object。将第一个参数设置为空对象进行赋值。在下面的代码示例中,我将error作为对象的第一个参数。赋值调用,所以两者是不相等的。Object的第一个参数。Assign会被修改,然后返回这就是它保留原型的原因。我在下面又加了一个例子:

const error = new Error(); error instanceof Error // true const errorExtendedUsingSpread = { ...error, ...{ someValue: true } }; errorExtendedUsingSpread instanceof Error; // false // What the spread operator desugars into const errorExtendedUsingImmutableObjectAssign = Object.assign({}, error, { someValue: true }); errorExtendedUsingImmutableObjectAssign instanceof Error; // false // The error object is modified and returned here so it keeps its prototypes const errorExtendedUsingAssign = Object.assign(error, { someValue: true }); errorExtendedUsingAssign instanceof Error; // true

参见:https://github.com/tc39/proposal-object-rest-spread/blob/master/Spread.md

对象。当目标对象是一个常量并且您希望一次设置多个属性时,Assign是必要的。

例如:

const target = { data: "Test", loading: true }

现在,假设你需要用一个源的所有属性来改变目标:

const source = { data: null, loading: false, ...etc }

Object.assign(target, source) // Now target is updated
target = { ...target, ...source) // Error: cant assign to constant

请记住,您正在改变目标obj,所以尽可能使用Object。分配空目标或扩散分配给一个新的obj。