咱們先建立一個對象:函數
var person = { name: "Nicholas", _job: "Software Engineer", sayName: function(){ alert(this.name); }, get job(){ return this._job; }, set job(newJob){ this._job=newJob; } }
在這個對象中,咱們定義了一個name屬性和一個_job屬性;至於以set和get開頭的兩處代碼,他們共同定義了一個屬性job。明顯屬性job和_job、name的是不一樣的。是的,JavaScript中的對象有兩種不一樣類型的屬性:數據屬性和訪問器屬性。性能
name和_job是數據屬性,job是訪問器。數據屬性和訪問器屬性的最大的不一樣就在於:當訪問一個訪問器屬性時,獲得get後面函數的返回值;給一個訪問器屬性賦值時,執行的是set後面的函數,這個函數以賦的值爲參數:測試
console.info(person.job);//Software Engineer person.job="Coder"; console.info(person.job);//Coder console.info(person._job);//Coder
在set函數中咱們經過this改變了_job的值(this指向person這個對象);this
在瞭解了對象屬性的類型後,咱們再來看看對象屬性特性。每一個對象的屬性在建立時都帶有一些特徵值,JavaScript經過這些特徵值來定義它們的行爲。spa
咱們在建立person對象時沒有爲它的屬性們直接指定特徵值,JavaScript自動爲它們建立了屬性特性。在ES3中屬性特性不可訪問,可是ES5中屬性的特性能夠經過Object.getOwnPropertyDescriptors或Object.getOwnPropertyDescriptor獲得:prototype
var descriptors= Object.getOwnPropertyDescriptors(person) //descriptors的內容以下; { age:{value: 29, writable: true, enumerable: true, configurable: true} job:{enumerable: true, configurable: true, get: ƒ, set: ƒ} name:{value: "Nicholas", writable: true, enumerable: true, configurable: true} sayName:{writable: true, enumerable: true, configurable: true, value: ƒ} _job:{value: "Coder", writable: true, enumerable: true, configurable: true} }
Object.getOwnPropertyDescriptors返回一個對象,包含了描述person對象的全部屬性的特性。其中每個屬性的特性用一個對象表示,咱們叫它屬性描述符。咱們能夠看到:數據屬性的描述符有四個屬性:value,writable ,enumerable ,configurable ;訪問器屬性的描述符也有四個屬性:enumerable ,configurable,get,set ;code
那麼咱們分別看下數據屬性及訪問器屬性的這幾個描述符:對象
數據屬性的描述符的有四個屬性分別是:blog
咱們最初開始建立的person對象的屬性name,它的value爲「Nicholas」,其餘描述符爲true。繼承
請看例子:
訪問器屬性的描述符的有四個屬性分別是:
咱們最初開始建立的person對象的屬性job,它的get和set值分別是咱們指定的函數,其餘描述符爲true。
要修改屬性默認的特性,必須使用 ECMAScript 5 的 Object.defineProperty或 Object.defineProperties。
有了定義屬性特性的方法,那咱們經過代碼來探索下這些屬性特性的做用:
數據屬性:
var person ={}; //除了configrable以外,其餘三個屬性相互之間不會影響,讀者能夠本身測試 console.info('---------------------writable start--------------------------------'); Object.defineProperty(person, "name", { writable: false, enumerable:true, configurable:true, value: "Nicholas" }); console.info(person.name); //"Nicholas" person.name = "Greg"; //writable爲false,屬性不可修改 console.info(person.name); //"Nicholas" //writable爲false,但configrable爲true,咱們能夠從新配置屬性描述符, Object.defineProperty(person, "name", { writable: false, enumerable:true, configurable:true, value: "John" }); console.info(person.name)//John delete person.name //但configrable爲true,屬性能夠被刪除 console.info (person.name)//undefined console.info('---------------------writable end--------------------------------'); console.info('---------------------enumerable start--------------------------------'); var person={}; Object.defineProperty(person, "name", { writable: false, enumerable:true, configurable:true, value: "Nicholas" }); //enumerable爲true屬性可枚舉 for(var prop in person){ console.info(prop)//name } Object.defineProperty(person, "name", { writable: false, enumerable:false, configurable:true, value: "Nicholas" }); //enumerable爲false屬性不可枚舉,循環體不執行 for(var prop in person){ console.info(prop)// } console.info('---------------------enumerable end--------------------------------'); console.info('---------------------configurable start--------------------------------'); var person={}; Object.defineProperty(person, "name", { writable: true, enumerable:true, configurable:false, value: "Nicholas" }); //configurable爲false,writable爲true,屬性仍然可修改 person.name="John" console.info(person.name);//John //configurable爲false,writable爲true,仍然能夠經過配置的方式改變屬性值 Object.defineProperty(person, "name", { writable: true, enumerable:true, configurable:false, value: "Nicholas" }); console.info(person.name) //configurable爲false,enumerable爲ture,屬性可枚舉 for(var prop in person){ console.info(prop)//name } //configurable爲false,咱們仍然能夠把writable屬性由true改成false Object.defineProperty(person, "name", { writable: false, enumerable:true, configurable:false, value: "Nicholas" }); console.info(Object.getOwnPropertyDescriptor(person,"name"))//{value: "Nicholas", writable: false, enumerable: true, configurable: false} //configurable爲false,writable爲false,不能經過配置改變value的值 try{ Object.defineProperty(person, "name", { writable: false, enumerable:true, configurable:false, value: "John" }); }catch(error){ console.info("value change error"); console.info(Object.getOwnPropertyDescriptor(person,"name"))//{value: "Nicholas", writable: false, enumerable: true, configurable: false} } //configurable爲false,可是不能把writable屬性由false改成true try{ Object.defineProperty(person, "name", { writable: true, enumerable:true, configurable:false, value: "Nicholas" }); }catch(error){ console.info("writable false to true error") console.info(Object.getOwnPropertyDescriptor(person,"name"))//{value: "Nicholas", writable: false, enumerable: true, configurable: false} } //configurable爲false,不能改變enumerable的值 try{ Object.defineProperty(person, "name", { writable: false, enumerable:false, configurable:false, value: "Nicholas" }); }catch(error){ console.info("enumerable change error"); console.info(Object.getOwnPropertyDescriptor(person,"name"))//{value: "Nicholas", writable: false, enumerable: true, configurable: false} } var person={}; Object.defineProperty(person, "name", { writable: true, enumerable:true, configurable:true, value: "Nicholas" }); //configurable爲true,能夠把數據屬性修改成訪問器屬性 try{ Object.defineProperty(person, "name", { get: function(){return "Nicholas"}, enumerable:true, configurable:false }); }catch(error){ console.info("get error"); } console.info(Object.getOwnPropertyDescriptor(person,"name"))//{set: undefined, enumerable: true, configurable: false, get: ƒ} var person={}; Object.defineProperty(person, "name", { writable: true, enumerable:true, configurable:false, value: "Nicholas" }); //configurable爲false,不能夠把數據屬性修改成訪問器屬性 try{ Object.defineProperty(person, "name", { get: function(){return "Nicholas"}, enumerable:true, configurable:false }); }catch(error){ console.info("get error"); } console.info(Object.getOwnPropertyDescriptor(person,"name"))//{value: "Nicholas", writable: true, enumerable: true, configurable: false} console.info('---------------------configurable end--------------------------------');
訪問器屬性
//訪問器屬性 console.info("------------------------------------------------------"); var person ={_name:"Nicholas"}; Object.defineProperty(person, "name", { get: function(){ console.info("get 被調用") return this._name }, set:function(newName){ console.info("set 被調用") this._name=newName }, enumerable:true, configurable:true, }); person.name;//get 被調用 person.name="John";//set 被調用 console.info("------------------------------------------------------"); console.info("----------------------不設set 開始--------------------------------"); var person ={_name:"Nicholas"}; Object.defineProperty(person, "name", { get: function(){ console.info("get 被調用") return this._name }, enumerable:true, configurable:true, }); person.name;//get 被調用 person.name="John";//沒有設置set,什麼也沒發生 console.info(person.name)//Nicholas, console.info("----------------------不設set 結束--------------------------------"); console.info("----------------------不設get 開始--------------------------------"); var person ={_name:"Nicholas"}; Object.defineProperty(person, "name", { set:function(newName){ console.info("set 被調用") this._name=newName }, enumerable:true, configurable:true, }); console.info(person.name);//沒有get,獲得 undefined console.info(person._name);//Nicholas person.name="John";//set 被調用 console.info(person._name)//John,經過set,_name的值被改變 console.info("----------------------不設get 結束--------------------------------"); console.info("----------------------不設get set開始--------------------------------"); //雖然不報錯,可是這個屬性沒有任何意義 var person ={_name:"Nicholas"}; Object.defineProperty(person, "name", { enumerable:true, configurable:true, }); console.info("----------------------不設get 結束--------------------------------"); console.info("----------------------enumerable 開始--------------------------------"); var person ={_name:"Nicholas"}; Object.defineProperty(person, "name", { get: function(){ console.info("get 被調用") return this._name }, set:function(newName){ console.info("set 被調用") this._name=newName }, enumerable:true, configurable:true, }); for(var prop in person){ console.info(prop)//_name,name } Object.defineProperty(person, "name", { get: function(){ console.info("get 被調用") return this._name }, set:function(newName){ console.info("set 被調用") this._name=newName }, enumerable:false, configurable:true, }); for(var prop in person){ console.info(prop)//_name } console.info("----------------------enumerable 結束--------------------------------"); console.info("----------------------configurable 開始--------------------------------"); var person ={_name:"Nicholas"}; Object.defineProperty(person, "name", { get: function(){ console.info("get 被調用") return this._name }, set:function(newName){ console.info("set 被調用") this._name=newName }, enumerable:true, configurable:false, }); person.name;//get 被調用 person.name="John";//set 被調用 console.info(person.name);//John //報錯 try{ Object.defineProperty(person, "name", { get: function(){ console.info("get 被調用") return this._name }, set:function(newName){ console.info("set 被調用") this._name=newName }, enumerable:true, configurable:false, }); }catch(e){ console.info("不能從新定義name的屬性標識符") } //報錯 try{ Object.defineProperty(person, "name", { value:"123", writable:true, enumerable:true, configurable:false, }); }catch(e){ console.info("不能從新定義name的屬性標識符") } console.info("----------------------configurable 結束--------------------------------");
總結一下:
1.writable爲true,屬性值就能夠修改,不管是經過.運算符仍是經過Object.defineProperty方法;
2.writable爲false,不能經過.運算符修改屬性。可是在configurable爲true的狀況下能夠經過經過Object.defineProperty方法從新設置value值,從而修改屬性值;
3.只要enumerable爲true,屬性就可枚舉,爲false則不可枚舉;
4.configurable爲true的狀況下,能夠對屬性描述符對象進行任何修改;
5.configurable爲fasle的狀況下,能夠經過Object.defineProperty把writable改成true,在writabe爲true的狀況下,能夠修改value的值。
6.configurable爲false的狀況下,除第5條的所述的狀況外,不能經過Object.defineProperty修改屬性的任何特性值。
ES5定義了三個方法Object.preventExtensions、Object.seal、Object.freeze分別定義了不一樣級別的可擴展性。可點擊鏈接前往MDN閱讀;
function Person(){} var p=Person.prototype; p._name="John"; Object.defineProperty(p,"name",{ get: function(){return this._name}, set: function(newName){ this._name=newName}, enumerable:true, configurable:true }) var person=new Person(); console.info(person.name)//John console.info(person.hasOwnProperty("_name"))//false //雖然name屬性的set get方法是定義在原型中的 //可是經過person調用時,它們的this屬性會指向person //因此經過person.name設置屬性時,執行set方法中的this._name=newName時, //會給person對象添加_name屬性,並把newName賦給person新建的屬性_name; person.name="Nicholas"; console.info(person.name);//Nicholas console.info(p._name)//John console.info(person.hasOwnProperty("name"))//false console.info(person.hasOwnProperty("_name"))//true
寫的有點冗長,可是算是基本測試了各類狀況,方便查閱;