监听对象属性的改变可以用Object.defineProperty(vue2响应式用法),但是也有很大弊端,就是新设置或者删除一个属性的时候无法监听到,但是vue2内部通过响应式系统进行优化,我们也可以通过$set和$delete设置或者删除一个响应式属性
ES6新增一个Proxy用于监听对象的属性(功能比属性描述符更全)(vue3响应式用法)
const obj = {name:"李斯",age:18}
const proxy = new Proxy(obj,{ //第二个参数为捕获器 里面有13种方法如下图
get(target,key){
console.log(`${key}被获取`);
return target[key]
},
set(target,key,newVal){
console.log(`${key}被设置值了`);
target[key] = newVal
},
// 监听进行in操作符查找对象属性
has(target,key){
console.log(`${key}被查找了`);
return key in target
},
// 监听删除对象时
deleteProperty(target,key){
console.log(`${key}被删除了`);
delete target[key]
}
})
console.log("name" in proxy);
delete proxy.age
相比于上面捕获器的用法,实际上更多个是与Reflect连用,return一个布尔值
// 参数receiver的作用,只有get和set有传,代表代理对象本身
const obj = {
_name : "李斯",
get name(){
return this._name
},
set name(newVal){
this._name = newVal
}
}
const objProxy = new Proxy(obj,{
get(target,key,receiver){
console.log("捕获到get操作----",key); //此时监听到原对象get中获取_name属性的方法了
return Reflect.get(target,key,receiver)
},
set(target,key,newVal,receiver){
console.log("捕获到set操作----",key);
return Reflect.set(target,key,newVal,receiver)
}
})
console.log(objProxy.name);
objProxy.name = "王五"
针对于Reflect用法的好处主要体现在receiver参数
const parent = {
_a: 1,
get a(){
console.log(this === child);
return this._a
}
}
const handler = {
// 此时receiver为直接调用的对象child,传给代理对象get中的this就变为child了
get(target,key,receiver){
// 使用这种的话代理对象get中的this就指向parent,此时打印 false 和 1
// return target[key]
// 使用这种的话代理对象get中的this就指向child,此时打印 true 和 2
return Reflect.get(target,key,receiver)
},
set(target,key,value,receiver){
return Reflect.set(target,key,value,receiver)
}
}
const proxyObj = new Proxy(parent,handler)
// 实现继承,child继承于代理对象proxyObj
const child = Object.setPrototypeOf({_a:2},proxyObj)
console.log(child.a);
Reflect.construct(target,args,newTarget)的使用,借用target的构造方法new newTarget
function Student(name,age){
this.name = name
this.age = age
}
function Teacher(){
}
// 此时new Teacher执行的是Student的构造方法
const obj = Reflect.construct(Student,["李斯",18],Teacher)
console.log('obj: ', obj); // Teacher {name: '李斯', age: 18}
console.log(obj.__proto__ === Teacher.prototype); //true
自己乱七八糟写了个vue3的响应式,随便看看就好
<body>
姓名:<p></p>
年龄:<span></span>
<script type="text/javascript">
const obj = {
name:"李斯", //depend
age:16 //depend
}
class Depend {
constructor(){
// 存放依赖
this.reactiveFns = []
}
addDepend(objProxy,key){
// 这里获取属性所对应的依赖
this.reactiveFns = dependWeakMap.get(objProxy).get(key)
}
notify(){
// 遍历执行所有依赖的方法
this.reactiveFns.forEach(item=>item())
}
}
const objProxy = new Proxy(obj,{
get(target,key,receiver){
return Reflect.get(target,key,receiver)
},
set(target,key,newVal,receiver){
const setVal = Reflect.set(target,key,newVal,receiver)
// 响应式查找并执行依赖
depend.addDepend(receiver,key)
depend.notify()
return setVal
}
})
document.getElementsByTagName("p")[0].innerHTML = objProxy.name
document.getElementsByTagName("span")[0].innerHTML = objProxy.age
// 这里通过Map设定响应对象每个属性对应的依赖
const dependMap = new Map([["name",[nameFoo]],["age",[ageFoo]]])
// 这里通过WeakMap绑定响应对象以及他所对应所有的依赖
const dependWeakMap = new WeakMap([[objProxy,dependMap]])
const depend = new Depend()
function nameFoo(){
document.getElementsByTagName("p")[0].innerHTML = objProxy.name
console.log("触发了name事件-------");
}
function ageFoo(){
document.getElementsByTagName("span")[0].innerHTML = objProxy.age
console.log("触发了age事件-------");
}
setTimeout(()=>{objProxy.name = "王五"},3000)
setTimeout(()=>{objProxy.age = 18},5000)
</script>
</body>
---有不理解的地方欢迎留言讨论哦