JavaScript發明之始,從技術上來說就是一門面向對象的語言,但在ES6以前,JS的不少特性和傳統的面嚮對象語言有所不一樣,好比沒有類的概念(ES6有了class)。今天結合《JS高編》第六章開始回顧和深刻學習面向對象部分,包括對象、原型、原型鏈、繼承等部分。程序員
談JS的對象以前,先複習一下面向對象的基礎概念和特色吧。
面向對象OOP(Object-oriented programming),結合維基百科和百度百科的闡述,再談談個人理解。函數
官方解釋:
面向對象就是基於對象概念,以對象爲中心,以類和繼承爲構造機制,來認識、理解、刻畫客觀世界和設計、構建相應的軟件系統學習
個人理解:
在JavaScript的世界中,萬物皆對象。任何事和物你均可以將其定義爲一個對象,程序員界有個笑話就是單身狗能夠new一個對象嘛......個人粗淺理解,若是我是一個上帝,這個世界的任何人和事相對於我而言都是一個對象。有了控制對象的權力,我就能夠對他們進行任何操做。針對事,我能夠發佈一個號令,發佈一個政策,告訴別人怎麼執行,何時開始,何時結束。針對人,我能夠把他們分爲男人、女人,這就是類。而後我能夠限制他們的兒子是男人仍是女人,是男人那就必須有和爸爸同樣的性別特徵,這就是繼承。我還能夠控制他們什麼時間作什麼事等等,整個過程我都是圍繞某個對象來展開的,那麼這個過程叫作面向對象。測試
特色:
1.類
2.繼承
3.封裝
4.多態
具體的在後面學習和複習時再談。this
let obj = { name:"勾鑫宇", age:23 }
1.數據屬性:[[Configurable]],[[Enumerable]],[[Writable]],[[Value]]
2.訪問器屬性:[[Configurable]],[[Enumerable]],[[Get]],[[Set]]
spa
書上講到屬性類型時,只是簡單提了一下是爲了表示對象的特性,描述了屬性的特徵,而且在JS中不能直接訪問。光看介紹不太理解究竟是幹什麼的,可是看了數據屬性的內容以後,發現不難理解。翻譯
個人理解,數據屬性就是咱們能夠從根源去控制一個對象的屬性是否能被修改、刪除、循環等,並能夠經過訪問器屬性在別人不知道的狀況下進行數據處理。經過Object.defineProperty()
這個方法,咱們能夠去設置這些限制對象屬性操做的值,從而限制別人對某個對象屬性的操做。舉個例子,上面的obj
這個對象的name
屬性的值是「勾鑫宇」,從如今起我不想任何人可以修改它的值,那麼我就經過數據屬性來將這個屬性設置爲不可修改,別人用obj.name = "張三"
來修改就不會生效了。而我若是想在修改name
屬性的值後同時讓age
也跟着改變,那麼此時就能夠用訪問器屬性來進行數據處理。設計
咱們是經過Object.defineProperty()
這個方法來進行兩種屬性的設置。那麼首先了解一下Object.defineProperty()
這個方法,它接收三個參數:code
Object.defineProperty(對象名,屬性名,描述符對象) //舉例 Object.defineProperty(obj,"name",{ writable:false,//設置不可修改 enumerable:false//設置不可循環到該屬性 })
能夠在對象的constructor中找到該方法
對象
同時,咱們能夠經過Object.getOwnPropertyDescriptor()
方法來查看這四個特性的設置狀況。接受兩個參數:
Object.getOwnPropertyDescriptor(對象名,屬性名)
數據屬性包含一個數據值的位置。在這個位置能夠讀取和寫入值,有4個描述其行爲的特性。
下面就具體來對每一個數據屬性進行分析:
1.[[Writable]]
:英文意思譯爲「可寫的」,可理解爲「可修改的」。這個屬性用來設置對象的某個屬性是否能被修改,默認爲true。
//舉例 let obj = { name:"勾鑫宇", age:23 } Object.defineProperty(obj,"name",{ writable:false,//設置不可修改 }) //這時再進行修改就不會生效,嚴格模式下會報錯 obj.name = "張三" console.log(obj.name)//輸出的仍是勾鑫宇
嚴格模式報錯
2.[[Enumerable]]
:英文譯爲「可數的,可枚舉的」,是否支持for-in循環來返回屬性,默認爲true。
//舉例 let obj = { name:"勾鑫宇", age:23, gender:male } Object.defineProperty(obj,"name",{ enumerable:false,//設置不可經過for-in循環返回 }) //循環測試 for(let i in obj){ console.log(i)//輸出結果爲age,gender,沒有name屬性,效果就像隱藏了這個屬性。 } //但這時咱們的name屬性仍是存在的 console.log(obj)
3.[[value]]
:這個就不說翻譯了,你們都知道,就是值。這個特性是設置咱們對象某個屬性的值,讀值、寫值都在這裏,默認值爲undefined。
//舉例 let obj = { name:"勾鑫宇", age:23, gender:male } Object.defineProperty(obj,"name",{ value:"張三",//設置name的值爲張三 }) console.log(obj.name)//輸出爲張三 //設置value不影響後面再次修改值,value至關於修改了一次你最早定義的值而已。 obj.name = "傻逼" concole.log(obj.name)//輸出爲「傻逼」
4.[[Configurable]]
:英文譯爲「可配置的」,這個和前面的Writable有什麼區別呢?放到最後講是有緣由的。前面有設置修改,設置循環,設置值,可是尚未設置是否可刪除。Configurable就是作這個事情的。它表示可否經過delete刪除屬性從而從新定義屬性,默認值爲true。
//舉例 let obj = { name:"勾鑫宇", age:23, gender:male } Object.defineProperty(obj,"name",{ configurable:false,//不容許刪除屬性 }) delete obj.name//報錯"Uncaught TypeError: Cannot delete property 'name' of #<Object>"
這個屬性還有最重要的一個特色,就是當你設置爲false事後,就不能再設爲true了,即便你設置了也無效。書上說得個時候你再設置value,enumerable都不會生效,只能設置writable,那麼咱們來試試。
//接着上面再把configurable修改成true Object.defineProperty(obj,"name",{ configurable:true, }) //此時爲會報錯「Uncaught TypeError: Cannot redefine property: name」 //接着再次調用 Object.defineProperty(obj,"name",{ value:'張三' }) console.log(obj)//此時打印出來的是「張三」,而並書上所說的不能修改value的值。 //設置enumerable Object.defineProperty(obj,"name",{ enumerable:false//報錯"Uncaught TypeError: Cannot redefine property: name" })
測試了不少遍,value值在configurable爲false的狀況下仍然是能夠修改的。
//設置writable Object.defineProperty(obj,"name",{ writable:false//不會報錯 }) //再次修改writable Object.defineProperty(obj,"name",{ writable:true//報錯「Uncaught TypeError: Cannot redefine property: name」 }) //修改value Object.defineProperty(obj,"name",{ value:"張三"//報錯「Uncaught TypeError: Cannot redefine property: name」 })
上面設置writable說明在configurable和writable同時爲false的狀況下,就不能再修改任何值了。
Configurable還能控制是否能修改成訪問器屬性,這個在訪問器屬性的時候再講。
訪問器屬性不包含數據值,包含一對getter和setter函數,讀取訪問器屬性時調用getter,寫入時調用setter,並負責處理數據。
訪問器屬性一樣有4個特性值能夠設置:
1.[[Configurable]]
:和數據屬性的功能同樣,只是有一點區別就是可否修改成數據屬性。
2.[[Enumerable]]
:和數據屬性的功能同樣。
3.[[Get]]
:讀取屬性時調用,默認值爲undefined。
get函數就是可以讓你讀取對象中的某個屬性,前提是這個屬性自己是隻能經過對象方法來訪問的,也就是說定義時要有下劃線記號,不然自己就能直接訪問的話,用get也沒有意義了。
//舉例 let obj = { _name:"勾鑫宇",//下劃線是一種記號,表示只能經過對象方法訪問 age:23, gender:male } console.log(obj.name)//輸出爲undefined //用get方法來讀取這個屬性,並返回給對象 Object.defineProperty(obj,"name",{ get(){ return this._name } }) console.log(obj.name)//輸出「勾鑫宇」
4.[[Set]]
:set函數就是寫入屬性的時候調用,默認值爲undefined。
set函數會接收一個參數,這個參數就是咱們修改對象或添加對象的屬性值。
//舉例 let obj = { _name:"勾鑫宇", age:23, gender:male } //用set方法來寫入這個屬性 Object.defineProperty(obj,"name",{ set(val){ this._name = 'hh'+val } }) obj.name = "張三" console.log(obj.name)//輸出爲undefined
這個時候咱們就無法進行下去了,由於不管怎樣,都是輸出undefined
。緣由就是由於咱們沒有使用get函數去讀取咱們寫入的屬性值,記住name
在初始定義時就必須是_name
。
//同時使用get和set let obj = { _name:"勾鑫宇", age:23, gender:male } //用set方法來寫入這個屬性 Object.defineProperty(obj,"name",{ get(){ return this._name }, set(val){ this. = 'hh'+val } }) obj.name = "張三" console.log(obj.name)//輸出'hh張三'
從上面的代碼就能夠看出這個set和get函數的強大之處,那就是能夠進行數據處理。咱們能夠經過set函數來進行對象裏不一樣屬性的關聯,也能夠實現屬性值的各類計算。
//舉例 let obj = { _name:"勾鑫宇", age:23, gender:male } //用set方法來進行不一樣屬性間的關聯 Object.defineProperty(obj,"name",{ get(){ return this._name }, set(val){ if(val === '張三'){ this._name = val; this.age = 18 } } }) //修改屬性值 obj.name = '張三'; console.log(obj)//輸出 {name:'張三',age:18,gender:male}
除了Object.defineProperty()
方法,還有Object.defineProperties()
方法,顧名思義,複數形式就是能夠同時定義多個屬性。它接受兩個參數:
Object.defineProperties(obj,{ name:{ writable:false }, age:{ configurable:true } ... })
這就是對屬性類型的一個學習和理解,若有錯誤,請使勁點我。