第十二节: 对数组进行劫持

61 阅读1分钟

劫持方法的文件 observer/index.js

import { ArrayMethods } from './arr';//给数组添加原型

export function observer(data) {
  // 1.判断data不是object不做劫持
  if(typeof data != 'object' || data == null ) {
    return data;
  }
  // 2.**对象 通过一个类劫持**
  return new Observer(data);
}

//  Object.defineProperty
class Observer {
  constructor(value) {
    // 判断是对象 还是 数组(观测数据)
    if(Array.isArray(value)) {//数组
      // => **给数组添加我们重写的方法** @@@@@@@@@@@@@@@@@
      value.__proto__ = ArrayMethods;
    }else {//对象
      this.walk(value);
    }
  }
  walk(data) {
    let keys = Object.keys(data);
    for(let i = 0; i < keys.length; i++) {
      // 对我们的每个属性进行劫持
      let key = keys[i];//属性
      let value = data[key];//属性值
      defineReactive(data, key, value);
    }
  }
}

/**
 * 对对象中的属性进行劫持
 * @param {*} data
 * @param {*} key 
 * @param {*} value 
 */
function defineReactive(data, key, value) {
  // 可能是个对象就需要递归了: 深度劫持
  observer(value);
  Object.defineProperty(data, key, {
    get() {
      return value;
    },
    set(newValue) {
      // 设置的值相同
      if(newValue === value) return value;
      observer(value);//避免 对象 替换 地址失去劫持
      value = newValue;
    }
  })
}

// 对数组进行劫持
// 方法函数劫持,重写数组方法

数组劫持文件 observe/arr.js

// 重写数组方法
// 1. 获取原来的数组方法
let oldArrayProtoMethods = Array.prototype;

// 2. 继承
export let ArrayMethods = Object.create(oldArrayProtoMethods);

// 3. 需要劫持方法
let methods = [
  'push',
  'pop',
  'unshift',
  'shift',
  'splice',
  'reverse',
  'sort'
]

methods.forEach(item => {
  ArrayMethods[item] = function(...args) {
    let result = oldArrayProtoMethods[item].apply(this, args);
    return result;
  }
})