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

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

使用对象扩展

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

或者使用Object.assign

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

这是让我疑惑的承诺。


当前回答

太多的错误答案……

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

总结

既不……扩散也不反对。赋值更快。视情况而定。 对象。如果不考虑副作用/对象突变,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时,我想添加这个简单的例子。

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

对象。当目标对象是一个常量并且您希望一次设置多个属性时,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。

这并不一定详尽无遗。

传播的语法

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

优点:

如果在没有本机支持的环境中编写代码以执行,则可以只编译此语法(而不是使用polyfill)。(比如巴别塔。) 可以减少复杂性。

缺点:

当这个答案最初被写出来的时候,这是一个建议,没有标准化。在使用提案时,请考虑如果您现在使用提案编写代码,而它没有得到标准化或在向标准化发展的过程中发生变化,该怎么办。这已经在ES2018中标准化了。 字面的,不是动态的。


Object.assign ()

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

优点:

标准化。 动态的。例子: var sources = [{a: " a "}, {b: " b "}, {c: " c "}]; options = Object.assign。应用(对象,({}).concat(来源)); / /或 options =对象。分配({},…来源);

缺点:

更详细的。 如果在没有本机支持的环境中编写代码执行,则需要填充。


这是让我疑惑的承诺。

这和你问的问题没有直接关系。这段代码没有使用Object.assign(),而是使用用户代码(object-assign)来做同样的事情。他们似乎在用Babel编译代码(并将其与Webpack捆绑),这就是我所说的:你可以编译的语法。他们显然更喜欢这样,而不是必须将对象赋值作为一个依赖项包含到他们的构建中。

作为参考,对象rest/spread在ECMAScript 2018中作为第4阶段完成。提案可以在这里找到。

在大多数情况下,对象赋值和扩展的工作方式是相同的,关键的区别是扩展定义属性,而object .assign()设置属性。这意味着Object.assign()触发设置器。

值得记住的是,除此之外,对象rest/spread 1:1映射到object .assign(),与数组(可迭代)spread的作用不同。例如,当展开一个数组时,将展开空值。然而,使用对象传播时,空值会被无声地传播到空值。

数组(可迭代)扩展示例

const x = [1, 2, null , 3];
const y = [...x, 4, 5];
const z = null;

console.log(y); // [1, 2, null, 3, 4, 5];
console.log([...z]); // TypeError

对象扩展示例

const x = null;
const y = {a: 1, b: 2};
const z = {...x, ...y};

console.log(z); //{a: 1, b: 2}

这与Object.assign()的工作方式是一致的,两者都无声地排除空值而没有错误。

const x = null;
const y = {a: 1, b: 2};
const z = Object.assign({}, x, y);

console.log(z); //{a: 1, b: 2}

其他答案都老了,得不到好的答案。 下面的例子是对象字面量,有助于两者如何相互补充,以及如何不能相互补充(因此存在差异):

var obj1 = { a: 1,  b: { b1: 1, b2: 'b2value', b3: 'b3value' } };

// overwrite parts of b key
var obj2 = {
      b: {
        ...obj1.b,
        b1: 2
      }
};
var res2 = Object.assign({}, obj1, obj2); // b2,b3 keys still exist
document.write('res2: ', JSON.stringify (res2), '<br>');
// Output:
// res2: {"a":1,"b":{"b1":2,"b2":"b2value","b3":"b3value"}}  // NOTE: b2,b3 still exists

// overwrite whole of b key
var obj3 = {
      b: {
        b1: 2
      }
};
var res3 = Object.assign({}, obj1, obj3); // b2,b3 keys are lost
document.write('res3: ', JSON.stringify (res3), '<br>');
// Output:
  // res3: {"a":1,"b":{"b1":2}}  // NOTE: b2,b3 values are lost

这里还有几个小例子,同样是数组和对象: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax