JS提供了一个内部数据结构,用来描述对象的属性,控制对象的行为,这个内部数据结构成为“属性描述对象”,对象中的每个属性都有自己对应的属性描述对象。
属性描述对象提供6个元属性。
Object.defineProperties() 方法直接在一个对象上定义新的属性或修改现有属性,并返回该对象。
语法 : Object.defineProperty ( obj , prop , descriptor )
obj
要在其上定义属性的对象。
prop
要定义或修改的属性的名称。
descriptor
将被定义或修改的属性描述符。
例如
let obj = {
test1 : "peng",
test2 : "chuan"
}
Object.defineProperty(obj,"test1",{
value:"fuckyou",//赋值
writable:false, //是否可改写
enumerable:false//是否可遍历
})
属性描述符
对象里目前存在的属性描述符有两种主要形式:1.数据描述符和2.存取描述符。数据描述符是一个具有值的属性,该值可能是可写的,也可能不是可写的。存取描述符是由getter-setter函数对描述的属性。描述符必须是这两种形式之一;不能同时是两者。
(1).数据描述符
数据描述中的属性都是可选的,来看一下设置每一个属性的作用。
我们定义一个名为obj1的对象。
let obj1 = {
}
value
该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。
Object.defineProperty(obj1,"test",{
value:"fuckyou",//赋值
})
console.log(obj1.test); //"fuckyou"
writable
属性是否可改写。该属性的writable为true时,value才能被赋值运算符改变。默认为 false。
Object.defineProperty(obj1,"test",{
value:"fuckyou",//赋值
writable:false, //是否可改写(false,value值不可改写)
})
console.log(obj1.test); //"fuckyou"
obj1.test = "peng2";
console.log(obj1.test) //"fuckyou" 未改写
Object.defineProperty(obj1,"test",{
writable:true, //是否可改写 (true,value值可改写)
})
console.log(obj1.test); //"fuckyou"
obj1.test = "peng2";
console.log(obj1.test) //"peng2"
enumerable
属性是否可遍历。当且仅当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中(使用for…in或Object.keys())。默认为 false。
var obj1 = {
test1:"值1",
test2:"值2",
}
Object.defineProperty(obj1,"test1",{
enumerable:false//是否可遍历 (false 不可遍历)
})
for(var item in obj1){
console.log(item)//输出为test2,由此可见test1不可遍历
}
console.log(Object.keys(obj1))
console.log(Object.getOwnPropertyNames(obj1))
注意:Object.getOwnPropertyNames( )枚举忽视
configurable
属性描述符是否能被改变, 当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false。
var obj = {};
Object.defineProperty(obj,"test1",{
value:"fuckyou",//赋值
writable:false, //是否可改写
enumerable:false,//是否可遍历
configurable:false//属性描述符是否能被改变,该属性是否能从对象上被删除
})
Object.defineProperty(obj,"test1",{
value:"fuckyou",//赋值
writable:true, //是否可改写
enumerable:true,
configurable:false//属性描述符是否能被改变,该属性是否能从对象上被删除
})//报错: Uncaught ReferenceError: test1 is not defined
这个属性起到两个作用:
目标属性是否可以使用delete删除
目标属性是否可以再次设置特性
提示:一旦使用Object.defineProperty给对象添加属性,那么如果不设置属性的特性,那么configurable、enumerable、writable这些值都为默认的false
var obj = {};
//定义的新属性后,这个属性的特性中configurable,enumerable,writable都为默认的值false
//这就导致了neykey这个是不能重写、不能枚举、不能再次设置特性
Object.defineProperty(obj,'newKey',{
});
//设置值
obj.newKey = 'hello';
console.log(obj.newKey); //undefined
//枚举
for( var attr in obj ){
console.log(attr);
}
value: 设置属性的值
writable: 值是否可以重写。true | false
enumerable: 目标属性是否可以被枚举。true | false
configurable: 目标属性是否可以被删除或是否可以再次修改特性 true | false
(2).存取器描述
当设置或获取对象的某个属性的值的时候,可以提供getter/setter方法。
getter 是一种获得属性值的方法
setter是一种设置属性值的方法。
当使用存取器描述属性的特性的时候,允许设置以下特性属性:
var obj = {};
Object.defineProperty(obj,"newKey",{
get:function (){} | undefined,
set:function (value){} | undefined
configurable: true | false
enumerable: true | false
});
当使用了getter或setter方法,不允许使用writable和value这两个属性
var obj = {};
var initValue = 'hello';
Object.defineProperty(obj,"newKey",{
get:function (){ //当获取值的时候触发的函数
return initValue;
},
set:function (value){ //当设置值的时候触发的函数,设置的新值通过参数**value**拿到
initValue = value;
}
});
//获取值
console.log( obj.newKey ); //hello
//设置值
obj.newKey = 'change value';
console.log( obj.newKey ); //change value
console.log("initValue=" + initValue) //change value
(附加)
下面的例子展示了如何实现一个自存档对象。 当设置temperature 属性时,archive 数组会获取日志条目。
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 }]