数组数据劫持
实现目的: 对数组方法进行劫持,如果调用数组方法了,那么监听数组更新,如果添加数据那么进行数据劫持
如果数组中的每个数据都进行劫持的话那么就是产生一大堆的冗余数据,极大的影响性能,所以对数据方法例如(push,unshift,splice)进行劫持
实现思路
使用Object.create()方法,复制出数组的原型
在复制出的原型上进行新方法的重写,这样不影响原有的数组的方法 aop编程
对数组进行循环,如果数组里面是数组类型或者对象类型进行劫持
// array.js let oldArrayProto = Array.prototype; // 获取数组的原型 export let newArrayProto = Object.create(oldArrayProto) // 对数组方法进行劫持 let methods = [ "shift", "push", "unshift", "splice", "pop", "reverse", "sort" ] // 只需要劫持添加新数据的方法 methods.forEach(method => { newArrayProto[method] = function(...args) { // 重写了数组的方法 const result = oldArrayProto[method].call(this, ...args) // 内部的方法 函数的劫持 切片编程 // 对新增的数据再次劫持 let inserted; let ob = this.__ob__; switch (method) { case "push": case "unshift": inserted = args; break; case "splice": inserted = args.slice(2); default: break; } console.log(inserted); if (inserted) { // 对新增的内容进行观测 ob.observeArray(inserted); } return result } }) // index.js import { newArrayProto } from "./array"; class Observe { constructor(data) { // 放在data属性上,这样其他地方也可以使用 Object.defineProperty(data, '__ob__', { value: this, enumerable: false, // 将__ob__设置不可枚举的 (循环的时候无法获取) }) // data.__ob__ = this; // 如果有ob则表示这个属性已经被观测过了 // Object.defineProperty 只能劫持已经存在的属性,后增的删除的是不知道的 if (Array.isArray(data)) { data.__proto__ = newArrayProto // 保留数组原有的特性,并且重写数组的部分方法 this.observeArray(data); // 如果数组中放的是对象可以被监测到 } else { this.walk(data) } } walk(data) { // 循环对象,依次劫持 // 重新定义属性 性能瓶颈在这 换了proxy性能明显提高 Object.keys(data).forEach(key => defineReactive(data, key, data[key])) } observeArray(data) { data.forEach(item => observe(item)) } } export function defineReactive(data, key, val) { // 闭包,get,set引用了val不销毁 observe(val) // 递归 对所有属性进行劫持 Object.defineProperty(data, key, { get() { return val }, set(newVal) { if (val === newVal) return observe(newVal) val = newVal }, }); } export default function observe(data) { // 对对象进行劫持 if (typeof data !== 'object' || data == null) { return // 只对对象进行劫持 } if (data.__ob__ instanceof Observe) { return data.__ob__ } // 如果一个对象被劫持了,那就不需要在被劫持了(要判读一个对象是否被劫持过,可以增添一个实例,用实例来判断一下) return new Observe(data) }