在JavaScript中,幾乎每一個值都是某種特定類型的對象,所以ES6加強了對象的功能性。
對象類別
·普通對象:具有JavaScript對象所有默認內(nèi)部行為。
·特異對象:具有某些與默認行為不符的內(nèi)部行為。
·標準對象:ECMAScript6規(guī)范中定義的對象,例如:Array,Date等。標準對象既可以是普通對象,也可以是特異對象。
·內(nèi)建對象:腳本開始執(zhí)行時存在于JavaScript執(zhí)行環(huán)境中的對象,所有標準對象都是內(nèi)建對象。
對象字面量語法擴展
屬性初始值的簡寫
在ES5中,對象字面量只是簡單的鍵值對集合,這意味著初始化屬性值時會有一些重復。
function createPerson(name,age){
return {
name:name,
age:age
};
}
這段代碼中這個函數(shù)屬性名稱與函數(shù)的參數(shù)相同,在ES6中,通過使用屬性初始化的簡寫語法,就可以消除這種屬性名稱與局部變量之間的重復書寫。
//這是簡寫
function createPerson(name,age){
return {
name,
age
};
}
當對象字面量里只有一個屬性的名稱時,JavaScript引擎會在可訪問作用域中查找其同名變量;如果找到,就會把變量的值賦值給對象字面量的同名屬性。這樣做的好處是:有助于消除命名錯誤。
對象方法的簡寫語法
ES6也改進了為對象字面量定義方法的語法。
// ES6之前
var persoon={
name:"cc",
sayName:function(){
console.log(this.name);
}
};
//ES6中消除了冒號和function關(guān)鍵字
var person={
name:"cc",
sayName(){
console.log(this.name);
}
}
簡寫方法和之前的定義方法唯一不同的是簡寫方法可以使用super關(guān)鍵字(稍后討論)。
可計算屬性名
在ES6之前,如果屬性名稱被包含在變量里或者通過計算獲得該變量的值,那么ES5并不能為一個對象字面量定義該屬性的。
而在ES6中,可在對象字面量中使用可計算屬性名稱,其語法與引用對象實例的可計算屬性名稱相同,也是使用方括號。
//例子一
let lastName="last name";
let person={
"first name":"Nicholas",
[lastName]:"Zakes"
};
console.log(person["first name"]);//"Nicholas"
console.log(person[lastName]);//"Zakes"
//例子二
var suffix="name";
var person={
["first"+suffix]:"Nicholas",
["last"+suffix]:"Zakes"
};
console.log(person["firstname"]);//"Nicholas"
console.log(person["lastname"]);//"Zakes"
新增方法
ES6為了讓某些任務也更容易完成,在全局對象Object對象中引入了新的方法。
Object.is()方法
ES5前,開發(fā)者習慣用===確定比較值,但是+0和-0在JavaScript中是是不同實體,然后+0===-0,同樣,NaN===NaN會返回false,而ES6中引入Object.is()彌補全等運算符的不準確性。
console.log(+0===-0);//true
console.log(NaN===NaN);//false
console.log(Object.is(+0,-0));//false
console.log(Object.is(NaN,NaN));//true
Object.assign()方法
混合(Mixin)是JavaScript中實現(xiàn)對象組合最流行的模式,它可以實現(xiàn)一個對象接收來自另一個對象的屬性和方法。
function mixin(receiver,supplier){
Object.keys(supplier).forEach(function(key){//遍歷自身屬性,并添加到新對象中
receiver[key]=supplier[key];
});
return receiver;
}
這樣一來利用這個函數(shù)不通過繼承就可以獲得新屬性。
function EventTarget(){/*...*/}
EventTarget.prototype={
constructor:EventTarget,
emit:function(){return "cc";},
on:function(){/*...*/}
};
var myObject={};
mixin(myObject,EventTarget.prototype);
myObject.emit("somethingChanged");//"cc"
這種混合模式非常流行,所以在ES6中添加了Object.assign()方法實現(xiàn)了相同的功能,這個方法接收對象和任意數(shù)量的源對象,最終返回對象。值得注意的是不能復制訪問器屬性。
任何使用mixin()的方法都可以直接使用這個方法。
function EventTarget(){/*...*/}
EventTarget.prototype={
constructor:EventTarget,
emit:function(){return "cc";},
on:function(){/*...*/}
};
var myObject={};
Object.assign(myObject,EventTarget.prototype);
myObject.emit("somethingChanged");//"cc"
Object.assign()可以接收任意數(shù)量的源對象,并按指定順序?qū)傩詮椭频浇邮諏ο笾校桥盼豢亢蟮膬?yōu)先。
function first(){/*...*/}
first.prototype={
constructor:"first",
emit:function(){return "first";},
on:function(){/*...*/}
};
function second(){/*...*/}
first.prototype={
constructor:"second",
emit:function(){return "second";},
on:function(){/*...*/}
};
var myObject={};
mixin(myObject,first.prototype,second.prototype);
myObject.emit("somethingChanged");//"second"
重復的對象字面量屬性
ES5嚴格模式中中,對象加入了對象字面量重復性的校檢,當多個命名屬性時會拋出錯誤。
而ES6中不會,ES6中會選取最后一個值。
//ES6
var person={
name:"cc",
name:"ccg"
}
console.log(person.name);//"ccg"
自有屬性枚舉順序
ES5中未定義對象屬性的枚舉順序,由JavaScript引擎廠商自行決定,然而ES6中嚴格規(guī)定了對象的自有屬性被枚舉的返回順序。
自有屬性的枚舉順序的基本規(guī)則是:
- 所有的數(shù)字鍵按升序排序。
- 所有的字符串鍵按照它們被加入對象的順序排序。
- 所有symbol鍵按照它們被加入對象的順序排序。
var obj={
a:1,
0:1,
c:1,
2:1,
b:1,
1:1
};
obj.d=1;
console.log(Object.getOwnPropertyNames(obj).join(""));//012acbd
增強對象原型
ES6對原型進行了改進。
改變對象的原型
正常情況下,對象原型在實例化后就無法改變了,但是ES6中添加了Object.setPrototypeOf()方法來改變這一現(xiàn)狀。
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
//將原型設(shè)置為dog
Object.setPrototypeOf(friend,dog);
console.log(friend.getGreeting());//Woof
console.log(Object.getPrototypeOf(friend)===dog);//true
簡化原型訪問的Super引用
Super可以更快捷的訪問原型,請看例子。
let person={
getGreeting(){
return "Hello";
}
};
let dog={
getGreeting(){
return "Woof";
}
};
let friend={
getGreeting(){
return Object.getPrototypeOf(this).getGreeting.call(this)+",hi";
}
};
//將原型設(shè)置為person
Object.setPrototypeOf(friend,person);
console.log(friend.getGreeting());//"Hello,hi"
console.log(Object.getPrototypeOf(friend)===person);//true
//將原型設(shè)置為dog
Object.setPrototypeOf(friend,dog);
console.log(friend.getGreeting());//"Woof,hi"
console.log(Object.getPrototypeOf(friend)===dog);//true
//用super可以準確找到指向當前對象的原型
let friend={
getGreeting(){
return super().getGreeting()",hi";
}
};
Super在多重繼承非常有用,請看例子
let person={
getGreeting(){
return "Hello";
}
};
//以person對象為原型
let friend={
getGreeting(){
return Object.getPrototypeOf(this).getGreeting.call(this)+",hi";
}
};
Object.setPrototypeOf(friend,person)
//原型是friend
let relative=Object.create(friend);
console.log(person.getGreeting());//"Hello"
console.log(friend.getGreeting());//"Hello,hi"
console.log(relative.getGreeting());//error!
//call(this)的定位會使程序進入遞歸調(diào)用,直到觸發(fā)棧溢出報錯
如果不使用call(this),程序會一級一級向上找,直到找到最大的構(gòu)造函數(shù)Object。
let person={
getGreeting(){
return "Hello";
}
};
//以person對象為原型
let friend={
getGreeting(){
return Object.getPrototypeOf(this).getGreeting()+",hi";
}
};
Object.setPrototypeOf(friend,person);
//原型是friend
let relative=Object.create(friend);
console.log(person.getGreeting());//"Hello"
console.log(friend.getGreeting());//"Hello,hi"
console.log(relative.getGreeting());//"Hello,hi,hi"
最佳實踐是使用Super,因為它不是動態(tài)變化的,總會指向正確的對象。
let person={
getGreeting(){
return "Hello";
}
};
//以person對象為原型
let friend={
getGreeting(){
return super.getGreeting()+",hi";
}
};
Object.setPrototypeOf(friend,person);
//原型是friend
let relative=Object.create(friend);
console.log(person.getGreeting());//"Hello"
console.log(friend.getGreeting());//"Hello,hi"
console.log(relative.getGreeting());//"Hello,hi"
正式的定義方法
ES6以前從未正式定義“方法”的概念,而在ES6中被正式定義為一個函數(shù),這個函數(shù)內(nèi)部包含[HomeObject]屬性容納這個方法從屬的對象。
let person={
//是方法
getGreenting(){
return "Hello";//明確賦值person,[HomeObject]的屬性值為person
}
};
//不是方法
function shareGreeting(){
return "Hi";//沒有明確賦值給一個對象,[HomeObject]無法定義
}
值得注意的點是,Super的所有引用都是要通過[HomeObject]屬性來確定后續(xù)的進程。第一步是在[HomeObject]屬性值上調(diào)用Object.getPrototypeOf()來檢索原型的引用率;然后搜索原型尋找同名函數(shù);最后,設(shè)置this值并且調(diào)用相應的方法。
let person={
getGreenting(){
return "Hello";
}
};
let friend={
getGreenting(){
return super.getGreenting()+",hi!";
}
};
//以person對象為原型
Object.setPrototypeOf(friend,person);
console.log(friend.getGreenting());//Hello,hi!