擴展對象的功能
- 普通對象 具有js對象所有默認內部行為的
- 特異對象 具有某些與默認行為不符的內部行為
- 標準對象 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()方法返回儲存在其中的值,調用Ojbect.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
