通常情况下,直接给对象添加属性和方法即可,但是Object.defineproperty()会更加强大。
var stu = new Person
stu.name = "张三" // 添加属性
stu.say = function(){} // 添加方法
Object.defineProperty()的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性。它也是vue2的响应式原理。
Object.defineProperty(obj, prop, desc)
属性描述符
,通过它来限制属性的读写行为属性描述符
属性描述符分为两种:「数据描述符」和「存取描述符」。
其中value、writable为数据描述符,get、set为存取描述符,configurable 和 enumerable不受限制。
value:属性值
writable:是否可以修改值,默认为false
let person = {
name: "张三",
}
Object.defineProperty(person, 'age', {
value: 18, // 设置属性值
writable:true, // 是否可修改,默认为false
})
console.log(person.age) // 18
person.age = 20
console.log(person.age) // 20
如果不写 writable 或 writable:false,看到控制台的打印未发生改变
get:当有人读取 person的prop时,get函数(getter)就会被调用,且返回值就是 prop 的值
set:当有人修改 person的prop时,set函数(setter)就会被调用,且会收到修改的具体值
let person = {
name: "张三",
}
let num = 18
Object.defineProperty(person, 'age', {
get: function () {
console.log('有人操作对象了');
return num
},
set: function (value) {
console.log('值被修改了,value:', value);
num = value
},
})
读取prop,触发get函数
person.age // 触发get函数(getter)
修改prop,触发set函数
person.age = 20 // 触发set函数(setter)
同时定义数据描述符和存储描述符会报错
这两个属性描述符既可以与「数据描述符」配合使用,也可以与「存储描述符」配合使用。
表示是否能再次「修改」或「删除」
该属性的属性描述符
,布尔值(默认为false)。
设为true时,才能再次修改该属性的属性描述符,同时该属性也能从对应的对象上被删除。
一般使用Object.defineProperty时,要设置为true,因为需要去更改这些键值。
不设置configurable(为false)时,无法删除
let person = {
name:"张三"
}
Object.defineProperty(person, 'age', {
value: 23,
})
delete person.age
console.log(person.age) // 23
configurable: true时,可以删除
let person = {
name:"张三"
}
Object.defineProperty(person, 'age', {
value: 23,
configurable: true, // 是否可以被删除或修改
})
delete person.age
console.log(person.age) // undefined
修改
属性值的修改有两种方式,一种是赋值的形式object.prop = xxx
,另一种是通过再次属性定义Object.defineProperty()
并为同一prop重新设置value的方式。
这里所说的「修改」不同于上文说的「数据描述符」writable: true 指是否可以修改属性值,而是,是否可以通过属性定义的方式修改描述符
。
let person = {
name:"张三"
}
Object.defineProperty(person, 'age', {
value: 23,
writable: false, // 这里为false只是禁止通过直接赋值的形式修改属性值
configurable: true, // 是否可以被删除或修改
})
// 通过属性定义的方式修改描述符value
Object.defineProperty(person, 'age', {
value: 18 // 这里重新定义了属性值,因为 configurable 为 true,所以可以修改
})
console.log(person.age); // 18
// 通过赋值的形式修改值,由于writable为false,所以不可修改
person.age = 20
console.log(person.age); // 18
let person = {
name:"张三"
}
Object.defineProperty(person, 'age', {
value: 23,
writable: true,
configurable: false, // 是否可以被删除或修改
})
// 通过属性定义的方式修改描述符value
Object.defineProperty(person, 'age', {
value: 18 // 这里重新定义了属性值,因为 configurable 为 true,所以可以修改
})
console.log(person.age); // 18
// 通过赋值的形式修改值,由于writable为true,所以可以修改
person.age = 20
console.log(person.age); // 20
不要混淆writable和configurable
,writable指是否可以通过,configurable是指是否可以以属性定义的方式修改属性值。表示属性是否可以被枚举(遍历),布尔值(默认为false)。
let person = {
name:"张三"
}
Object.defineProperty(person, 'age', {
value: 23,
})
console.log(person.age) // 23
上述代码,即使设置了age属性,并且也可以取到age的值,但是直接打印person,并未发现新增的age属性,结果如下:
使用 for…in 遍历,或者 Object.keys方法 也无法读取新增的age属性
for (let key in person) {
console.log(key) // name
}
console.log(Object.keys(person)) // ['name']
想让新增属性也可以被遍历,将 enumerable描述符 设置为true即可
let person = {
name:"张三"
}
Object.defineProperty(person, 'age', {
value: 23,
enumerable: true, // 是否可枚举(遍历)
})
console.log(person);
for (let key in person) {
console.log(key)
}
console.log(Object.keys(person));
如此发现,新增属性age属性可遍历
注意
数据描述符(value、writable)和 存取描述符(get、set),不允许同时使用
,否则会报错,configurable 和 enumerable不做限制。
get 和 set不必要同时存在,也可以单独配置。