ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

vue2/vue3响应式原理 Proxy/reflect

2022-01-18 19:35:12  阅读:152  来源: 互联网

标签:objProxy obj name vue2 reflect Proxy key console target


 let obj = {
    name:'ll',
    age:18
  }

  Object.keys(obj).forEach(key=>{
    let value  = obj[key]
    Object.defineProperty(obj,key,{
      get:function () {
        console.log('监听到访问'+key);
      return  value
      },
      set:function(newVal){
       console.log('监听到设置' + key);  
         value  = newVal

      }
    })
  })

  obj.name = 'kk'
  obj.age = 30
  console.log(obj.name);
  console.log(obj.age);
  

 

Object.defineProperty :设计的初衷不是监听对象属性,而是定义属性,如果想监听更多的操作比如新增属性,删除属性,Object.defineProperty是无能为力的

proxy :es6新增类,帮我们创建一个代理,如果想监听对象的操作,先创建一个代理,然后对对象所有操作都通过代理完成,代理可以监听我们对原对象的操作

   let obj = {
      name: 'll',
      age: 18
    }

    // const objProxy = new Proxy(obj,捕获器对象)
    const objProxy = new Proxy(obj, {
      // 重写捕获器
      // 获取值时捕获器
      get: function (target, key, receiver) {
        // receiver 调用的代理对象
        // target 就是  目标对象(obj)
        console.log('监听到访问' + key,target);
        return target[key]
      },
      // 设置值时捕获器
      set: function (target,key,newVal,receiver) {
         console.log('监听到设置' + key,target);  
        target[key] = newVal
      }
    })
    objProxy.name = 'kk'
    objProxy.age = 30
    console.log(objProxy.name);
    console.log(objProxy.age);

 如果想监听具体操作需要在 handle中添加对应的捕获器Trap 

 let obj = {
      name: 'll',
      age: 18
    }

    const objProxy = new Proxy(obj, {
       // 监听 in 捕获器
      has: function (target, key) {
        console.log('监听in操作' + key, target)
        return key  in target
      },
      // 
      deleteProperty:function(target, key) {
        console.log('监听delete操作' + key, target)
        delete target[key]
      },
    })

   
    // in操作符
    console.log('name' in objProxy);
    // delete 操作
    delete objProxy.name
console.log(objProxy);

 

 proxy 一共有13种捕获器

应用于函数对象: 函数调用/new 操作符的捕获器

 function foo() {
      
    }
    const fooProxy  = new Proxy(foo,{
      //  监听apply调用
      apply:function(target,thisArg,argArray){
        console.log('监听apply');
        return target.apply(thisArg,argArray)
      },
      // new 操作符
      construct: function (target,argArray,newTarget) {
        console.log('监听new');
        return new target(...argArray)
      }
    })
    // foo()
    // apply调用
    fooProxy.apply({},['abc','cba'])
     // new调用
    new fooProxy('abc','cba')

reflect作用:es6新增api,是一个对象,反射的意思

作用: 提供了操作js对象的方法,Reflect.getPrototypeOf(target)类似 Object.getPrototypeOf

Reflect.defineProperty( target,propertyKey,attributes)类似Object..defineProperty()

早期因为考虑不周全,把没有考虑的一些api都塞到object中,后面考虑到放object里面不合适,然后新增其他的属性(in delete 操作符让js看起来很怪)

Reflect和object 区别:比较 Reflect 和 Object 方法 - JavaScript | MDN

receiver作用:

  const obj = {
      _name:'why',
      get name(){
        // 这里的this 指的是obj 
        return this._name  
        // 这里访问不到objProxy的get方法 绕过objProxy直接访问obj的_name ,不能满足所有访问都经过代理
      },
      set name(newVal){
        this._name = newVal
      }
    }
    const objProxy = new Proxy(obj,{
      get:function(target,key){
          console.log('触发了get方法',key );
        return Reflect.get(target,key) // 去上面的get
      },
      set:function(target,key,newVal){
        Reflect.set(target,key,newVal)
      }
    })
    console.log(objProxy.name,'name'); 

 只有name走了代理,_name 访问没走 (无法拦截)

