假设我有一个选项变量我想设置一个默认值。
这两种选择的优点/缺点是什么?
使用对象扩展
options = {...optionsDefault, ...options};
或者使用Object.assign
options = Object.assign({}, optionsDefault, options);
这是让我疑惑的承诺。
假设我有一个选项变量我想设置一个默认值。
这两种选择的优点/缺点是什么?
使用对象扩展
options = {...optionsDefault, ...options};
或者使用Object.assign
options = Object.assign({}, optionsDefault, options);
这是让我疑惑的承诺。
当前回答
作为参考,对象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}
其他回答
正如其他人所提到的,在撰写本文时,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()
我们将创建一个名为identity的函数,它只返回我们给它的任何参数。
identity = (arg) => arg
和一个简单的数组。
arr = [1, 2, 3]
如果用arr求恒等函数,我们知道会发生什么
其他答案都老了,得不到好的答案。 下面的例子是对象字面量,有助于两者如何相互补充,以及如何不能相互补充(因此存在差异):
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
(1)创建对象的浅副本和(2)将多个对象合并为单个对象的方法在2014年至2018年之间有了很大的发展。
下面概述的方法在不同时期变得可用并广泛使用。这个答案提供了一些历史视角,但并不详尽。
如果没有库或现代语法的帮助,您将使用for-in循环,例如。 var mergedOptions = {} for (var键在defaultOptions) { mergedOptions[key] = defaultOptions[key] } 对于(var键在选项中){ mergedOptions[key] = options[key] } options = mergedOptions
2006
jQuery 1.0有jQuery.extend(): 选项= $。extend({}, defaultOptions, options)
⋮
2010
下划线。js 1.0有_.extend() 选项= _。extend({}, defaultOptions, options)
⋮
2014
2ality发布了一篇关于Object.assign()加入ES2015的文章 对象分配发布到npm。 var objectAssign = require('object-assign') options = objectAssign({}, defaultOptions, options) ES2016提出的对象Rest/扩展属性语法。
2015
对象。Chrome (45), Firefox(34)和Node.js(4)都支持assign。不过,旧的运行时需要Polyfill。 options =对象。assign({}, defaultOptions, options) 对象Rest/Spread Properties提案进入第二阶段。
2016
对象Rest/扩展属性语法没有被包含在ES2016中,但是提案已经到了第三阶段。
2017
对象Rest/Spread属性语法没有包含在ES2017中,但在Chrome (60), Firefox(55)和Node.js(8.3)中可用。不过,旧的运行时需要一些编译。 选项={…defaultOptions,…选择}
2018
对象Rest/Spread Properties提案达到了第4阶段,语法被包含在ES2018标准中。