Object.defineProperty()这个函数很有意思,为了充分理解这个函数,特总结如下。欢迎批评指正。
其中引用了MDN,但做出了详细的解读。文章代码可以自行复制后测试一下,再配合本文味道更佳!
对象的定义和赋值
我们常对对象进行定义和赋值,如下
var obj={}
obj.name = '此时此地GS'
obj['gender'] = 'male'
console.log(obj.name);
// => 此时此地GS
console.log(obj.gender);
// => male
当需要对一个对象的属性进行精细控制时,就需要用到Object.defineProperty()。Object.defineProperty()方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
Object.defineProperty()的语法
其语法如下:
Object.defineProperty(obj, prop, descriptor)
其中:
obj是要定义的对象
prop是要定义或修改的属性的名称或Symbol
在ES6中,由于 Symbol类型的特殊性,用Symbol类型的值来做对象的key与常规的定义或修改不同,而`Object.defineProperty` 是定义key为Symbol的属性的方法之一。
descriptor是要定义或修改的属性描述符。
descriptor属性描述符的具体使用
如何精准控制prop属性呢?就是通过descriptor这个属性描述符实现。descriptor属性描述符是一个对象,里面有6个键值对。如果描述符中的某些键值对被省略,会使用以下默认值:
按照属性之间的关联程度把这些属性列举如下:
一、value
该属性对应的值,可以是任何有效的 JavaScript 值(数值,对象,函数等)。
这个值定义的时候,相当于常用的obj.name = '此时此地GS'
方法,效果是一样的。
var obj={}
Object.defineProperty(obj,'name',{
value:'此时此地GS',
})
console.log(obj.name);
// => 此时此地GS
二、configurable
当且仅当该属性的 configurable 键值为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。
当 configurable 键值为 false 时,再次更改 descriptor 属性描述符的属性内容是会报错的,包括get/set/enumerable/writable(对value的影响在第三点里详述),甚至他自己 configurable 也无法再重新定义 (我狠起来连自己都打......);
var obj={}
Object.defineProperty(obj,'name',{
configurable:false,
// writable:true,
value:'此时此地GS',
})
Object.defineProperty(obj,'name',{
configurable:true,
// writable:false,
// value:'zs',
})
console.log(obj.name);
// => Cannot redefine property: name
这里也有一个例外,当configurable属性为false时,支持writable 属性单向从 true 变为 false,且不会报错
var obj={}
Object.defineProperty(obj,'name',{
configurable:false,
writable:true,
value:'此时此地GS',
})
Object.defineProperty(obj,'name',{
writable:false,
value:'zs',
})
console.log(obj.name);
// => zs
三、writable
1、value值能否改变,受writable属性控制,当writable属性为true时,不管configurable为true还是false,在Object.defineProperty()内外都可以修改value值
var obj={}
Object.defineProperty(obj,'name',{
value:'此时此地GS',
// configurable:true,
configurable:false,
writable:true,
// writable:false
})
// Object.defineProperty(obj,'name',{
// value:'ls',
// })
obj.name = 'zs'
console.log(obj.name);
// => zs
2、当writable属性为false时,configurable属性为true时,可以通过Object.defineProperty()重新为value赋值
因为value键值对也是属于descriptor这个属性描述符,因此也会受到configurable属性影响。而通过obj.name = 'zs'
这种直接赋值方式无法改变value值。
var obj={}
Object.defineProperty(obj,'name',{
value:'此时此地GS',
configurable:true,
// configurable:false,
// writable:true,
writable:false
})
Object.defineProperty(obj,'name',{
value:'ls',
})
// obj.name = 'zs'
console.log(obj.name);
// => ls
3、当writable属性为false时,configurable属性为false时,在Object.defineProperty()内外都无法重新更改value值
var obj={}
Object.defineProperty(obj,'name',{
value:'此时此地GS',
// configurable:true,
configurable:false,
// writable:true,
writable:false
})
Object.defineProperty(obj,'name',{
value:'ls',
})
// obj.name = 'zs'
console.log(obj.name);
// => Cannot redefine property: name
四、enumerable
当且仅当该属性的 enumerable 键值为 true 时,该属性才会出现在对象的枚举属性中。
如果 enumerable 键值为 false 时,使用(for...in 或 Object.keys 方法)枚举对象属性时,会无法枚举出来。
var obj={gender:'male'}
Object.defineProperty(obj,'name',{
value:'此时此地GS',
enumerable:false
})
console.log(obj);
// => {gender: "male", name: "此时此地GS"}
for(let key in obj ) { console.log( key )}
// => gender
console.log(Object.keys(obj));
// => ["gender"]
五、get
和set
这俩很像,所以放在一起来说,但这两个是可以单独存在的
get
:属性的 getter 函数,如果没有 getter,则为 undefined。当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入 this 对象(由于继承关系,这里的this并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值。
set
:属性的 setter 函数,如果没有 setter,则为 undefined。当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的 this 对象。
用法如下:
var obj={}
var temp
Object.defineProperty(obj,'name',{
get:function(){
return temp
},
set:function(val){
temp = val
}
})
obj.name = '此时此地GS'
console.log(obj.name);
// => 此时此地GS
这个get 或 set 属性无法和 writable 或 value 属性共存,否则报错。为什么呢?大概就是不可能定一个属性即可以对它进行正常读写,又要在它上面架设一层getter/setter来进行访问改写。
var obj={}
var temp
Object.defineProperty(obj,'name',{
value:'此时此地GS',
writable:true,
get:function(){
return temp
},
set:function(val){
temp = val
}
})
// => Cannot both specify accessors and a value or writable attribute
总结
以上就是Object.defineProperty()中descriptor属性描述符的详细使用解读,如果需要对obj中prop的多个属性值进行精细控制,就需要用到Object.defineProperties(),具体某个属性值的描述符内的使用方法同本文内容。
感谢观看,不足之处欢迎批评指正!