zoukankan      html  css  js  c++  java
  • vue深度学习之对象和数组的监听原理

    对象的监听

    定义一个defineReactive对Object.defineProperty进行封装,用于监听对象的调用和改变

    // 传入的key要是计算属性格式哟
    function defineReactive(obj, key, val) {
     const dep = new Dep() // 用于收集依赖的类,具体原理就不讲解了
     Object.defineProperty(obj, key) {
      enumerable: true,
      configurable: true,
      get: function () { // 每次调用到对象属性时都会出发这个函数
        dep.depend()// window.target是一个方法,用于存放调用的实例的依赖
        return val // 返回调用对象时获取到的值
      },
      set: function (newVal) {
       if (val === newVal) return
       for (i = 0; i < dep.length; i ++) {
        dep.notify()  // 更显依赖绑定的数据值,实现页面的重新渲染
       }
       val = newVal
      }
     }
    }
    

    数组的监听

    数组的监听和对象略有不同。除了通过赋值操作改变数组外,还可以通过数组的一些自带方法改变数组,而通过后面那种方式该改变数组setter是监听不到的。

    解决方法:拦截数组的原型,并赋予新的原型。

    // 通过整理,数组中能够改变数组本身的有七个方法
    
    // 数组能够改变自身的方法:
      const arrayProto = Array.prototype
      // Object.create(obj) 创建一个新对象,并且使用现有对象obj作为新对象的__proto__
      const arrayMethods = Object.create(arrayProto);
      
      [
        'push',  // 在数组最后追加一个元素
        'pop',  // 删除最后一个元素
        'unshift', // 在第一个元素位添加一个元素
        'shift',  // 删除第一个元素
        'splice', // 添加或者删除元素
        'sort',  // 排序
        'reverse' // 反向排序
      ].forEach( method => {
        // 缓存原始方法
        var original = arrayProto[method]
     
        Object.defineProperty(arrayMethods, method, {
          value: function mutator (...args) {  // args是调用原型方法时传入的参数
            console.log('this', this) // this指向属性所属对象
            ... // 在这里面执行监听变化的操作
            return original.apply(this, args)
          }
        })
      })
      module.exports = arrayMethods  
    

    只对需要覆盖原型的数组实例进行原型拦截;避免直接改变Array.proptotype造成的全局污染

    const  arrayMethods = require('./3-2拦截器')
    // 让拦截器只覆盖那些响应式数组的原型,而非构造函数Array的原型
    
    // 考虑到有些浏览器不支持原型的情况,要进行判断
    const hasProto = '__ptoto__' in {} // 有酒返回true,没有就返回false
    
    export class Observe {
      // 数组实例; value的实例数组
      constructor (value) {
        this.value = value
        if (Array.isArray(value)) {
          // 添加拦截器
          const augment = hasProto ? protoAugment : copyAugment
          augment(value, arrayMethods, arrayKeys)
              
          } else {
           // 如果浏览器不支持原型,就直接将方法添加到数组上
          }
         
        } else {
          this.walk(value) // 如果数据是其他类型时执行
        }
      }
    }
    
    // 用拦截器直接覆盖原型
    function protoAugment (value, arrayMethods, arrayKeys) {
      value.__proto__ = arrayMethods  
    }
    
    // 将添加了拦截器的方法追加到数组的属性中
    function copyAugment (value, arrayMethods, keys) {
      for(i = 0; i < keys.length; i ++) {
         const key = keys[i]
        def(value, key, src[key])
      }
    }
    

    收集依赖:将数组作为对象的属性传入defineReactive方法

    function defineReactive (data, key, val) {
      if (typeof val === 'object') new Observer()
      let dep = new dep()
      Object.defineProperty(data, key, {
        enumerable: true,
        configurable: true,
        get: function () {
          dep.depend() // 收集array的依赖
          return value
        },
        set: function (newVal) {
          if (val === newVal) return
          dep.notify() // 监听array的变化
          val = newVal
        }
      })
    }
    

    缺陷:像通过直接改变数组项的值(比如:arr[2]=23)或者通过arr.length改变数组的长度是不能被监听到的。通过es6的元编程能力可以解决,后续再了解。

  • 相关阅读:
    复习题之后缀表达式
    专项训练之枚举
    专项训练之二分
    深夜毒物
    笑面的学校日常(14)最近一次更新2017 04 06
    了不起的竞赛生(7)(最近一次更新2017 04 06)
    水题日常——动态规划 洛谷
    Bzoj 1926: [Sdoi2010]粟粟的书架(二分答案+乱搞+主席树)
    Cogs 1708. 斐波那契平方和(矩阵乘法)
    Codevs 1482 路线统计(矩阵乘法)
  • 原文地址:https://www.cnblogs.com/tina-12138/p/13060629.html
Copyright © 2011-2022 走看看