对象传播与Object.assign

时间:2015-10-03 17:33:23

标签: javascript ecmascript-6

假设我有一个options变量,我想设置一些默认值。

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

使用对象传播

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

或使用Object.assign

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

这是让我惊讶的commit

12 个答案:

答案 0 :(得分:261)

这不一定是详尽无遗的。

传播语法

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

优点:

  • 如果创作代码在没有本机支持的环境中执行,您可能只需编译此语法(而不是使用polyfill)。 (例如,使用Babel。)

  • 少详细。

缺点:

  • 最初编写此答案时,这是proposal,未标准化。在使用提案时,请考虑如果您现在编写代码时要执行的操作,并且在向标准化过程中不会进行标准化或更改。这已经在ES2018中标准化了。

  • 字面意思,不是动态的。

Object.assign()

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

优点:

  • 标准化。

  • 动态。例如:

    var sources = [{a: "A"}, {b: "B"}, {c: "C"}];
    options = Object.assign.apply(Object, [{}].concat(sources));
    // or
    options = Object.assign({}, ...sources);
    

缺点:

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

这是让我惊讶的提交。

这与您提出的问题没有直接关系。该代码并未使用Object.assign(),而是使用用户代码(object-assign)执行相同的操作。他们似乎正在用Babel编译代码(并将其与Webpack捆绑在一起),这就是我所说的:你可以编译的语法。他们显然更倾向于将object-assign作为依赖包含在他们的构建中。

答案 1 :(得分:121)

参考对象休息/传播在ECMAScript 2018中作为第4阶段最终确定。可以找到提案here

对于大部分对象重置和传播工作方式相同,关键区别在于spread defines properties, whilst Object.assign() sets them。这意味着Object.assign()会触发setter。

值得记住的是,除此之外,对象rest / spread 1:1映射到Object.assign()并且对数组(可迭代)传播的行为不同。例如,在传播数组时,传播空值。但是,使用对象传播空值会无声地传播到任何内容。

数组(可迭代)传播示例

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

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

对象展开示例

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

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

这与Object.assign()的工作方式一致,都默默地排除null值而没有错误。

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

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

答案 2 :(得分:14)

我认为在当前答案中似乎没有提到的传播算子和Object.assign之间的一个大差异是传播算子不会使原型保持完整。如果要向对象添加属性,并且不想更改对象的实例,则必须使用Object.assign。下面的示例应证明这一点:

const error = new Error();
console.error instanceof Error; // true

const errorExtendedUsingSpread = {
  ...error,
  ...{
    someValue: true
  }
};
errorExtendedUsingSpread instanceof Error; // false

const errorExtendedUsingAssign = Object.assign(error, {
  someValue: true
});
errorExtendedUsingAssign instanceof Error; // true

答案 3 :(得分:12)

对象传播运算符(...)在浏览器中不起作用,因为它不是任何ES规范的一部分,只是一个提议。唯一的选择是用Babel(或类似的东西)编译它。

正如您所看到的,它只是Object.assign({})的语法糖。

据我所知,这些是重要的差异。

  • Object.assign适用于大多数浏览器(无需编译)
  • 对象的
  • ...未标准化
  • ...可以防止您意外改变对象
  • ...会在没有它的浏览器中填充Object.assign
  • ...需要更少的代码来表达相同的想法

答案 4 :(得分:11)

正如其他人所提到的,在写作的这一刻,Object.assign()需要一个polyfill和对象传播...需要一些转换(也可能是polyfill)才能工作。

请考虑以下代码:

// 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()实际上是标准化的,而对象传播...尚未实现。唯一的问题是浏览器支持前者和未来,后者也是。

Play with the code here

希望这有帮助。

答案 5 :(得分:9)

我想通过工具在浏览器和生态系统中总结“传播对象合并”ES功能的状态。

规格

浏览器:在Chrome中,在SF中,Firefox很快(版本60,IIUC)

  • 浏览器支持“传播属性”shipped in Chrome 60,包括此方案。
  • 对此方案的支持在当前的Firefox(59)中不起作用,但是在我的Firefox Developer Edition中可以使用。所以我相信它将在Firefox 60中发布。
  • Safari:未经测试,但Kangax表示它适用于Desktop Safari 11.1,但不适用于SF 11
  • iOS Safari:没有尝试,但Kangax表示它适用于iOS 11.3,但不适用于iOS 11
  • 不在Edge中

工具:节点8.7,TS 2.1

链接

代码示例(兼容兼容性测试)

var x = { a: 1, b: 2 };
var y = { c: 3, d: 4, a: 5 };
var z = {...x, ...y};
console.log(z); // { a: 5, b: 2, c: 3, d: 4 }

再次:在撰写本文时,此示例无需在Chrome(60+),Firefox Developer Edition(Firefox 60预览版)和Node(8.7 +)中进行转换。

为什么要回答?

我在原始问题之后写了2.5 。但我有同样的问题,这就是谷歌发给我的地方。我是SO改善长尾的使命的奴隶。

由于这是“数组传播”语法的扩展,我发现谷歌很难找到,而且在兼容性表中很难找到。我能找到的最接近的是Kangax "property spread",但该测试在同一个表达式中没有两个点差(不是合并)。此外,提案/草稿/浏览器状态页面中的名称都使用“属性传播”,但在我看来,这是社区在使用“对象合并”的扩展语法后提出的“第一主体”。 (这可能解释了为什么谷歌这么难。)所以我在这里记录我的发现,以便其他人可以查看,更新和编译有关此特定功能的链接。我希望它能够抓住。请帮助传播它在规范和浏览器中登陆的消息。

最后,我会将此信息添加为评论,但我无法在不破坏作者原始意图的情况下编辑它们。具体来说,我不能编辑@ChillyPenguin的评论而不会失去纠正@RichardSchulte的意图。但多年后理查德证明是对的(在我看来)。所以我写了这个答案,希望它最终可以获得旧答案的牵引力(可能需要数年,但毕竟这就是长尾效果的全部内容)。

答案 6 :(得分:7)

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

Object.assign将setter应用于新对象,Spread不应用。此外,该对象必须是可迭代的。

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

用它来创建对象的浅表副本 总是将不可变属性设置为复制的好习惯 - 因为可变版本可以传递给不可变属性,复制将确保您将始终处理不可变对象

指定 分配与复制有些相反。 Assign将生成一个setter,它直接将值赋给实例变量,而不是复制或保留它。 调用assign属性的getter时,它返回对实际数据的引用。

答案 7 :(得分: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

答案 8 :(得分:0)

现在这是ES6的一部分,因此是标准化的,并且还记录在MDN上: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator

使用起来非常方便,并且在对象解构的同时也很有意义。

上面列出的另一个优点是Object.assign()的动态功能,但这就像在文字对象中传播数组一样简单。在编译的babel输出中,它完全使用了Object.assign()

所展示的内容

所以正确的答案是使用对象传播,因为它现在已经标准化,广泛使用(参见react,redux等),易于使用,并具有Object.assign()的所有功能

答案 9 :(得分:0)

当您必须使用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

答案 10 :(得分:0)

spread运算符将Array扩展为函数的单独参数。

let iterableObjB = [1,2,3,4]
function (...iterableObjB)  //turned into
function (1,2,3,4)

答案 11 :(得分:-6)

我们将创建一个称为身份的函数,该函数将仅返回我们提供的任何参数。

identity = (arg) => arg

还有一个简单的数组。

arr = [1, 2, 3]

如果您使用arr呼叫身份,我们就会知道