1、Object.defineProperty()
1.1、基本使用
- Object.defineProperty(obj,prop,descriptor)
- obj:要添加属性的对象
- prop:属性
- descriptor:配置对象
let person = {}
let personName = '万人'
//给person添加一个'name'属性
Object.defineProperty(person,'name',{
//getter,当访问该属性时触发
get(){
console.log('访问了name属性')
return personName
},
//setter,修改了该属性时触发
set(value){
console.log('修改了name属性');
personName = value
}
})
//console 访问了name属性
//console 万人
console.log(person.name)
//console 修改了name属性
person.name = '帅哥'
1.2、Object.defineProperty的缺点
- 1、通过上面可以看到,Object.defineProperty()只能监听一个属性的变化,如果要监听多个属性的变化,只能使用Object.keys(obj)进行遍历
let person = {
name:'万人',
age:18,
address:'广州'
}
//设置一个中转函数
function Observer(obj){
Object.keys(obj).forEach((key)=>{
defineProperty(obj,key,obj[key])
})
}
//对多个属性进行监听
function defineProperty(obj,key,value){
Object.defineProperty(obj,key,{
get(){
console.log(`访问了${key}属性`);
return value
},
set(val){
console.log(`修改了${key}属性`)
value = val
}
})
}
//对person上的属性进行监听
Observer(person)
//console 访问了name属性
//console 万人
console.log(person.name)
-
上面的例子可以看得出很麻烦,而且需要设置于一个中转函数
-
2、当对象中还嵌套了一个对象的时候,如果嵌套的对象中的属性发生了变化,Object.defineProperty同样也无法监听出来。如果想深度监听一个对象,我们就得设置递归
let person = {
name: '万人',
age: 18,
address: '广州',
school: {
schoolName: '家里蹲',
schoolAddress: '家里'
}
}
//设置一个中转函数
function Observer(obj) {
//如果传入的不是一个对象,return
if (Object.prototype.toString.call(value).slice(8, -1) !== 'Object') {
Observer(value)
}
Object.keys(obj).forEach((key) => {
defineProperty(obj, key, obj[key])
})
}
//对多个属性进行监听
function defineProperty(obj, key, value) {
//如果对象的属性也是一个对象,递归进入该函数,进行监听
if (Object.prototype.toString.call(value).slice(8, -1) === 'Object') {
Observer(value)
}
Object.defineProperty(obj, key, {
get() {
console.log(`访问了${key}属性`);
return value
},
set(val) {
console.log(`修改了${key}属性`)
value = val
}
})
}
//对person上的属性进行监听
Observer(person)
//console 修改了name属性
//console 访问了name属性
person.school.schoolName = 'wr'
-
3、Object.defineProperty也无法原生监听到数组
-
4、Object.defindeProperty也无法监听到对象新增属性/删除属性
2、Proxy
-
vue3中使用了proxy,就可以很好解决上面的问题了
-
基本语法:
-
const obj = new Proxy(target,handler)
-
被代理后返回的对象 = new Proxy(被代理对象,要代理对象的操作)
-
常用方法:
-
get(target, propKey, receiver)
-
set(target, propKey, value, receiver),要返回一个布尔值
-
has(target,propKey):拦截propKey in proxy的操作,返回一个布尔值
-
deleteProperty(target,propKey):拦截delete proxy[propKey]的操作,返回一个布尔值
-
construct(target, args):
-
apply(target, object, args)
-
示例:
let person = {
name: '万人',
age: 18,
address: '广州',
school: {
schoolName: '家里蹲',
schoolAddress: '家里'
}
}
let handler = {
get(obj,key){
console.log(`访问了${key}属性`);
return key in obj ? obj[key] : '不存在该属性'
},
set(obj,key,value){
console.log(`修改了${key}属性`);
obj[key] = value
return true
}
}
let proxyPerson = new Proxy(person,handler)
//console 访问了name属性
//console 万人
console.log(proxyPerson.name)
//console 修改了address属性
proxyPerson.address = '北京'
- 可以看出,proxy代理的是整个对象,而不是对象的某个特定属性,不需要我们通过遍历来逐个进行数据绑定
- 如果要进行深度监听,同样像上面一样加一个递归就好
- 同时proxy还可以对数组进行监听了
- 可以看出proxy对Object.defineProperty进行了很大的优化