ES6:對象新增方法


ES6---對象新增方法

1.Object.is()

ES5 比較兩個值是否相等,只有兩個運算符:相等運算符()和 嚴格相等運算符(=)。它們都有缺點,前者會自動轉換數據類型,后者的NaN不等於自身,以及+0等於-0。JavaScript 缺乏一種運算,在所有環境中,只要兩個值是一樣的,它們就應該相等。

ES6 提出“Same-value equality”(同值相等)算法,用來解決這個問題。Object.is就是部署這個算法的新方法。它用來比較兩個值是否嚴格相等,與嚴格比較運算符(===)的行為基本一致。

Object.is('foo', 'foo')
// true
Object.is({}, {})
// false

不同之處只有兩個:一是+0不等於-0,二是NaN等於自身。

+0 === -0 //true
NaN === NaN // false

Object.is(+0, -0) // false
Object.is(NaN, NaN) // true

ES5 可以通過下面的代碼,部署Object.is

Object.defineProperty(Object, 'is', {
  value: function(x, y) {
    if (x === y) {
      // 針對+0 不等於 -0的情況
      return x !== 0 || 1 / x === 1 / y;
    }
    // 針對NaN的情況
    return x !== x && y !== y;
  },
  configurable: true,
  enumerable: false,
  writable: true
});

2.Object.assign()

Object.assign()方法用於對象的合並,將源對象(source)的所有可枚舉屬性,復制到目標對象(target)。

const target = { a: 1 };

const source1 = { b: 2 };
const source2 = { c: 3 };

Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

Object.assign()方法的第一個參數是目標對象,后面的參數都是源對象。

注意,如果目標對象與源對象有同名屬性,或多個源對象有同名屬性,則后面的屬性會覆蓋前面的屬性。

const target = { a: 1, b: 1 };

const source1 = { b: 2, c: 2 };
const source2 = { c: 3 };

Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

如果只有一個參數,Object.assign()會直接返回該參數。

const obj = {a: 1};
Object.assign(obj) === obj // true

如果該參數不是對象,則會先轉成對象,然后返回。

typeof Object.assign(2) // "object"

由於undefined和null無法轉成對象,所以如果它們作為參數,就會報錯。

Object.assign(undefined) // 報錯
Object.assign(null) // 報錯

其他類型的值(即數值、字符串和布爾值)不在首參數,也不會報錯。但是,除了字符串會以數組形式,拷貝入目標對象,其他值都不會產生效果。

const v1 = 'abc';
const v2 = true;
const v3 = 10;

const obj = Object.assign({}, v1, v2, v3);
console.log(obj); // { "0": "a", "1": "b", "2": "c" }

上面代碼中,v1、v2、v3分別是字符串、布爾值和數值,結果只有字符串合入目標對象(以字符數組的形式),數值和布爾值都會被忽略。這是因為只有字符串的包裝對象,會產生可枚舉屬性。
Object.assign()拷貝的屬性是有限制的,只拷貝源對象的自身屬性(不拷貝繼承屬性),也不拷貝不可枚舉的屬性(enumerable: false)。

注意點:

1.Object.assign()方法實行的是淺拷貝,而不是深拷貝。也就是說,如果源對象某個屬性的值是對象,那么目標對象拷貝得到的是這個對象的引用。
2.對於這種嵌套的對象,一旦遇到同名屬性,Object.assign()的處理方法是替換,而不是添加。
3.Object.assign()可以用來處理數組,但是會把數組視為對象。
4.Object.assign()只能進行值的復制,如果要復制的值是一個取值函數,那么將求值后再復制。

常見用途:

1.為對象添加屬性

class Point {
  constructor(x, y) {
    Object.assign(this, {x, y});
  }
}

上面方法通過Object.assign()方法,將x屬性和y屬性添加到Point類的對象實例。
2.為對象添加方法

Object.assign(SomeClass.prototype, {
  someMethod(arg1, arg2) {
    ···
  },
  anotherMethod() {
    ···
  }
});

// 等同於下面的寫法
SomeClass.prototype.someMethod = function (arg1, arg2) {
  ···
};
SomeClass.prototype.anotherMethod = function () {
  ···
};

上面代碼使用了對象屬性的簡潔表示法,直接將兩個函數放在大括號中,再使用assign()方法添加到SomeClass.prototype之中。

4.克隆對象

function clone(origin) {
  return Object.assign({}, origin);
}

上面代碼將原始對象拷貝到一個空對象,就得到了原始對象的克隆。

不過,采用這種方法克隆,只能克隆原始對象自身的值,不能克隆它繼承的值。如果想要保持繼承鏈,可以采用下面的代碼.

function clone(origin) {
  let originProto = Object.getPrototypeOf(origin);
  return Object.assign(Object.create(originProto), origin);
}
//The Object.create() method creates a new object, using an existing object as the prototype of the newly created object.

4.合並多個對象
將多個對象合並到某個對象。

const merge =
  (target, ...sources) => Object.assign(target, ...sources);

如果希望合並后返回一個新對象,可以改寫上面函數,對一個空對象合並。

const merge =
  (...sources) => Object.assign({}, ...sources);

5.為屬性指定默認值

const DEFAULTS = {
  logLevel: 0,
  outputFormat: 'html'
};

