Object.defineProperty(obj, prop, descriptor)
obj
prop
descriptor
被傳遞給函數的對象。app
使用說明:函數
咱們通常給一個對象建立一個新的屬性時,大部分採用的都是以下方式:this
var obj = {}; obj.key = "Hello"
可是這種建立方式的屬性,其值可被修改,可被遍歷等等。 若是想建立隱藏屬性(使用for in 沒法遍歷出來)spa
或者想建立一個只讀屬性(不可寫入),使用這種方式就不可取了。在ES6中,有一個叫作Symbol類型的東西prototype
這個東西能夠人爲的定義它是否可寫,可枚舉,可重配置等等,而定義這個Symbol的方法則是Object對象的 defineProperty
code
這裏能夠將Symbol理解爲帶特殊技能的屬性。 咱們能夠爲一個對象,使用defineProperty方法來定義一個帶紅藍buff的屬性對象
先來講說屬性描述符,由名稱可知是用來描述一個屬性具備什麼特色用的,屬性描述符分爲兩類: 數據描述符和存取描述符blog
數據描述符表示一個具備值的屬性,該值可能只讀,可能可寫繼承
存儲描述符表示一個具備對數據進行存取操做的屬性。ip
一個屬性只能爲以上兩種類別中的一個,不能同時是二者
下面是一些上面兩個描述符可選的鍵值:
configurable: 是否容許改變描述符,是否容許刪除屬性,默認false
enumerable: 是否容許被枚舉(for in), 默認false
writeable: 是否可寫,默認false
value: 設定數據描述符的value值,默認是undefine
get: 設定存儲描述符的getter方法,默認undefine
set: 設定存儲描述符的setter方法,默認undefine
注意: configurable enumerable 這兩個描述鍵值 是數據描述符 與 存儲描述符 共有的
writeable 和 value 是數據描述符獨有的, 而set 和 get方法是存儲描述符獨有的,另外
這些選項不必定是自身的屬性,也須要考慮繼承,爲確保留有默認值,須要凍結以前的
Object.prototype,或者將__proto__屬性指向null
說了那麼多概念性的東西,下面來實戰練習一下吧
任務一: 建立一個帶有隻讀屬性的對象
var o = {}; Object.defineProperty(o, "myName", { value: "孫悟空", writeable: false //不容許寫,只讀的 }); document.write(o.myName); //孫悟空 //嘗試改變屬性 o.myName = "豬八戒"; //這裏不會報錯,可是會拋出readonly document.write(o.myName); //孫悟空
任務二: 建立一個帶有"隱藏"屬性的對象
var o = {}; Object.defineProperty(o, "myName", { value: "孫悟空", enumerable: false //不容許被枚舉出來 }); //經過普通方式建立其餘屬性 o.myAge = 33; o.myAddress = "China"; //枚舉對象全部變量 for(var i in o){ document.write(i +":"+ o[i] + "<br>"); } //輸出結果: //myAge: 33 //myAddress: China //雖然沒法遍歷出來,可是依然是能夠訪問的 document.write(o.myName); //孫悟空
任務三:建立一個帶有存儲描述符的屬性對象
var p; //稍後使用存儲描述符對他進行讀寫操做 var o = {}; Object.defineProperty(o, "b", { //屬性名爲b get: function(){ return p; //取全局變量 }, set: function(value){ p = value; //寫全局變量 }, enumerable: true, //可遍歷 configurable: true //可修改 }); o.b = 38; //看起來像是直接賦值,實際上是調用了set方法賦值 document.write(p); //38, 由於get方法讀寫的對象是全局變量
存儲描述符,給人感受像是對 對象的屬性進行讀寫操做,但其實背後多是對其餘變量的讀寫
操做,由於能夠人爲的定義讀寫操做執行的函數 set/get ,因此咱們就能夠實現讀寫時實現本身
想要的功能,好比數據類型判斷等等,若是對存儲描述符仍是不太理解,能夠看看下面兩段代碼
實現一個自存檔的屬性
function Archiver() { var temperature = null; var archive = []; Object.defineProperty(this, 'temperature', { get: function() { console.log('get!'); return temperature; }, set: function(value) { temperature = value; archive.push({ val: temperature }); } }); this.getArchive = function() { return archive; }; } var arc = new Archiver(); arc.temperature; // 'get!' arc.temperature = 11; arc.temperature = 13; arc.getArchive(); // [{ val: 11 }, { val: 13 }]
第二段
var pattern = { get: function () { return 'I alway return this string,whatever you have assigned'; }, set: function () { this.myname = 'this is my name string'; } }; function TestDefineSetAndGet() { Object.defineProperty(this, 'myproperty', pattern); } var instance = new TestDefineSetAndGet(); instance.myproperty = 'test'; // 'I alway return this string,whatever you have assigned' console.log(instance.myproperty); // 'this is my name string' console.log(instance.myname);
對於configurable的效果,你們可能還有疑問,能夠閱讀下面這段代碼,本身動手試試
var o = {}; Object.defineProperty(o, "a", { get : function(){return 1;}, configurable : false } ); // throws a TypeError Object.defineProperty(o, "a", {configurable : true}); // throws a TypeError Object.defineProperty(o, "a", {enumerable : true}); // throws a TypeError (set was undefined previously) Object.defineProperty(o, "a", {set : function(){}}); // throws a TypeError (even though the new get does exactly the same thing) Object.defineProperty(o, "a", {get : function(){return 1;}}); // throws a TypeError Object.defineProperty(o, "a", {value : 12}); console.log(o.a); // logs 1 delete o.a; // Nothing happens console.log(o.a); // logs 1