7天搞懂JS進階議題(day05)-getter & setter: 屬性描述器

文章推薦指數: 80 %
投票人數:10人

本系列文章討論JS 物件導向設計相關的特性。

不含CSS,不含HTML! 建議先有些JS基礎再繼續閱讀。

你也可以看看從零開始遲來的Web開發筆記 本系列文章討論JS物件導向設計相關的特性。

不含CSS,不含HTML!建議先有些JS基礎再繼續閱讀。

你也可以看看從零開始遲來的Web開發筆記雖然是「7天寫作松」挑戰,但同樣可以視為系列後續文章NoCSS!NoHTML!NoBrowser!Justneedprogramminglanguage有了物件然後呢?今天來說說關於成員(field/attribute/member)背後的屬性。

getter&setter有一個家族–神崎家,生了一個小孩叫アリア,但生下後被りこ偽裝,被發現後重新命名為アリア。

如有雷同存屬巧合var神崎家=class{ constructor(name){ this.__name=`神崎・${name}`; } staticborn(name){ returnnew神崎家(name) } setname(new_name){ this.__name=`神崎・${new_name}`; } getname(){ letfirst_name=this.__name.substr(0,2), last_name=this.__name.substr(3,this.__name.length+1); return`${first_name}・H・${last_name}` } introduce(){ console.log(`Hi~Mynameis${this.name}`) } rename(new_name){ this.name=new_name; returnthis.name; } } vararia=神崎家.born("アリア"); aria.introduce();//=>Hi~Mynameis神崎・H・アリア aria.rename("りこ");//=>神崎・H・りこ aria.introduce();//=>Hi~Mynameis神崎・H・りこ aria.name="アリア"; aria.introduce();//=>Hi~Mynameis神崎・H・アリア console.log(aria.name);//=>神崎・H・アリア 上例中,アリア的家族名是神崎,中間名是H。

而且アリア一生下來就會自我介紹。

並且可以看到,有著get描述器的函式,可以像是一般屬性一樣被存取;set可以在賦予值時做加工。

透過只給get不給set,還可以做到唯讀(readonly)屬性。

Object.defineProperty(神崎家.prototype,"first_name",{ get(){return"神崎"}, configurable:true,//僅為了之後測試方便 }); console.log(aria.first_name);//=>神崎 aria.first_name="遠山"; console.log(aria.first_name);//=>神崎 aria.__proto__.first_name="遠山"; console.log(aria.first_name);//=>神崎 沒人可以亂改家族名!為什麼我要加在神崎家.prototype上呢?不懂嗎?去看看關於PrototypeChain繼承作業-更靈活的first_name你做得到嗎?注意到一開始把"神崎"寫死,成了一個魔術數字(magicnumber)了嗎?你有辦法把上面例子改的更靈活更有彈性吧?透過改寫和Object.defineProperty加點工看看吧!部份程式碼可能如下:很顯眼?絕對不是因為我懶得刪程式碼是要給你提示拉!Object.defineProperty(神崎家.prototype,"first_name",{ get(){returnthis.__first_name||"神崎";}, set(v){this.__first_name=v;}, configurable:true,//僅為了之後測試方便 }); accessor除了getter和setter,還可以直接設定accessor,不過關鍵字是value:Object.defineProperty(aria,"weapons",{ value:["雙槍","雙劍"], writable:true, configurable:true,//僅為了之後測試方便 }); console.log(aria.weapons);//=>['雙槍','雙劍'] 其實也差不多就等於:aria.rivals=["峰理子"];//對...就上面偽裝過アリア的人 defineProperty&getOwnPropertyDescriptor上面例子應該夠你使用Object.defineProperty了,還是不懂嗎?去參考一下更正式的文件吧!這以小節主要用Object.getOwnPropertyDescriptor1來看看上面設定的屬性。

首先看看一般直接設定的結果和accessor:Object.getOwnPropertyDescriptor(aria,"weapons");//=> //不用註解有上色比較好看 { value:['雙槍','雙劍'], writable:true, enumerable:false, configurable:true } Object.getOwnPropertyDescriptor(aria,"rivals");//=> { value:['峰理子'], writable:true, enumerable:true, configurable:true } 可以看到最主要差別在enumerable,表示是否能用for去迭代。

(點我看關於屬性描述器中的enumerable補充)再來看看getter和setter。

就不多J4了。

Object.getOwnPropertyDescriptor(aria.__proto__,"first_name");//=> { get:[Function:get], set:[Function:set], enumerable:false, configurable:true } 2019/02/28關於屬性描述器中的enumerable補充上面看到過關於enumerable的內容。

Object.getOwnPropertyDescriptor(aria,"weapons");//=> //不用註解有上色比較好看 { value:['雙槍','雙劍'], writable:true, enumerable:false, configurable:true } Object.getOwnPropertyDescriptor(aria,"rivals");//=> { value:['峰理子'], writable:true, enumerable:true, configurable:true } 關於enumerable的行為表現在物件aria的迭代上(for-loop)。

不過可以先來看看Object.keys()的結果:console.log(Object.keys(aria)); //=>['__name','rivals'] 可以看到並沒有weapons,因為enumerable:false。

既然keys()找不到,也大概可以理解為什麼迭代時找不到他了。

來用for-in試試:for(keyinaria){ console.log(`${key}:${aria[key]}`); } /*Output: __name:神崎・アリア rivals:峰理子 */ 不過有個例外:當key是symbol時,不管如何都無法迭代:Object.defineProperty(aria,Symbol("super_power"),{ value:["超級推理"], writable:true, enumerable:true, configurable:true,//僅為了之後測試方便 }); s=Object.getOwnPropertySymbols(aria) console.log(s);//=>[Symbol(super_power)] console.log(aria[s[0]]);//=>['超級推理'] for(keyinaria){ console.log(`${key}:${aria[key]}`); } /*Output: __name:神崎・アリア rivals:峰理子 */ 雖然可以找得到這個屬性,但是迭代裡不會出現,也不會出現在keys()裡。

參考資料MDN:Object.getOwnPropertyDescriptor↩︎jsjavascriptECMAScriptcommentspoweredbyDisqus相關內容7天搞懂JS進階議題(day04)-Class&Constructor:吃語法糖別噎到7天搞懂JS進階議題(day03)-Function&Object:關於PrototypeChain繼承7天搞懂JS進階議題(day02)-new&factory:如何建立一個新物件7天搞懂JS進階議題(day01)-This&Bind:你不能不知道的7天搞懂JS進階議題(day00)-Oh...開始了......



請為這篇文章評分?