因为this指向obj ,如果让this指向objProxy,就会经过get(objProxy的)

receiver 的意义就来了,是创建的代理对象,可以拦截所有属性

  const obj = {
      _name:'why',
      get name(){
        // 这里的this 指的是obj 
        return this._name  
        // 这里访问不到objProxy的get方法 绕过objProxy直接访问obj的_name ,不能满足所有访问都经过代理,加上receiver this指向objProxy 
      },
      set name(newVal){
        this._name = newVal
      }
    }
    const objProxy = new Proxy(obj,{
      get:function(target,key,receiver){
          console.log('触发了get方法',key );
        return Reflect.get(target,key,receiver) // 去上面的get
      },
      set:function(target,key,newVal,receiver){
        Reflect.set(target,key,newVal,receiver)
      }
    })
    console.log(objProxy.name,'name'); 

 

reflect中construct的作用:

 function Student(name,age) {
      this.name = name;
      this.age = age;
    }

    function  Teacher() {
      
    }

     // 执行student函数内容,创建出来是teacher对象
   const teacher =  Reflect.construct(Student,['why,18'],Teacher)
   console.log(teacher,'teacher');
   console.log(teacher.__proto__===Teacher.prototype );

 响应式:

大白话: MM有一个初始化的值,有一段代码用到了这个值,那么在MM有一个新值,这段代码可以自动重新执行(相关代码都自动执行)

    let m = 100  // m发生变化下面的代码自动执行

    //一段代码 
    console.log(m);
    console.log(m*2);

vue3 响应式的粗糙实现

  //  封装响应式函数

  let activeReactiveFn = null
    // 收集所有属性的依赖
    class Depend {
      constructor(){
        // 用set 避免重复
        this.reactiveFns = new Set() 
      }
      depend(){
        if(activeReactiveFn){
          this.reactiveFns.add(activeReactiveFn)
        }

      };
      notify(){
        this.reactiveFns.forEach(fn => {
           fn()
        })
      }
    }
    // 封装一个响应式函数
    // const depend = new Depend()

     
    function watchFn (fn){
       activeReactiveFn = fn
       fn()
       // 用完销毁
      activeReactiveFn = null
    }

    // 正确收集依赖

    //封装一个获取depend函数
    const targetMap = new WeakMap()
    // 依赖收集的数据结构  
    function getDepend (target,key){
      // 获取对应depend 
     let map =  targetMap.get(target,key)
     if(!map){
       map = new Map()
       targetMap.set(target,map)
     }
     // 根据key获取depend对象
     let depend = map.get(key)
     if(!depend){
       depend = new Depend()
       map.set(key,depend)
     }
     return depend
    }

    function reactive(obj) {
      return  new Proxy(obj, {
        // 获取值的捕获器
        get: function (target, key, receiver) {
          // 根据target,key获取对应的depend
          const depend = getDepend(target, key)
          // 给depend对象添加函数
          depend.depend()

          return Reflect.get(target, key, receiver);
        },
        // 设置值的捕获器 
        set: function (target, key, newVal, receiver) {
          Reflect.set(target, key, newVal, receiver);
          const depend = getDepend(target, key)
          depend.notify()
        },
      });  
      
    }
    const obj = {
      name:'wy', //一个属性对应一个dep对象
      age:18 ,
    }
 

    // 监听属性变化,自动执行notify
  const objProxy = reactive(obj)

     watchFn(function () {
        console.log('我是 name', objProxy.name);
      });
      watchFn(function () {
        console.log('我是 age', objProxy.age);
      })

      objProxy.name = 'pp'

      const infoProxy =reactive({
        address: 'tianjin'
      })

  watchFn(function () {
    console.log(infoProxy.address,'监听地址的变化');
  })
  infoProxy.address = 'beijing'

 

标签:objProxy,obj,name,vue2,reflect,Proxy,key,console,target
来源: https://blog.csdn.net/web_girls/article/details/122483009

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有