语法
Object.defineProperty(obj, prop, descriptor)
参数
obj
要在其上定义属性的对象。
prop
要定义或修改的属性的名称。
descriptor
将被定义或修改的属性描述符。
返回值
被传递给函数的对象。
使用说明:
我们一般给一个对象创建一个新的属性时,大部分采用的都是如下方式:
var obj = {};
obj.key = "Hello"
但是这种创建方式的属性,其值可被修改,可被遍历等等。 如果想创建隐藏属性(使用for in 无法遍历出来)
或者想创建一个只读属性(不可写入),使用这种方式就不可取了。在ES6中,有一个叫做Symbol类型的东西
这个东西可以人为的定义它是否可写,可枚举,可重配置等等,而定义这个Symbol的方法则是Object对象的 defineProperty
这里可以将Symbol理解为带特殊技能的属性。 我们可以为一个对象,使用defineProperty方法来定义一个带红蓝buff的属性
先来说说属性描述符,由名称可知是用来描述一个属性具有什么特点用的,属性描述符分为两类: 数据描述符和存取描述符
数据描述符表示一个具有值的属性,该值可能只读,可能可写
存储描述符表示一个具有对数据进行存取操作的属性。
一个属性只能为以上两种类别中的一个,不能同时是两者
下面是一些上面两个描述符可选的键值:
configurable:
enumerable:
writeable:
value:
get:
set:
注意: 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
程序员最高境界:静若瘫痪,动若癫痫