js中創(chuàng)建一個自定義對象有兩種方法概作,一種是使用new腋妙,另一種是使用對象字面量形式,至于構(gòu)造函數(shù)模式讯榕、工廠模式骤素、原型模式匙睹、組合模式、寄生模式济竹、es6中的class痕檬、Object.create()等等,都不過是這兩種方法的組合應(yīng)用
在創(chuàng)建一個對象字面量前送浊,要記住幾點:
這種對象不是一個構(gòu)造函數(shù)梦谜,不能使用new進行實例化
它是引用類型,也就意味著對象名是一個指針袭景,當(dāng)你把對象名賦值給另一個變量時唁桩,你在新變量上所做的任何操作都會影響源對象
var obj={
id:123
}
var b=obj
b.id=456;
console.log(obj.id) //456
- 對象中的所有成員默認(rèn)是公開的,如果想實現(xiàn)私有成員耸棒,只能采用es6的Symbo來定義一個成員名朵夏,然后采用export模塊化來達到隔離效果。
一榆纽、對象字面量語法
var person={
name:'小王',
age:18,
_pri:233
}
-成員名稱的單引號不是必須的
最后一個成員結(jié)尾不要用逗號仰猖,不然在某些瀏覽器中會拋出錯誤
成員名相同會發(fā)生什么?
es5普通模式下后定義的會覆蓋前面定義的奈籽,嚴(yán)格模式則會報錯,es6則不管什么模式都采用后面的覆蓋前面的
成員名可以是動態(tài)變量嗎饥侵?
es5只能在對象字面量表達式申明以后再添加
var dynamicVar="dyna";
var person={
}
person[dynamicVar]='123';
console.log(person[dynamicVar])
es6則更符合使用場景,可在表達式內(nèi)創(chuàng)建動態(tài)的成員名
var dynamicVar="dyna";
var person={
[dynamicVar]:'test'
}
console.log(person[dynamicVar])
es6中如果想使用表達式外面的變量名作為成員名衣屏,變量的值作為成員值躏升,可進一步簡寫為
var dynamicVar="dyna";
var person={
dynamicVar, //這是一個語法糖,js引擎會解釋為dynamicVar:'dyna'
age:15
}
console.log(person.dynamicVar)
注意:此時不能采用person[dynamicVar]
方式訪問狼忱,因為這句話js引擎會解釋為person['dyna']
膨疏,對象中沒有dyna
屬性,肯定就是undefined
了
可以采用new person()
的方式使用嗎钻弄?
肯定是不可以佃却,new關(guān)鍵字后面必須是一個構(gòu)造函數(shù)才行,對象字面量哪來的構(gòu)造函數(shù)
二窘俺、對象成員配置
對象申明后饲帅,會默認(rèn)為內(nèi)部的每個成員(屬性或方法)生成一些隱藏屬性,這些隱藏屬性是可以讀取和可配置的:
Object.getOwnPropertyDescriptor()或getOwnPropertyDescriptor()-讀取成員的隱藏屬性
Object.definePropertype或Object.defineProperties
----設(shè)置成員的隱藏屬性
相應(yīng)的隱藏信息如下:
1.configurable
是否可以刪除某個成員瘤泪,默認(rèn)為true,需要注意的是灶泵,如果該屬性如果定義為false,后續(xù)又定義為true的話會報錯
Object.defineProperty(person,'name',{
configurable:false
})
Object.defineProperty(person,'name',{
configurable:true
})
2. writable
成員是否可寫,默認(rèn)為true
Object.defineProperty(person,'name',{
writable:false
})
person.name='小李'; //屬性不可寫对途,嚴(yán)格模式下會報錯
console.log(person.name); //輸出小王赦邻,說明上面一句無效
3. enumerable
成員是否可被枚舉,默認(rèn)為rue
,該屬性主要是用來防范Object.keys()
和for in
的实檀,也就是說該屬性設(shè)置對于Object.getOwnPropertyNames()
方法是無效的惶洲。
使用相應(yīng)的枚舉方法按声,輸出的結(jié)果是一個數(shù)組,那么數(shù)組中元素的順序是按什么規(guī)則組織的呢湃鹊?
在es5中并沒有明確這一點儒喊,各個瀏覽器有自己的實現(xiàn)镣奋,es6中采用Object.keys()
和for in
方法時還是沒有明確币呵,但采用Object.getOwnPropertyNames()
方法枚舉時,有了相應(yīng)的標(biāo)準(zhǔn):
最先放入數(shù)組中的是數(shù)值型的成員名侨颈,按升序排列余赢;
其次是其它類型的,按添加的先后順序排列
var obj={
3:'我是1',
1:'我是1',
b:'我是b',
a:'我是a'
}
var names=Object.getOwnPropertyNames(obj);
console.log(names) //["1","3","b","a"]
4. get與set
讀寫成員時調(diào)用的函數(shù)哈垢,默認(rèn)為undefined
在文章最開始處的對象定義中妻柒,我們創(chuàng)建了一個_pri成員,表示這個成員應(yīng)在內(nèi)部讀取耘分,下劃線只是一個標(biāo)記符举塔,并不能限制該成員只能在對象內(nèi)部訪問。接下來我們來封裝一個屬性讀寫器對_pri成員進行讀取求泰,讀寫器名稱隨意取央渣,這里叫pri只是為了可讀性
Object.defineProperty(person,'pri',{
get:function(){
//做一些其它操作
console.log('準(zhǔn)備獲取_pri的值')
return _pri;
},
set:function(newValue){
_pri =newValue
}
})
person.pri='456';
console.log(person.pri);
如果只有g(shù)et則表示屬性值是只讀的,只有set表示只能寫渴频。
屬性讀寫器最常用的場景就是在讀取或?qū)懭雽傩郧翱梢愿綆У淖鲆恍┎僮餮康ぃ_到更好的封裝性
三、對象保護
禁止添加成員
Object.preventExtensions()
該方法用于阻止向?qū)ο筇砑映蓡T,使用Object.isExtensible()`判斷對象是否可添加成員
Object.preventExtensions(person);
//添加成員無效卜朗,非嚴(yán)格模式下什么都不會發(fā)生拔第,嚴(yán)格模式下會報錯
person.bankAccount='中國農(nóng)業(yè)銀行'
//依然可以刪除成員,證明了preventExtensions方法只能阻止添加方法
delete person.age;
console.log(person.age) //undefined场钉,表明刪除成功了
禁止添加蚊俺、刪除成員
Object.seal()
用來阻止添加或刪除成員,判斷對象是否是密封的可采用Object.isSealed()
禁止任何操作
使用Object.freeze()
方法后逛万,除了不能添加刪除成員春叫,連成員的賦值都會失效,但是寫入屬性(上面set定義的)依然是有效的
四泣港、其它技巧
實現(xiàn)繼承
Object.create(person)
可產(chǎn)生一個具有繼承特性的新對象暂殖,但是需要注意的是,父對象的引用類型成員是和子對象共享的当纱,當(dāng)子對象修改引用類型的成員時呛每,父對象的該成員也會同步發(fā)生變化
var person={
name:'小王',
age:18,
_pri:233,
gf:['豆得兒','張G','TFb']
}
var child=Object.create(person);
child.gf.splice(0,1); //跟豆得兒分手了
console.log(person.gf.length) //父類的gf也變成2了,父子共享女友坡氯,尼瑪晨横,太亂了
es6中的Object.setPrototypeOf(obj, prototype)方法可將已有的對象變成繼承關(guān)系洋腮,其內(nèi)部原理也跟Object.create一樣,都是將子對象的prototype指向父對象手形,該方法實現(xiàn)的繼承依然有父子對象共享了引用類型成員的問題啥供。
var person={
age:15
}
var man={
}
Object.setPrototypeOf(man,person)
console.log(Object.getPrototypeOf(man)===person) //true
console.log(man.age); //15
重寫父對象的成員
直接在子對象中定義一個同名的成員即可
子對象中訪問父對象的成員
super
關(guān)鍵字是es6新增的,它是一個指針库糠,指向當(dāng)前對象的原型伙狐,也就是父對象
var person={
age:15,
testMethod(){
console.log('我是父類方法')
}
}
var man={
//重寫父類方法
testMethod(){
console.log('我是子類方法')
super.testMethod();
}
}
Object.setPrototypeOf(man,person)
man.testMethod();
需要注意的是,如果兩個對象不是繼承關(guān)系瞬欧,使用super關(guān)鍵字會報錯
實現(xiàn)jquery.extend
jquery.extend
是一個典行的對象混入贷屎,所謂對象混入,就是將n個對象(為了便于表述艘虎,直接叫做輸入對象)組合成一個新對象唉侄,新對象具有各個輸入對象的特征,這在軟件設(shè)計模式中叫做裝飾器模式野建,在es6以前需要自己實現(xiàn)属划,核心代碼如下:
function mixins(target,sourceArr){
sourceArr.forEach(function(source){
Object.keys(source).forEach(function(item){
target[item] = source[item]
})
})
return target
}
var obj1={
name:'123'
}
var obj2={
id:100
}
var obj3={
meth(){
console.log('haha')
}
}
var target=mixins(obj1,[obj2,obj3])
target.meth()
上面的代碼實現(xiàn)了一個簡易版的jquery.extend
的淺拷貝模式(也就是deep參數(shù)為false時的功能),如果多個對象成員同名候生,則后面的會覆蓋前面的同眯,該代碼如果要在正式環(huán)境使用,還需要加不少的判斷代碼陶舞,但是在es6中一句話就可以實現(xiàn)mixins()
函數(shù)的功能嗽测。
var target=Object.assign(obj1,obj2,obj3)
需要注意的一點就是輸入對象中使用了get修飾符,這時后會有一個轉(zhuǎn)換,方法名變成了新對象的屬性名肿孵,其值為get修飾符方法中的返回值
var obj1={
name:'123'
}
var obj2={
id:100
}
var obj3={
get getMethod(){
return '123'
},
meth(){
console.log('haha')
}
}
var target=Object.assign(obj1,obj2,obj3)
console.log(target.getMethod) //target.getMethod()會報錯唠粥,原因是發(fā)生了轉(zhuǎn)換