[譯] Object.assign 和 Object Spread 之爭, 用誰?

原文連接 thecodebarbarian.com/object-assi…javascript

在 2018 年 Object Rest/Spread Proposal 達到了 stage 4,這意味着在將來它會將入到 ECMAScript 標準中。它也被加入到Node LTS. Node.js 8 之後的版本你可使用它,因此你能夠放心地開始使用它。html

Object Spread 也能夠叫作對象展開符,下文都以 Object Spread 來進行描述。java

$ 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 在功能上很類似。你應該使用哪個? 事實證實,答案比你想象的要微妙許多。node

Object Spread 概論

Object Spread 運算符的基本思想是使用現有對象的自身屬性來建立新的普通對象。 因此{...obj} 建立一個和 obj 具備相同屬性的對象。 對於普通的舊 JavaScript 對象,你其實是在建立一個obj副本。git

const obj = { foo: 'bar' };
const clone = { ...obj }; // `{ foo: 'bar' }`
obj.foo = 'baz';
clone.foo; // 'bar'
複製代碼

與object .assign()相似,Object spread 操做符不復制繼承的屬性或類的屬性。可是它會複製 ES6 的 symbols 屬性。github

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 操做符混合其餘屬性。bash

順序問題: Object spread 操做符將覆蓋在它以前定義的屬性。async

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(),你必須確保始終將空對象{​​}做爲第一個參數傳遞。

2019.02.12 補充說明,當一個 Object 使用了 Object.defineProperty 修改了 set 方法,由於調用 Object.assign 會觸發 setter 方法,會觸發意想不到的錯誤。

性能怎麼樣? 這是一些簡單的基準測試。若是將空對象做爲第一個參數傳遞給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或更高版本,請嘗試使用這些新運算符,使代碼更簡潔。

更多請關注

友情連接: huayifeng.top/

相關文章
相關標籤/搜索