Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。这个方法有三个参数,分别是对象,对象的属性值(key)和属性描述符,如果开始该对象没有这个属性值,就会就会定义一个新属性,如果开始就有,就是修改。
let obj = { a:1 }
Object.definePropety(obj,'a',{ //修改
value:10,
})
Object.definePropety(obj,'b',{ //新建属性
value:20
})
console.log(obj) // {a:10,b:20}
属性描述符有两种类型:数据描述符和存取描述符,这两者有两个相同的属性和两个特有的属性。
数据描述符拥有以下属性:
key | 作用 | 可选值 | 默认值 |
---|---|---|---|
value | 属性对应的值 | 任意值都可以 | undefined |
writable | 控制该属性是否能被=赋值 | true或false | false |
enumerable | 控制该属性是否出现在对象的枚举属性中 | true或false | false |
configurable | 控制该属性的除 value 和 writable 特性外的其他特性是否可以被修改和该属性能否被删除 | true或false | false |
存取描述符拥有有以下属性:
key | 作用 | 可选值 | 默认值 |
---|---|---|---|
enumerable | 控制该属性是否出现在对象的枚举属性中 | true或false | false |
configurable | 控制该属性的除 value 和 writable 特性外的其他特性是否可以被修改和该属性能否被删除 | true或false | false |
get | 属性的 getter 函数,当访问该属性时,会调用此函数 | function | undefined |
set | 属性的 setter 函数,当属性值被修改时,会调用此函数 | function | undefined |
各属性介绍
1.value属性:给该属性赋的值
let obj = {}
Object.defineProperty(obj,'a',{
value:10
})
console.log(obj) //{a:10}
2.writable 属性:控制该属性是否能被 “=” 赋值:
// 'use strict'
let obj = {};
Object.defineProperty(obj,"a",{
value:11,
writable:false,
})
console.log(obj.a); // 11
obj.a = 20;
console.log(obj.a) // 11
在普通模式下,给writable为false的属性使用赋值不会改变该属性,也不会报错,在严格模式下(‘use strict’),会报以下的错误:
注意:就算writable为false,你依然可以通过Object.defineProperty()方法给该属性赋值。代码如下
let obj = {};
Object.defineProperty(obj,"a",{
value:11,
writable:false,
})
console.log(obj.a); // 11
Object.defineProperty(obj,'a',{
value:20
})
console.log(obj.a) // 20
3.enumerable 属性:控制该属性是否能被枚举(for…in循环、Object.keys()等能否获取到该值),如果时false,就拿不到该属性
let obj = {a:1,b:2}
Object.defineProperty(obj,'c',{
value:3,
enumerable:false
})
console.log(obj.c) // 3
console.log(Object.keys(obj)) // ["a","b"]
4.configurable 属性:表示对象的属性是否可以被删除,以及除 value 和 writable 属性外的其他属性是否可以被修改。
下面是对能否删除的测试
// 'use strict'
let obj = {};
Object.defineProperty(obj,'a',{
value:10,
configurable:false
})
delete obj.a
console.log(obj.a) // 10,delete没有成功,但也不会报错,如果使用严格模式('use strict'),就会报错
下面是对能否修改属性测试
"use strict"
let obj = {a:1,b:2}
Object.defineProperty(obj,'c',{
value:3,
enumerable:false,
configurable:false
})
Object.defineProperty(obj,'c',{
enumerable:true // 会报错:Uncaught TypeError: Cannot redefine property: c
})
Object.defineProperty(obj,'c',{
configurable:true // 会报错:Uncaught TypeError: Cannot redefine property: c
})
注意: 当 configurable 设置为 false 时,无法再次修改 configurable 的值,即一旦将该值设置为false,就无法再修改为true;
5.set 方法和set方法,就是对象的自定义 getters 和 setters ;在给对象的改属性赋值时,就会执行你写的set函数里面的代码,当你获取改属性时,获取到的就是你get函数里面的返回值;这两个一般会同时出现,如果只有get,没有set,那么改属性无法被改变,严格模式下还会报错;如果只有set,没有get,那么无法获取到改属性值(一直为 undefined );
let obj = {};
let arr = [];
Object.defineProperty(obj,'a',{
set(val){
arr.push(val)
},
get(){
return arr
}
})
obj.a = 1;
console.log(obj.a) // [1]
obj.a = 2
console.log(obj.a) // [1,2]
如以上例子,此时给obj的a属性赋值时,就不是覆盖掉之前 a 的值,而是将数据放在一个数组里,获取值的时候也是获取的一个数组;
**注意:**这里set和get函数不能通过 **this.**的形式对自己进行操作,因为对自己赋值时又会执行set和get函数,这样就无限调用,会报 Maximum call stack size exceeded 的错,如以下代码:
let obj = {};
Object.defineProperty(obj,'a',{
set(val){
this.a = val
},
get(){
return this.a
}
})
就会报下面这个错
所以,设置值的时候需要使用另外一个变量或者对象的另外一个属性。
常用操作的默认值
1.在对象进行操作时,我们一般会进行如下操作:
let obj = {};
obj.a = 1
这个操作就相当于进行如下操作:
let obj = {}
Object.defineProperty(obj, "a", {
value : 1,
writable : true,
enumerable : true,
configurable : true
});
2.在使用Object.defineProperty()给属性赋值时,如果只传了value,如下:
let obj = {};
Object.defineProperty(obj,'a',{
value:1
})
此时就相当于进行如下操作:
let Object.defineProperty(obj,'a',{
value:1,
writable:false,
enumerable:false,
configurable:false
})
此时对象的值是只读的,所以使用Object.defineProperty() 给属性赋值的时候不能偷懒,一般还是用第一种方法舒服 \(^o^)/ ,Object.defineProperty() 方法一般用于对该属性赋值时使用set和get进行自己相应的操作。