ES6系列---對象功能擴展

ES6經過多種方式來增強對象的使用,經過簡單的語法擴展,提供更多操做對象及與對象交互的方法。javascript

對象字面量語法擴展

對象屬性初始值的簡寫

ES5中初始化屬性值的方式:java

function createPerson(name, age) {
    return {
        name: name,
        age: age
    };
}

這段代碼中的createPerson()函數建立的對象,其屬性名與函數的參數相同,在返回的結果中,nameage分別重複了兩遍,只是其中一個是對象屬性名,另外一個是爲屬性賦值的變量。編程

ES6中能夠簡單地只寫屬性名便可:函數

function createPerson(name, age) {
    return {
        name,
        age
    };
}

當對象字面量裏只有一個屬性的名稱時,JavaScript引擎會在可訪問做用域中查找同名變量;若是找到,則該變量的值被賦給對象字面量的同名屬性。this

對象方法的簡寫

ES5中定義對象方法:prototype

var person = {
    name: "Nicholas",
    sayName: function() {
        console.log(this.name);
    }
};

ES6中簡寫後的對象方法:指針

var person = {
    name: "Nicholas",
    sayName() {
        console.log(this.name);
    }
};

兩者惟一的區別是,簡寫方法可使用super關鍵字(稍後會討論)。code

可計算屬性名

ES5中,若是想要經過計算獲得屬性名,就須要使用方括號代替點記法,請看:對象

var person = {};
    lastName = "last name";
    
person["first name"] = "Nicholas";
person[lastName] = "Zakas";

console.log(person["first name"]);   // "Nicholas"
console.log(person[lastName]);   // "Zakas"

ES5在對象字面量中,也能夠直接使用字符串字面量做爲屬性名:繼承

var person = {
    "first name": "Nicholas"
};

console.log(person["first name"]);  // "Nicholas"

這種模式適用於屬性名提早已知或可被字符串字面量表示的狀況。然而,若是屬性名"first name"被包含在一個變量中(就像以前的示例中的那樣),或者須要經過計算才能獲得該變量的值,那麼ES5中是沒法爲一個對象字面量定義該屬性的。

而在ES6中,可在對象字面量中使用可計算屬性名,請看:

let lastName = "last name";

let person = {
    "first name": "Nicholas",
    [lastName]: "Zakas"
};

console.log(person["first name"]);  // "Nicholas"
console.log(person[lastName]);   // "Zakas"

在對象字面量中使用方括號表示該屬性名是可計算的,它的內容將被求值並最終轉化爲一個字符串,於是一樣可使用表達式做爲屬性的可計算名稱,例如:

var suffix = " name";

var person = {
    ["first" + suffix]: "Nicholas",
    ["last" + suffix]: "Zakas"
};

console.log(person["first name"]);  // "Nicholas"
console.log(person["last name"]);   // "Zakas"

新增方法

Object.is()方法

當你想在JavaScript中比較兩個值時,可能習慣於使用相等運算符(==)或全等運算符(===),許多開發者更喜歡後者,從而避免在比較時觸發強制類型轉換的行爲。但即便全等運算符也不徹底準確,好比+0和-0在JavaScript引擎中被表示爲兩個徹底不一樣的實體,而若是使用全等運算符===對二者進行比較,獲得的結果是二者相等;一樣,NaN===NaN的返回值爲false,須要使用isNaN()方法才能夠正確檢測NaN

ES6引入了Object.is()方法來彌補全等運算符的不許確運算。請看示例:

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

console.log(NaN == NaN);  // false
console.log(NaN === NaN);  // false
console.log(Object.is(NaN, NaN));  // true

consolog.log(5 == 5);   // true
consolog.log(5 == "5");   // true
consolog.log(5 === 5);   // true
consolog.log(5 === "5");   // false
console.log(Object.is(5, 5));   // true
console.log(Object.is(5, "5"));   // false

對於Object.is()方法來講,其運行結果在大部分狀況下與===運算符相同,惟一的區別在於+0和-0被識別爲不相等而且NaN與NaN等價。可是你大可沒必要拋棄等號運算符,是否選擇使用Object.is()方法而不是===取決於那些特殊狀況如何影響代碼。

Object.assign()方法

混合(Mixin)是JavaScript種實現對象組合最流行的一種模式。在一個mixin方法中,一個對象接收來自另外一個對象的屬性和方法,許多JavaScript庫中都有相似minxin方法:

function mixin(receiver, supplier) {
    Object.keys(supplier).forEach(function(key) {
        receiver[key] = supplier[key];
    });
    return receiver;
}

