之前在看《Javascript 高級程序設計》一書中遇到過getter和setter,但因當時難於理解,且覺得用處較小,沒有細看,今日突然遇到了一種使用get&set讀寫對象屬性的方式。
//字面量創建對象 var p={ //對象的成員:屬性&方法 name:"cj", work:function(){ console.log("working...."); }, _age:18, //一般常用下划線開頭,表示該屬性只能有對象的方法進行訪問。 //get讀取p._age get age(){ return this._age; }, //set設置p._age set age(val){ if(val<0||val>150){ throw new Error("invalid value"); }else{ this._age=val; } } }; console.log(p.age); //18 console.log(p._age); //18 p.age=30; console.log(p.age); //18 console.log(p._age); //18
然后在網上搜到了一組類似的用法:
//創建一個類 var Person = function() { //屬性:姓名,注意要屬性名與get和set的名稱不能重復否則會報錯 this._username = 'unknown'; this._age = 0; } //在原型中給set和get方法 //在原型中get和set方法的名稱是一樣的,方便調用 Person.prototype = { set username(name) { // console.log('調用username的set方法'); this._username = name; }, get username() { // console.log('調用了username的get方法'); return this._username; } } var p = new Person(); console.log(p); console.log(p.username); //unknown p.username; p.username = 'foo'; console.log(p.username); //foo
但是沒能查找到更多關於get和set的信息,只能琢磨一下getter和setter:
ECMAScript中有兩種屬性:數據屬性和訪問器屬性。
數據屬性包含一個數據值的位置。在這個位置可以讀取和寫入值。
1、數據屬性:在使用字面量形式創建的對象中,直接定義在對象上的屬性,包含四個默認的屬性:
(1)[[configurable]]:表示能否通過delete刪除屬性從而重新定義屬性,能否修改屬性的特性,或者能否把屬性修改為訪問器屬性。該特性默認值為true。
(2)[[enumerable]]:表示能否通過for-in循環返回屬性。該特性默認值為true。
(3)[[writable]]:表示能否修改屬性的值。該特性默認值為true。
(4)[[value]]:包含這個屬性的數據值,讀取屬性值的時候,從這個位置讀;寫入屬性值的時候,把新值保存在這個位置上,該特性默認值為undefined。
如果需要修改數據屬性,需要使用ECMAScript5中的object.defineProperty()方法。
var p={ name:"nicole" }; Object.defineProperty(p,"name",{ value:"abc" }) console.log(p.name); //abc
2、訪問器屬性:不包含數據值;他們包含一對兒getter&setter函數,有如下4個特性:
(1)configurable:表示能否通過delete刪除屬性從而定義新的屬性,能否修改屬性的特性,或者能否把屬性修改為數據屬性。該特性默認值為true。
(2)enumerable:表示能否通過for-in循環返回屬性。該特性默認值為true。
(3)get:在讀取屬性時調用的函數。該特性默認值為undefined;
(4)set:在寫入屬性時調用的函數。該特性默認值為undefined;
注:對於兩種屬性默認值的測試如下,並非書中說的默認為true,有的情況下默認為false:
tip:_ 下划線是一種常用的記號,用於表示只能通過對象方法訪問的屬性。
var book={ _year:2004, edition:1 }; Object.defineProperty(book,"year",{ //跟數據屬性不一樣,此處的year是訪問器屬性,_year是數據屬性。 get:function(){ return this._year; }, set:function(newValue){ if(newValue>2004){ this._year=newValue; this.edition+=newValue-2004; } } }) book.year=2005; alert(book.edition); //2
支持這種方法的瀏覽器有:IE9+(IE8只是部分實現),Firefox4+,Safari5+,Opera12+,Chrome.舊版創建訪問器屬性,一般都是用兩個非標准的方法:_defineGetter_()和_defineSetter_()
3、定義多個屬性
var book={}; Object.defineProperties(book,{ _year:{ writable:true, value:2004 }, edition:{ writable:true, value:1 }, year:{ get:function(){ return this._year; }, set:function(newValue){ if(newValue>2004){ this._year=newValue; this.edition+=newValue-2004; } } } }) console.log(book._year); //2004 console.log(book.edition); //1 book.year=2005; console.log(book.edition); //2
使用ECMAScript5的Object.getOwnPropertyDescriptor(屬性所在的對象,屬性名稱),可以取得給定屬性的描述符,返回一個對象,對象包含相應的屬性。
如果是數據屬性,這個對象的屬性有:configurable,enumerable,writable,value;
如果是訪問器屬性,這個對象的屬性有:configurable,enumerable,get,set;
支持這個方法的瀏覽器有:IE9+,Firefox4+,Safari5+,Opera12+,Chrome。
++++ var descriptor=Object.getOwnPropertyDescriptor(book,"_year"); console.log(descriptor); var descriptor2=Object.getOwnPropertyDescriptor(book,"year"); console.log(descriptor2);
第一個_year是數據屬性,第二個year是訪問器屬性。所以最終打印出的結果如下:
所以看完這里,仍然不理解最初遇到的那種寫法是為何,姑且當做是在字面量對象中創建了訪問器屬性,來讀寫數據屬性吧。