100行代码一步步讲解Vue如何劫持修改Array方法

136 阅读1分钟
// Vue 2 数组方法劫持
function def(obj, key, val, enumerable) {
  console.log(enumerable, !!enumerable)
  Object.defineProperty(obj, key, {
    value: val,
    enumerable: false, // false 为不可枚举
    writable: true,
    configurable: true
  })
}
const methods = [
  'push',
]
var aryPro = Array.prototype
// 数组上的方法们
var arrayMethods = Object.create(Array.prototype)
methods.forEach(method => {
  arrayMethods[method] = function (...args) {
    const result = aryPro[method].apply(this, args); // 调用原生的数组方法
    // push unshift 添加的元素可能还是一个对象
    let inserted; // 当前用户插入的元素
    let ob = this.__ob__;
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args;
        break;
      case 'splice': // 3个  新增的属性 splice 有删除 新增的的功能 arr.splice(0,1,{name:1})
        inserted = args.slice(2)
      default:
        break;
    }
    if (inserted) ob.observerArray(inserted); // 将新增属性继续观测
    return result;
  }
})

const arrayKeys = Object.getOwnPropertyNames(arrayMethods) // 这里我只监听了 push方法

console.log(arrayKeys) // push 因为上面对push进行了修改 通过 def 函数

// 监听函数入口
/**
* Augment a target Object or Array by intercepting
* the prototype chain using __proto__
*/
function protoAugment(target, src) {
  /* eslint-disable no-proto */
  target.__proto__ = src
  /* eslint-enable no-proto */
}
/**
 * Augment a target Object or Array by defining
 * hidden properties.
 */
/* istanbul ignore next */
function copyAugment(target, src, keys) {
  for (let i = 0, l = keys.length; i < l; i++) {
    const key = keys[i]
    def(target, key, src[key])
  }
}
function runObserve(val) {
  this.value = val
  let value = val
  def(value, '__ob__', this)
  if (Array.isArray(val)) {
    // 对 Array 的 __proto__ 添加 push 原来的方法
    if ('__proto__' in {}) {
      protoAugment(val, arrayMethods)
    } else {
      debugger
      copyAugment(value, arrayMethods, arrayKeys)
    }
    // 调用监听数组方法
    observeArray(value)

  } else {
    // 监听对象
  }
}
// 监听数组

function observeArray(arys) {
  // 循环数组中的每一个项目
  for (let i = 0, l = arys.length; i < l; i++) {
    // 对每一个项目进行监听
    observe(arys[i])
  }
}
const hasOwnProperty = Object.prototype.hasOwnProperty
function hasOwn(obj, key) {
  return hasOwnProperty.call(obj, key)
}
function isObject(obj) {
  return obj !== null && typeof obj === 'object'
}

// 监听数组的方法
function observe(value, asRootData) {
  // 如果值不存在 out
  debugger
  if (!isObject(value) || value) {
    return
  }
  // 声明一个值
  let ob
  /// 判断这个要监听的项目是否有原型属性有就把这个值的原型属性添加到 ob上 
  if (hasOwn(value, '__ob__') && value.__ob__) {
    ob = value.__ob__
  }
  if (asRootData && ob) {
    ob.vmCount++
  }
  // 直接返回这个值的原型属性方法
  return ob
}
var test = [1, 2, 3, 4]
new runObserve(test)