天天看點

ES6躬行記(11)——對象

  在第5篇中,講解了多個對象字面量的改進,本節将重點介紹兩個新增的靜态方法,以及對象屬性的重複處理和枚舉順序。

一、Object.is()

  此方法用于判斷兩個值是否相同,内部實作了SameValue算法,其行為類似于全等(===)比較,但它認為兩個NaN是相等的,而+0和-0卻是不等的。Object.is()和全等的差別如下所示。

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

二、Object.assign()

  此方法可将多個對象合并成一個,它的第一個參數是目标對象,剩餘的參數都是源對象,傳回值是最終的目标對象,接下來會列舉出此方法的6個特點。

  (1)隻能拷貝可枚舉的自有屬性(定義在對象中),無法拷貝繼承屬性(定義在對象原型中)和不可枚舉的屬性。以下面的代碼為例,obj2對象中的name是繼承屬性,age是不可枚舉的屬性,隻有school是可枚舉的自有屬性,是以隻有school屬性拷貝到了空對象中。

var obj1 = { name: "strick" },
  obj2 = Object.create(obj1);        //name是繼承屬性
obj2.age = 28;                    //age是不可枚舉的屬性
Object.defineProperty(obj2, "age", {
  enumerable: false
});
obj2.school = "university";      //school是可枚舉的自有屬性
Object.assign({}, obj2);        //{school: "university"}      

  (2)遇到同名的屬性,後面的會覆寫之前的。例如下面的兩個對象obj1和obj2都包含name屬性,obj1在obj2之前傳到方法中,最終obj1對象的name屬性将被覆寫掉。

var obj1 = { name: "strick" },
  obj2 = { name: "freedom" };
Object.assign({}, obj1, obj2);       //{name: "freedom"}      

  (3)Object.assign()執行的是淺拷貝,如果屬性的值是對象,那麼隻會拷貝引用該對象的指針。在下面的代碼中,obj1對象的man屬性是一個對象,将其與空對象合并,然後把傳回值賦給obj2變量,再修改obj1中的name屬性,由于是淺拷貝,是以obj2中的name屬性也會受影響。

var obj1 = { man: { name: "strick" } },
  obj2 = Object.assign({}, obj1);
obj1.man.name = "freedom";
console.log(obj2);                  //{man: {name: "freedom"}}      

  (4)Symbol類型的屬性也能被拷貝。Symbol是ES6引入的第6種基本類型,可以像字元串那樣作為對象的屬性名,具體如下所示。

var obj1 = { [Symbol("name")]: "strick" },
  obj2 = Object.assign({}, obj1);
console.log(obj2);                  //{Symbol(name): "strick"}      

  (5)當源對象的位置是基本類型的值時,它們會被包裝成對象,再進行合并。但由于undefined和null沒有包裝對象,并且數字和布爾值的包裝對象又沒有可枚舉的屬性,是以隻有字元串的包裝對象才能不被忽略,最終以數組的形式拷貝到目标對象中,如下所示。

var obj = Object.assign({}, 1, "a", true, undefined, null);
console.log(obj);                   //{0: "a"}      

  (6)源對象的通路器屬性會變成目标對象的資料屬性。如下代碼所示,obj對象包含一個名為name的通路器屬性,在把它與空對象合并後,目标對象會有一個名為name的資料屬性,其值就是通路器屬性中get()方法的傳回值。

var obj = {
  get name() {
    return "strick";
  }
};
Object.assign({}, obj);            //{name: "strick"}      

三、重複屬性

  在ES5的嚴格模式中,重複的屬性名會引起文法錯誤。但ES6不會再做這個檢查,當出現重複屬性時,排在後面的同名屬性将覆寫前面的,即屬性值以後面的為準,執行過程如下所示。

var obj = {
  name: "strick",
  name: "freedom"
};
console.log(obj.name);             //"freedom"      

四、枚舉順序

  ES6規定了自有屬性的枚舉順序,并且會将同一類别的屬性整合到一塊,具體的排列規則如下所列:

(1)首先周遊數字類型或數字字元串的屬性,按大小升序排列。

(2)接着周遊字元串類型的屬性,按添加時間的先後順序排列。

(3)最後周遊符号類型的屬性,也按添加順序排列。

  for-in循環、JSON對象的序列化方法stringify()、Object對象的getOwnPropertyNames()、keys()、getOwnPropertySymbols()以及新引入的assign()方法在執行過程中都會遵循這套新的排列規則,具體如下所示。

var obj = {
  c: 1,
  1: 2,
  a: 3,
  "0": 4,
  [Symbol("x")]: 5,
  [Symbol("y")]: 6
};
var properties = [];
for(var key in obj) {
  if(obj.hasOwnProperty(key)) {          //過濾掉繼承屬性
    properties.push(key);
  }
}
console.log(properties);           //["0", "1", "c", "a"]
JSON.stringify(obj);             //{"0":4,"1":2,"c":1,"a":3}
Object.getOwnPropertyNames(obj);      //["0", "1", "c", "a"]
Object.keys(obj);               //["0", "1", "c", "a"]
Object.getOwnPropertySymbols(obj);     //[Symbol(x), Symbol(y)]
Object.assign({}, obj);           //{0: 4, 1: 2, c: 1, a: 3, Symbol(x): 5, Symbol(y): 6}