擴展物件的功能
- 普通物件 具有 JavaScript 物件所有預設內部行為的
- 特異物件 具有某些與預設行為不符的內部行為
- 標準物件 ES6 規範中定義的物件,例如 Array/Date 等
- 內建物件 腳本開始執行時存在於 JavaScript 執行環境的物件,所有標準物件都是內建物件
物件字面值語法擴展
- 屬性初始值的簡寫,當一個物件的屬性與本地變數同名時,不必再寫冒號和值
- 物件方法的簡寫語法
- 可計算屬性名
// javascript 引擎會在可訪問作用域中查找其同名變量;找到,則該量變的值被賦給對象字面量里的同名屬性,在現在的js開發中,為對象字面量的屬性賦同名局部變量的值是一種常見的做法,這種簡寫方法有助於消除命名錯誤function createPerson(name,age){ return { name, age }}// 對象方法的簡寫 消除冒號和function關鍵字。如下var person = { name:"Nicholas", sayName(){ console.log(this.name) }}// 通過對象方法簡寫語法創建的方法有一個name屬性,其值為小括號前的名稱,上例中person.sayName()方法的name屬性的值為"sayName"ES5 有些屬性名要透過[]方括號來設定和存取其值,例如
var person = {},lastName = "last name"
person["first name"] = "Nicholas"
person[lastName] = "Zakas"
console.log(person["first name"]); // Nicholas
console.log(person[lastName]); //Zakas
因為屬性名稱中都含有空格,因此不可使用點的方式引用,卻可以使用方括號,因為它支援透過任何字串值作為名稱存取屬性值。
在物件字面值中,可以直接使用字串字面值作為屬性名稱,如下
var person = {
"first name": "Nicholas"
}
console.log(person["first name"]); // "Nicholas"
// Es5 無法為通過計算得到的變量值,作為對象的字面量定義該屬性
// es6 通過中括號方式可以定義這樣的字面量
let lastName = "last name";
let person = {
"first name": "Nicholas",
[lastName]:"Zakas"
}
console.log(person["first name"]); // Nicholas
console.log(person[lastName]); // Zakas
console.log(person["last name"]) // Zakas
Object.is()
該方法用來彌補全等運算的不準確性。這個方法接受兩個參數,如果這兩個參數類型相同且有相同的值,則返回 true。
Object.is(NaN,NaN) // true
NaN === Nan //false
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) //true
console.log(Object.is(NaN,Nan)) //true
console.log(Object.is(5,5)) //true
console.log(Object.is(5,"5")) //false
Object.assign()
混合(Mixin)是 JavaScript 中實現物件組合最流行的一種模式。在一個 mixin 方法中,一個物件接收來自另一個物件的屬性與方法,許多 JavaScript 函式庫中都有類似的 mixin 方法:
// 淺複製
function minxin(receiver, supplier){
Object.keys(supplier).forEach(function(key){
receiver[key] = supplier[key]
})
return receiver
}
Object.assign() 方法來實現相同的功能,這個方法接受一個接收物件和任意數量的來源物件,mixin() 方法使用賦值運算子=來複製相關屬性,卻不能複製存取器屬性到接收物件中,因此最終添加的方法棄用mixin而改用 assign 作為方法名。
Object.assign() 方法可以接受任意數量的來源物件,並按指定順序將屬性複製到接收物件中。如果多個來源物件具有同名屬性,則排位靠後的來源物件會覆蓋排位靠前的。
Object.assign()方法不能將提供者的存取器屬性複製到接收物件中。由於Object.assign()方法執行了賦值操作,因此提供者的存取器屬性最終會轉變為接收物件中的一個資料屬性。
var receiver = {},supplier = {
get name() {
return "file.js"
}
};
Object.assign(receiver,supplier);
var descriptor = Object.getOwnPropertyDescriptior(receiver,"name")
console.log(descriptor.value); //file.js
console.log(descriptor.get); // undefined
重複的物件字面值屬性
ES5 屬性重名會報錯,ES6 不再做這樣強制性的約束,對於每一組重複屬性,都會取最後一個取值。
自有屬性列舉順序
ES6 嚴格規定了物件的自有屬性被列舉時的返回順序,這會影響到Object.getOwnPropertyName() 方法及Reflect.ownKeys 返回屬性的方式,Object.assign() 方法處理屬性的順序也將隨之改變,規則如下:
- 所有數字鍵按升序排序
- 所有字串鍵按照它們被加入物件的順序排序
- 所有 Symbol 鍵,按照它們被加入物件的順序排序。
增強物件原型
ES5 都是 JavaScript 程式設計最重要的設定之一,雖然在 ES5 中添加了Object.getPrototypeOf() 方法來返回任意物件的原型,但仍缺少物件在實例化後改變原型的標準方法。ES6 中添加了Object.setPrototypeOf() 方法來改變這一現狀,透過這一方法可以改變任意指定物件的原型,它接受兩個參數:被改變原型的物件及替代第一個參數的原型物件。
let person={
getGreeting(){
return "Hello";
}
}
let dog = {
getGreeting(){
return "Woof";
}
}
// 以person對象為原型
let friend = Ojbect.create(person);
console.log(friend.getGreeting()); // Hello
console.log(Object.getPrototypeOf(friend) === person) // true
// 將原型設置為dog
Object.setPrototypeOf(friend,dog);
console.log(friend.getGreeting()) // Woof
console.log(Object.getPrototypeOf(friend)===dog) // true
物件原型的真實值被儲存在內部專用屬性[[Prototype]]中,呼叫Object.getPrototypeOf() 方法返回儲存在其中的值,呼叫Object.setPrototypeOf() 方法改變其中的值。然而,這不是操作[[Prototype]]值的唯一方法。
- 我們需要牢記兩點:①
__proto__和constructor屬性是物件所獨有的;②prototype屬性是函式所獨有的,因為函式也是一種物件,所以函式也擁有__proto__和constructor屬性。 __proto__屬性的作用就是當存取一個物件的屬性時,如果該物件內部不存在這個屬性,那麼就會去它的__proto__屬性所指向的那個物件(父物件)裡找,一直找,直到__proto__屬性的終點null,再往上找就相當於在null上取值,會報錯。透過__proto__屬性將物件連接起來的這條鏈路即我們所謂的原型鏈。prototype屬性的作用就是讓該函式所實例化的物件們都可以找到公用的屬性與方法,即f1.__proto__ === Foo.prototype。constructor屬性的含義就是指向該物件的建構函式,所有函式(此時看成物件了)最終的建構函式都指向 Function。
簡化原型存取的 Super 作用
子類方法覆寫父類方法,還要呼叫傳參給父類方法;ECMAScript 6 引入了 Super 引用的特性,使用它可以更便捷地存取物件原型
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
ES6 簡化了這種寫法:
let friend = {
getGreeting(){
// return Object.getPrototypeOf(this).getGreeting.call(this)+', hi!'
return super.getGreeting()+", hi~"
}
}
// 注意要使用簡寫方法對象中使用super引用,如下會報錯
let friend = {
getGreeting:function(){
// return Object.getPrototypeOf(this).getGreeting.call(this)+', hi!'
return super.getGreeting()+", hi~"
}
}
super引用不是動態變化的,它總是指向正確的物件,在這個範例中,無論有多少其他方法繼承了 getGreeting 方法,super.getGreeting()始終指向
正式的方法定義
ES6 中正式將方法定義為一個函式,它會有一個內部的[[HomeObject]]屬性來容納這個方法所屬的物件。如下:
let person = {
// 這是方法
getGreeting(){
return "Hello"
}
};
// 不是方法
function shareGreeting(){
return "Hi~";
}
理解:這個範例中定義了 person 物件,它有一個getGreeting() 方法,由於直接把函式賦值給了 person 物件,因此getGreeting() 方法的[[HomeObject]]屬性值為 person。而下面的方法shareGreeting 建立時未將其賦值給一個物件,因此該方法沒有明確定義[[HomeObject]]屬性。在大多數情況下這點小差別無關緊要,但是當使用 Super 引用時就變得非常重要了。
super 的所有引用都是透過[[HomeObject]]屬性來確定後續運行過程。第一步在[[HomeObject]]屬性上呼叫Object.getPrototypeOf() 方法來檢索原型引用;然後搜尋原型找到同名函式,最後,設定 this 綁定並且呼叫相應的方法。
// 自己分析一下
let person={
getGreeting(){
return "Hello";
}
}
let friend = {
getGreeting(){
// return Object.getPrototypeOf(this).getGreeting.call(this)+', hi!'
return super.getGreeting()+", hi~"
}
}
Object.setPrototypeOf(friend,person)
console.log(friend.getGreeting()) // "Hello hi~"
總結
- 屬性
- 簡化屬性定義語法,使將目前作用域中的同名變數賦值給物件的語法變得更加簡潔;
- 添加可計算屬性名特性,允許為物件指定非字面值屬性名;
- 添加物件方法簡寫語法,在物件字面值中定義方法時可以省略冒號和 function 關鍵字
- ES6 嚴格模式下物件字面值重複名稱校驗,即使在同一個物件字面值中定義兩個同名屬性也不會拋出錯誤
Object.assign()方法可以一次性更改物件中的多個屬性,如果使用混入(mixin)模式這將非常有用Object.is()方法對於所有值進行嚴格等價判斷,當將其用於處理 JavaScript 值問題時比===更加安全Object.setPrototypeOf()方法,物件被建立後修改它的原型super關鍵字呼叫原型上的方法,此時的 this 綁定會被自動設定為目前作用域的 this 值
主題測試文章,只做測試使用。發佈者:Walker,轉轉請注明出處:https://walker-learn.xyz/archives/4329
