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