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}