mixin()函數遍歷supplier的自有屬性並複製到receiver中(複製行爲是淺複製,當屬性值爲對象時只複製對象的引用)。這樣一來,receiver不經過繼承就能夠得到新屬性,請參考這段代碼:

function EventTarget() { ... }
EventTarget.prototype = {
    constructor: EventTarget,
    emit: function() { ... },
    on: function() { ... }
};

var myObject = {};
mixin(myObject, EventTarget.prototype);

myObject.emit("somethingChanged");

這種混合模式很是流行,於是ES6添加了Object.assign()方法來實現相同的功能:

Object.assign(myObject, EventTarget.prototype);

Object.assign()方法接受一個接收對象和任意數量的源對象,最終返回接收對象。若是多個源對象具備相同屬性,則排位靠後的會覆蓋排位靠前的。

須要注意的是,Object.assign()方法不能將提供者的訪問器屬性複製到接收對象中。因爲Object.assign()方法執行了賦值操做,所以提供者的訪問器屬性最終會轉變爲接收對象中的一個數據屬性,請看示例:

var receiver = {},
    supplier = {
        get name() {
            return "file.js";
        }
    };
    
Object.assign(receiver, supplier);

var descriptor = Object.getOwnPropertyDescriptor(receiver, "name");

console.log(descriptor.value);   // "file.js"
console.log(descriptor.get);     // undefined

在這段代碼中,supplier有一個名爲name的訪問器屬性。當調用Object.assign()方法時返回字符串"file.js",所以receiver接收這個字符串後將其存爲數據屬性receiver.name

加強對象原型

改變對象的原型

正常狀況下,不管是經過構造函數仍是Object.create()方法建立對象,其原型是在被建立時指定的。對象原型在實例化以後保持不變,直到ES5都是JavaScript編程最重要的設定之一,雖然在ES5中添加了Object.getPrototypeOf()方法來返回任意指定對象的原型,但仍缺乏對象在實例化後改變原型的標準方法。

因此,在ES6中添加了Object.setPrototypeOf()方法來改變這一現狀:

let person = {
    getGreeting() {
        return "Hello";
    }
};

let dog = {
    getGreeting() {
        return "Woof";
    }
};

// 以person對象爲原型
let friend = Object.create(person);
console.log(friend.getGreeting());     // "Hello"
console.log(Object.getPrototypeOf(friend) === person);   // true

// 將原型設置爲dog
console.log(friend.getGreeting());   // "Woof"
console.log(Object.getPrototypeOf(friend) === dog);   // true

這段代碼定義了兩個基對象:persondog。兩者多有getGreeting()方法。friend對象先繼承person對象,調用getGreeting()方法輸出"Hello";當原型被變動爲dog對象時,原先與person對象的關聯被解除,調用person.getGreeting()方法時輸出的內容就變爲了"Woof"。

對象原型的真實值被存儲在內部專用屬性[[Prototype]]中,調用Object.getPrototypeOf()方法返回存儲在其中的值,調用Object.setPrototypeOf()方法改變其中的值。然而,這不是操做[[Prototype]]值的惟一方法。

用super簡化原型訪問

ES6引入了super引用的特性,使用它能夠更便捷地訪問對象原型。舉個例子,若是你想重寫對象實例方法,又須要調用與它同名的原型方法,先看看ES5中的實現:

let person = {
    getGreeting() {
        return "Hello";
    }
};

let dog = {
    getGreeting() {
        return "Woof";
    }
};

let friend = {
    getGreeting() {
        return Object.getPrototypeOf(this).getGreeting.call(this) + ", hi!";
    }
};

// 將原型設置爲`person`
Object.setPrototypeOf(friend, person);
console.log(friend.getGreeting());   // "Hello, hi!"
console.log(Object.getPrototypeOf(friend) === person);   // true

// 將原型設置爲`dog`
Object.setPrototypeOf(friend, dog);
console.log(friend.getGreeting());   // "Woof, hi!"
console.log(Object.getPrototypeOf(friend) === dog);   // true

在這個示例中,friend對象的getGreeting()方法調用了同名的原型方法。Object.getPrototypeOf()方法能夠確保調用正確的原型,並向輸出字符串疊加另外一個字符串;後面的.call(this)能夠確保正確設置原型方法中的this值。

要準確記得如何使用Object.getPrototypeOf()方法和.call(this)方法來調用原型上的方法實在有些複雜,因此ES6引入了super關鍵字。super引用至關於指向對象原型的指針:

let friend = {
    getGreeting() {
        // return Object.getPrototypeOf(this).getGreeting.call(this) + ", hi!";
        return sper.getGreeting() + ", hi!";
    }
};
相關文章
相關標籤/搜索