接上篇:手摸手从0实现简版Vue --- (对象劫持)javascript
前面已经完成了对data
数据里对象的劫持,可是针对数组的变化,Object.defineProperty
不能很好的支持,因此在Vue
中,采起了对数组原生方法进行劫持的操做,来保证数据能够被正常监测到。vue
因为原来的方法不能正常对数组进行数据劫持,因此咱们要对data的数据类型进行区分,因此咱们须要改写Observer
类:java
class Observer { constructor(data) { // data === vm._data // 将用户的数据使用 Object.defineProperty从新定义 if (Array.isArray(data)) { // 对数组方法进行劫持, 让数组经过链来查找咱们本身改写的原型方法 data.__proto__ = arrayMethods; } else { this.walk(data); } } // ... }
经过使用data.__proto__
将数组上的方法换成咱们改写的原型方法 arrayMethods
,咱们新建一个array.js
来重写咱们的数组方法。git
/** * 拦截用户调用的push、shift、unshift、pop、reverse、sort、splice数组方法 */ // 获取老的数组方法 let oldArrayProtoMethods = Array.prototype; // 拷贝新的对象,用来查找老的方法, 不修改原型上的方法 export let arrayMethods = Object.create(oldArrayProtoMethods); let methods = [ 'push', 'pop', 'unshift', 'shift', 'sort', 'splice' ]; methods.forEach(method => { arrayMethods[method] = function(...args) { // 函数劫持 let result = oldArrayProtoMethods[method].apply(this, args); console.log('调用数组更新方法'); return result; } });
此时咱们去调用vm.arr.push(1, 2, 3)
会打印调用数组更新方法,说明此时的push
方法是调用的咱们重写的方法。github
若是咱们调用vm.arr.push({a: 1})
,那咱们也须要对数组新增的对象属性进行监测,也就是须要添加observe
观测,因此还须要进一步改写数组的方法:segmentfault
/** * 对数组新增的元素进行劫持 * @param {*} inserted */ +export function observerArray(inserted) { + for (let i = 0; i < inserted.length; i++){ + observe(inserted[i]); // 还须要对数组里面的内容进行监测 + } +} methods.forEach(method => { arrayMethods[method] = function(...args) { // 函数劫持 let result = oldArrayProtoMethods[method].apply(this, args); console.log('调用数组更新方法'); + let inserted; + switch (method) { + case 'push': + case 'unshift': + inserted = args; + break; + case 'splice': + inserted = args.slice(2); // 获取splice(start, deleteCount, [])新增的内容 + default: + break; + } + if(inserted) observerArray(inserted); return result; } });
此时若是咱们数组添加一个对象而且修改对象的值均可以正常被监测到:数组
console.log(vm.arr.push({a: 1})); console.log(vm.arr[3].a = 100);
此时浏览器会打印:浏览器
到这咱们的数组劫持也实现了,下一部分咱们去实现如何将数据经过模板的方式替换到页面并同步改变。app
代码部分可看本次提交commit 函数
但愿各位大佬点个star,小弟跪谢~