ES6
經過多種方式來增強對象的使用,經過簡單的語法擴展,提供更多操做對象及與對象交互的方法。javascript
ES5中初始化屬性值的方式:java
function createPerson(name, age) { return { name: name, age: age }; }
這段代碼中的createPerson()
函數建立的對象,其屬性名與函數的參數相同,在返回的結果中,name
和age
分別重複了兩遍,只是其中一個是對象屬性名,另外一個是爲屬性賦值的變量。編程
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"
當你想在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()
方法而不是===取決於那些特殊狀況如何影響代碼。
混合(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
這段代碼定義了兩個基對象:person
和dog
。兩者多有getGreeting()
方法。friend
對象先繼承person
對象,調用getGreeting()
方法輸出"Hello";當原型被變動爲dog
對象時,原先與person
對象的關聯被解除,調用person.getGreeting()
方法時輸出的內容就變爲了"Woof"。
對象原型的真實值被存儲在內部專用屬性[[Prototype]]
中,調用Object.getPrototypeOf()
方法返回存儲在其中的值,調用Object.setPrototypeOf()
方法改變其中的值。然而,這不是操做[[Prototype]]
值的惟一方法。
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!"; } };