[譯] Object.assign 和 Object Spread 之爭, 用誰?
原文連結thecodebarbarian.com/object-assi…
在 2018 年Object Rest/Spread Proposal 達到了stage 4,這意味著在未來它會將入到 ECMAScript 標準中。它也被加入到Node LTS. Node.js 8 以後的版本你可以使用它,所以你可以放心地開始使用它。
Object Spread 也可以叫做物件展開符,下文都以 Object Spread 來進行描述。
$ node -v v8.9.4 $ node > const obj = { foo: 1, bar: 1 }; undefined > ({ ...obj, baz: 1 }); { foo: 1, bar: 1, baz: 1 } 複製程式碼
Object Spread 和 Object.assign 在功能上很相似。你應該使用哪一個? 事實證明,答案比你想象的要微妙許多。
Object Spread 概論
Object Spread 運算子的基本思想是使用現有物件的自身屬性來建立新的普通物件。 所以{...obj}
建立一個和obj
具有相同屬性的物件。 對於普通的舊 JavaScript 物件
,你實際上是在建立一個obj
副本。
const obj = { foo: 'bar' }; const clone = { ...obj }; // `{ foo: 'bar' }` obj.foo = 'baz'; clone.foo; // 'bar' 複製程式碼
與object .assign()類似,Object spread 操作符不復制繼承的屬性或類的屬性。但是它會複製 ES6 的symbols 屬性。
class BaseClass { foo() { return 1; } } class MyClass extends BaseClass { bar() { return 2; } } const obj = new MyClass(); obj.baz = function() { return 3; }; obj[Symbol.for('test')] = 4; // Does _not_ copy any properties from `MyClass` or `BaseClass` const clone = { ...obj }; console.log(clone); // { baz: [Function], [Symbol(test)]: 4 } console.log(clone.constructor.name); // Object console.log(clone instanceof MyClass); // false 複製程式碼
還可以使用 Object spread 操作符混合其他屬性。
順序問題: Object spread 操作符將覆蓋在它之前定義的屬性。
const obj = { a: 'a', b: 'b', c: 'c' }; { a: 1, b: null, c: void 0, ...obj }; // { a: 'a', b: 'b', c: 'c' } { a: 1, b: null, ...obj, c: void 0 }; // { a: 'a', b: 'b', c: undefined } { a: 1, ...obj, b: null, c: void 0 }; // { a: 'a', b: null, c: undefined } { ...obj, a: 1, b: null, c: void 0 }; // { a: 1, b: null, c: undefined } 複製程式碼
和 Object.assign() 的區別
對於上面的例子,Object.assign()
函式基本上可以與 Object spread 操作符互換。事實上,object spread spec
明確指出{... obj}
等同於Object.assign({},obj)
。
const obj = { a: 'a', b: 'b', c: 'c' }; Object.assign({ a: 1, b: null, c: void 0 }, obj); // { a: 'a', b: 'b', c: 'c' } Object.assign({ a: 1, b: null }, obj, { c: void 0 }); // { a: 'a', b: 'b', c: undefined } Object.assign({ a: 1 }, obj, { b: null, c: void 0 }); // { a: 'a', b: null, c: undefined } Object.assign({}, obj, { a: 1, b: null, c: void 0 }); // { a: 1, b: null, c: undefined } 複製程式碼
那麼你為什麼要使用其中一個呢?一個關鍵的區別是 Object spread 操作符總是給你一個POJO(Plain Ordinary JavaScript Object)。而Object.assign()
函式卻修改其第一個傳入物件obj
:
class MyClass { set val(v) { console.log('Setter called', v); return v; } } const obj = new MyClass(); Object.assign(obj, { val: 42 }); // Prints "Setter called 42" 複製程式碼
換句話說,Object.assign()
修改了一個物件,因此它可以觸發ES6 setter。如果你更喜歡使用immutable技術,那麼 Object spread 操作符就是你更好的選擇。使用Object.assign()
,你必須確保始終將空物件{}
作為第一個引數傳遞。
效能怎麼樣? 這是一些簡單的基準測試。如果將空物件作為第一個引數傳遞給Object.assign()
,看起來 Object spread 會更快,但除此之外它們是可互換的。
下面是一個使用Object.assign()
和in-place賦值的基準測試:
const Benchmark = require('benchmark'); const suite = new Benchmark.Suite; const obj = { foo: 1, bar: 2 }; suite. add('Object spread', function() { ({ baz: 3, ...obj }); }). add('Object.assign()', function() { Object.assign({ baz: 3 }, obj); }). on('cycle', function(event) { console.log(String(event.target)); }). on('complete', function() { console.log('Fastest is ' + this.filter('fastest').map('name')); }). run({ 'async': true }); 複製程式碼
在這種情況下,兩者是相似的:
Object spread x 3,170,111 ops/sec +-1.50% (90 runs sampled) Object.assign() x 3,290,165 ops/sec +-1.86% (88 runs sampled) Fastest is Object.assign() 複製程式碼
但是,一旦向Object.assign()
輸入一個空物件引數,物件擴充套件運算子就會更快
suite. add('Object spread', function() { ({ baz: 3, ...obj }); }). add('Object.assign()', function() { Object.assign({}, obj, { baz: 3 }); }) 複製程式碼
這是輸出:
Object spread x 3,065,831 ops/sec +-2.12% (85 runs sampled) Object.assign() x 2,461,926 ops/sec +-1.52% (88 runs sampled) Fastest is Object spread 複製程式碼
ESLint 配置
預設情況下,ESLint在解析層面禁止物件rest / spread運算子 你需要在.eslintrc.yml中將parserOptions.ecmaVersion選項設定為至少9,否則你將得到一個解析錯誤。
parserOptions: # Otherwise object spread causes 'Parsing error: Unexpected token ..' ecmaVersion: 9 複製程式碼
ESLint添加了一個新的規則
prefer-object-spread,它會強制你使用 Object spread 操作符 而不是Object.assign()
。 要啟用此規則,請使用:
parserOptions: ecmaVersion: 9 rules: prefer-object-spread: error 複製程式碼
現在,如果您使用object .assign()
而不是Object spread, ESLint將報告一個錯誤。
Use an object spread instead of `Object.assign` eg: `{ ...foo }`prefer-object-spread 複製程式碼
最後
Object rest / spread運算子在語法更加簡潔,並且比Object.assign()
提供了效能優勢。 如果你執行的是Node.js 8或更高版本,請嘗試使用這些新運算子,使程式碼更簡潔。