假设我有一个options
变量,我想设置一些默认值。
这两种选择的好处/缺点是什么?
使用对象传播
options = {...optionsDefault, ...options};
或使用Object.assign
options = Object.assign({}, optionsDefault, options);
这是让我惊讶的commit。
答案 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 ...
需要更少的代码来表达相同的想法答案 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()
实际上是标准化的,而对象传播...
尚未实现。唯一的问题是浏览器支持前者和未来,后者也是。
希望这有帮助。
答案 5 :(得分:9)
我想通过工具在浏览器和生态系统中总结“传播对象合并”ES功能的状态。
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呼叫身份,我们就会知道