proxy

ES6中新增Proxy类,可用于创建Proxy对象去监听对象的所有操作(其包含13种捕获器,能监听所有的对象操作)

监听对象的操作

方式一:Object.defineProperty

缺陷:

  • 其初衷是定义普通的属性,而本来就不是用于监听对象中所有属性的,,但后来强行将它变成了数据属性描述符
  • 其无法完全监听到对象的所有操作(新增属性、删除属性)
  1. const obj={
  2. name:'rv',
  3. age:20
  4. }
  5. Object.keys(obj).forEach((key)=>{
  6. // 利用闭包实现保存属性值
  7. let value=obj[key];
  8. Object.defineProperty(obj,key,{
  9. get(){
  10. console.log(`[get]obj.${key}`)
  11. return value
  12. },
  13. set(newValue){
  14. console.log(`[set]obj.${key}=${newValue}`)
  15. value=newValue
  16. }
  17. })
  18. })
  19. obj.age // [get]obj.age,20
  20. obj.age=30 // [set]obj.age=30,30

方式二:Proxy

  1. const obj={
  2. name:'rv',
  3. age:20
  4. }
  5. // 创建代理对象 && 基本使用
  6. /*
  7. 创建代理对象时,若不传target或handler则报错,因此需两个参数
  8. */
  9. const proxy1=new Proxy(obj,{})
  10. proxy1.age // 20
  11. proxy1.age=30 // 30
  12. // 重写捕获器
  13. /*
  14. 参数解析:
  15. target:代理对象
  16. key:捕获操作的key
  17. receiver:调用的代理对象
  18. */
  19. const proxy2=new Proxy(obj,{
  20. // get捕获器
  21. get(target,key){
  22. console.log(`[get]obj.${key}`)
  23. return target[key]
  24. },
  25. // set捕获器
  26. set(target,key,newValue){
  27. console.log(`[set]obj.${key}=${newValue}`)
  28. target[key]=newValue
  29. }
  30. })

Proxy的捕获器

  1. get(target,key,receiver)
    监听获取值时的捕获器

  2. set(target,key,value,receiver)
    监听设置值时的捕获器

  3. has(target,key)
    监听in的捕获器

  4. deleteProperty(target,property)
    监听delete的捕获器

  5. apply(target, thisArg, argumentsList)
    监听函数调用操作的捕获器

  6. construct(target, argumentsList, newTarget)
    监听new操作符的捕获器

  7. getPrototypeOf()
    监听Object.getPrototypeOf

  8. setPrototypeOf()
    监听Object.setPrototypeOf

  9. isExtensible()
    监听Object.isExtensible

  10. preventExtensions()
    监听Object.preventExtensions

  11. getOwnPropertyDescriptor()
    监听Object.getOwnPropertyDescriptor

  12. defineProperty()
    监听Object.defineProperty

  13. ownKeys()
    监听Object.getOwnPropertyNames,Object.getOwnPropertySymbols

receiver作用

receiver参数传入的是proxy对象自身,可用于Reflect.getReflect.set

  1. /* 应用场景:
  2. 拦截的对象中存在get和set操作
  3. 导致发现无法拦截get、set操作中的this操作
  4. 例如此处仅能拦截name的get、set操作,而没有_name的get、set操作
  5. */
  6. const obj={
  7. _name:'rv',
  8. get name(){
  9. return this._name;
  10. },
  11. set name(value){
  12. this._name=value;
  13. }
  14. }
  15. const proxy=new Proxy(obj,{
  16. get(target,key,receiver){
  17. console.log(`[get]obj.${key}`);
  18. /* 写法一: */
  19. return Reflect.get(target,key)
  20. },
  21. set(target,key,value,receiver){
  22. console.log(`[set]obj.${key}=${value}`)
  23. /* 写法二: */
  24. return Reflect.set(target,key,value,receiver)
  25. },
  26. })
  27. /* 写法一此处仅拦截name一次,而没有拦截_name */
  28. obj.name // [get]obj.name 'rv'
  29. /* 写法二此处拦截正常 */
  30. 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 - JavaScript | MDN (mozilla.org)

Reflect.construct()

作用类比new操作符,且提供newTarget可将创建的对象修改其隐式原型指向其他构造函数的显式原型

  1. Reflect.construct(target, argumentsList, ?newTarget)
  2. /*
  3. 参数介绍:
  4. target:构造函数
  5. argumentList:参数数组
  6. newTarget:创建对象的原型指向其他构造函数
  7. */asdasda

Proxy,Reflect实现响应式

Vue3响应式原理实现

  1. // 待实现响应式函数
  2. const obj={
  3. name:'rv',
  4. age:20
  5. }
  6. // 依赖保存结构 WeakMap=>Map=>depend
  7. const targetMap=new WeakMap()
  8. /* Proxy代理 */
  9. const proxy=new Proxy(obj,{
  10. get(target,key,receiver){
  11. // 收集依赖
  12. const depend=getDepend(target,key)
  13. depend.addDepend(activeReactiveFn)
  14. return Reflect.get(target,key,receiver)
  15. },
  16. set(target,key,value,receiver){
  17. Reflect.set(target,key,value,receiver)
  18. // 响应式通知
  19. const depend=getDepend(target)
  20. depend.notify()
  21. }
  22. })
  23. /* 封装获取depend函数 */
  24. function getDepend(target,key){
  25. // 根据target获取map
  26. let map=targetMap.get(target)
  27. if(!map){
  28. map=new Map();
  29. targetMap.set(target,map)
  30. }
  31. // 根据map获取depend对象
  32. let depend=map.get(key)
  33. if(!depend){
  34. depend=new Depend()
  35. map.set(key,depend)
  36. }
  37. return depend;
  38. }
  39. /* 封装依赖收集类 */
  40. class Depend{
  41. constructor(){
  42. this.reactiveFn=[]
  43. }
  44. addDepend(fn){
  45. this.reactiveFn.push(fn)
  46. }
  47. notify(){
  48. this.reactiveFn.forEach(fn=>{
  49. fn()
  50. })
  51. }
  52. }
  53. /* 封装依赖收集函数 */
  54. let activeReactiveFn=null;
  55. function warchFn(fn){
  56. activeReactiveFn=fn
  57. fn()
  58. activeReactiveFn=null
  59. }