vue源码学习之Object.defineProperty 对数组监听
上一篇中,我们介绍了一下defineProperty对对象的监听,这一篇我们看下defineProperty对数组的监听
数组的变化
先让我们了解下Object.defineProperty()对数组变化的跟踪情况:
vara={}; bValue=1; Object.defineProperty(a,"b",{ set:function(value){ bValue=value; console.log("setted"); }, get:function(){ returnbValue; } }); a.b;//1 a.b=[];//setted a.b=[1,2,3];//setted a.b[1]=10;//无输出 a.b.push(4);//无输出 a.b.length=5;//无输出 a.b;//[1,10,3,4,undefined];
可以看到,当a.b被设置为数组后,只要不是重新赋值一个新的数组对象,任何对数组内部的修改都不会触发setter方法的执行。这一点非常重要,因为基于Object.defineProperty()方法的现代前端框架实现的数据双向绑定也同样无法识别这样的数组变化。因此第一点,如果想要触发数据双向绑定,我们不要使用arr[1]=newValue;这样的语句来实现;第二点,框架也提供了许多方法来实现数组的双向绑定。
对于框架如何实现数组变化的监测,大多数情况下,框架会重写Array.prototype.push方法,并生成一个新的数组赋值给数据,这样数据双向绑定就会触发。
实现简单的对数组的变化的监听
vararrayPush={}; (function(method){ varoriginal=Array.prototype[method]; arrayPush[method]=function(){ //this指向可通过下面的测试看出 console.log(this); returnoriginal.apply(this,arguments) }; })('push'); vartestPush=[]; testPush.__proto__=arrayPush; //通过输出,可以看出上面所述this指向的是testPush //[] testPush.push(1); //[1] testPush.push(2);
在官方文档,所需监视的只有push()、pop()、shift()、unshift()、splice()、sort()、reverse()7种方法。我们可以遍历一下:
vararrayProto=Array.prototype vararrayMethods=Object.create(arrayProto) ;[ 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse' ].forEach(function(item){ Object.defineProperty(arrayMethods,item,{ value:functionmutator(){ //缓存原生方法,之后调用 console.log('array被访问'); varoriginal=arrayProto[item] varargs=Array.from(arguments) original.apply(this,args) //console.log(this); }, }) })
完整代码
functionObserver(data){ this.data=data; this.walk(data); } varp=Observer.prototype; vararrayProto=Array.prototype vararrayMethods=Object.create(arrayProto) ;[ 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse' ].forEach(function(item){ Object.defineProperty(arrayMethods,item,{ value:functionmutator(){ //缓存原生方法,之后调用 console.log('array被访问'); varoriginal=arrayProto[item] varargs=Array.from(arguments) original.apply(this,args) //console.log(this); }, }) }) p.walk=function(obj){ varvalue; for(varkeyinobj){ //通过hasOwnProperty过滤掉一个对象本身拥有的属性 if(obj.hasOwnProperty(key)){ value=obj[key]; //递归调用循环所有对象出来 if(typeofvalue==='object'){ if(Array.isArray(value)){ varaugment=value.__proto__?protoAugment:copyAugment augment(value,arrayMethods,key) observeArray(value) } newObserver(value); } this.convert(key,value); } } }; p.convert=function(key,value){ Object.defineProperty(this.data,key,{ enumerable:true, configurable:true, get:function(){ console.log(key+'被访问'); returnvalue; }, set:function(newVal){ console.log(key+'被修改,新'+key+'='+newVal); if(newVal===value)return; value=newVal; } }) }; vardata={ user:{ //name:'zhangsan', age:function(){console.log(1)} }, apg:[{'a':'b'},2,3] } functionobserveArray(items){ for(vari=0,l=items.length;i以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。