function processContent(options) {
  options = Object.assign({}, DEFAULTS, options);
  console.log(options);
  // ...
}

上面代碼中,DEFAULTS對象是默認值,options對象是用戶提供的參數。Object.assign()方法將DEFAULTS和options合並成一個新對象,如果兩者有同名屬性,則options的屬性值會覆蓋DEFAULTS的屬性值。

注意,由於存在淺拷貝的問題,DEFAULTS對象和options對象的所有屬性的值,最好都是簡單類型,不要指向另一個對象。否則,DEFAULTS對象的該屬性很可能不起作用。

3.Object.getOwnPropertyDescriptors()

ES5 的Object.getOwnPropertyDescriptor()方法會返回某個對象屬性的描述對象(descriptor)。ES2017 引入了Object.getOwnPropertyDescriptors()方法,返回指定對象所有自身屬性(非繼承屬性)的描述對象。

const obj = {
  foo: 123,
  get bar() { return 'abc' }
};

Object.getOwnPropertyDescriptors(obj)
// { foo:
//    { value: 123,
//      writable: true,
//      enumerable: true,
//      configurable: true },
//   bar:
//    { get: [Function: get bar],
//      set: undefined,
//      enumerable: true,
//      configurable: true } }

上面代碼中,Object.getOwnPropertyDescriptors()方法返回一個對象,所有原對象的屬性名都是該對象的屬性名,對應的屬性值就是該屬性的描述對象。

該方法的實現非常容易。

function getOwnPropertyDescriptors(obj) {
  const result = {};
  for (let key of Reflect.ownKeys(obj)) {
    result[key] = Object.getOwnPropertyDescriptor(obj, key);
  }
  return result;
}
//靜態方法 Reflect.ownKeys() 返回一個由目標對象自身的屬性鍵組成的數組。

該方法的引入目的,主要是為了解決Object.assign()無法正確拷貝get屬性和set屬性的問題

const source = {
  set foo(value) {
    console.log(value);
  }
};

const target1 = {};
Object.assign(target1, source);

Object.getOwnPropertyDescriptor(target1, 'foo')
// { value: undefined,
//   writable: true,
//   enumerable: true,
//   configurable: true }

上面代碼中,source對象的foo屬性的值是一個賦值函數,Object.assign方法將這個屬性拷貝給target1對象,結果該屬性的值變成了undefined。這是因為Object.assign方法總是拷貝一個屬性的值,而不會拷貝它背后的賦值方法或取值方法。

這時,Object.getOwnPropertyDescriptors()方法配合Object.defineProperties()方法,就可以實現正確拷貝。

4.__proto__屬性,Object.setPrototypeOf(),Object.getPrototypeOf()

1.__proto__屬性

__proto__屬性(前后各兩個下划線),用來讀取或設置當前對象的原型對象(prototype)。目前,所有瀏覽器(包括 IE11)都部署了這個屬性。

// es5 的寫法
const obj = {
  method: function() { ... }
};
obj.__proto__ = someOtherObj;

// es6 的寫法
var obj = Object.create(someOtherObj);
obj.method = function() { ... };

該屬性沒有寫入 ES6 的正文,而是寫入了附錄,原因是__proto__前后的雙下划線,說明它本質上是一個內部屬性,而不是一個正式的對外的 API,只是由於瀏覽器廣泛支持,才被加入了 ES6。標准明確規定,只有瀏覽器必須部署這個屬性,其他運行環境不一定需要部署,而且新的代碼最好認為這個屬性是不存在的。因此,無論從語義的角度,還是從兼容性的角度,都不要使用這個屬性,而是使用下面的Object.setPrototypeOf()(寫操作)、Object.getPrototypeOf()(讀操作)、Object.create()(生成操作)代替。
實現上,__proto__調用的是Object.prototype.proto,具體實現如下。

Object.defineProperty(Object.prototype, '__proto__', {
  get() {
    let _thisObj = Object(this);
    return Object.getPrototypeOf(_thisObj);
  },
  set(proto) {
    if (this === undefined || this === null) {
      throw new TypeError();
    }
    if (!isObject(this)) {
      return undefined;
    }
    if (!isObject(proto)) {
      return undefined;
    }
    let status = Reflect.setPrototypeOf(this, proto);
    if (!status) {
      throw new TypeError();
    }
  },
});

function isObject(value) {
  return Object(value) === value;
}

2.Object.setPrototypeOf()

Object.setPrototypeOf方法的作用與__proto__相同,用來設置一個對象的原型對象(prototype),返回參數對象本身。它是 ES6 正式推薦的設置原型對象的方法。

// 格式
Object.setPrototypeOf(object, prototype) //設置object的原型對象是prototype,了解即可

// 用法
const o = Object.setPrototypeOf({}, null);

3.Object.getPrototypeOf()

該方法與Object.setPrototypeOf方法配套,用於讀取一個對象的原型對象。

function Rectangle() {
  // ...
}

const rec = new Rectangle();

Object.getPrototypeOf(rec) === Rectangle.prototype
// true

Object.setPrototypeOf(rec, Object.prototype);
Object.getPrototypeOf(rec) === Rectangle.prototype
// false

參考資料:

ES6對象新增方法


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM