proxy
ES6中新增Proxy类,可用于创建Proxy对象去监听对象的所有操作(其包含13种捕获器,能监听所有的对象操作)
监听对象的操作
方式一:Object.defineProperty
缺陷:
- 其初衷是定义普通的属性,而本来就不是用于监听对象中所有属性的,,但后来强行将它变成了数据属性描述符
- 其无法完全监听到对象的所有操作(新增属性、删除属性)
const obj={name:'rv',age:20}Object.keys(obj).forEach((key)=>{// 利用闭包实现保存属性值let value=obj[key];Object.defineProperty(obj,key,{get(){console.log(`[get]obj.${key}`)return value},set(newValue){console.log(`[set]obj.${key}=${newValue}`)value=newValue}})})obj.age // [get]obj.age,20obj.age=30 // [set]obj.age=30,30
方式二:Proxy
const obj={name:'rv',age:20}// 创建代理对象 && 基本使用/*创建代理对象时,若不传target或handler则报错,因此需两个参数*/const proxy1=new Proxy(obj,{})proxy1.age // 20proxy1.age=30 // 30// 重写捕获器/*参数解析:target:代理对象key:捕获操作的keyreceiver:调用的代理对象*/const proxy2=new Proxy(obj,{// get捕获器get(target,key){console.log(`[get]obj.${key}`)return target[key]},// set捕获器set(target,key,newValue){console.log(`[set]obj.${key}=${newValue}`)target[key]=newValue}})
Proxy的捕获器
get(target,key,receiver)
监听获取值时的捕获器set(target,key,value,receiver)
监听设置值时的捕获器has(target,key)
监听in的捕获器deleteProperty(target,property)
监听delete的捕获器apply(target, thisArg, argumentsList)
监听函数调用操作的捕获器construct(target, argumentsList, newTarget)
监听new操作符的捕获器getPrototypeOf()
监听Object.getPrototypeOfsetPrototypeOf()
监听Object.setPrototypeOfisExtensible()
监听Object.isExtensiblepreventExtensions()
监听Object.preventExtensionsgetOwnPropertyDescriptor()
监听Object.getOwnPropertyDescriptordefineProperty()
监听Object.definePropertyownKeys()
监听Object.getOwnPropertyNames,Object.getOwnPropertySymbols
receiver作用
receiver参数传入的是proxy对象自身,可用于Reflect.get和Reflect.set
/* 应用场景:拦截的对象中存在get和set操作导致发现无法拦截get、set操作中的this操作例如此处仅能拦截name的get、set操作,而没有_name的get、set操作*/const obj={_name:'rv',get name(){return this._name;},set name(value){this._name=value;}}const proxy=new Proxy(obj,{get(target,key,receiver){console.log(`[get]obj.${key}`);/* 写法一: */return Reflect.get(target,key)},set(target,key,value,receiver){console.log(`[set]obj.${key}=${value}`)/* 写法二: */return Reflect.set(target,key,value,receiver)},})/* 写法一此处仅拦截name一次,而没有拦截_name */obj.name // [get]obj.name 'rv'/* 写法二此处拦截正常 */obj.name='why' //[set]obj.name=why [set]obj._name=why true
Reflect
ES6新增的一个API,Reflect是一个对象
Reflect作用
它主要提供了很多操作JavaScript对象的方法,类似于Object中操作对象的方法
- 早期ECMA规范中并没有考虑到对对象本身的操作如何设计的更加规范,因此将这些API放到了Object上
- 但Object作为一个构造函数,这些操作实际上放到它身上并不合适
- 另外JavaScript中还包含一些类似
in,delete操作符,让JS看起来有一些奇怪 - 所以ES6中新增了
Reflect,将这些操作都集中到了Reflect对象上
Reflect方法
Reflect.construct()
作用类比new操作符,且提供newTarget可将创建的对象修改其隐式原型指向其他构造函数的显式原型
Reflect.construct(target, argumentsList, ?newTarget)/*参数介绍:target:构造函数argumentList:参数数组newTarget:创建对象的原型指向其他构造函数*/asdasda
Proxy,Reflect实现响应式
Vue3响应式原理实现
// 待实现响应式函数const obj={name:'rv',age:20}// 依赖保存结构 WeakMap=>Map=>dependconst targetMap=new WeakMap()/* Proxy代理 */const proxy=new Proxy(obj,{get(target,key,receiver){// 收集依赖const depend=getDepend(target,key)depend.addDepend(activeReactiveFn)return Reflect.get(target,key,receiver)},set(target,key,value,receiver){Reflect.set(target,key,value,receiver)// 响应式通知const depend=getDepend(target)depend.notify()}})/* 封装获取depend函数 */function getDepend(target,key){// 根据target获取maplet map=targetMap.get(target)if(!map){map=new Map();targetMap.set(target,map)}// 根据map获取depend对象let depend=map.get(key)if(!depend){depend=new Depend()map.set(key,depend)}return depend;}/* 封装依赖收集类 */class Depend{constructor(){this.reactiveFn=[]}addDepend(fn){this.reactiveFn.push(fn)}notify(){this.reactiveFn.forEach(fn=>{fn()})}}/* 封装依赖收集函数 */let activeReactiveFn=null;function warchFn(fn){activeReactiveFn=fnfn()activeReactiveFn=null}
