视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
vue源码学习之Object.defineProperty 对数组监听
2020-11-27 22:13:47 责编:小采
文档

上一篇中,我们介绍了一下defineProperty 对对象的监听,这一篇我们看下defineProperty 对数组的监听

数组的变化

先让我们了解下Object.defineProperty()对数组变化的跟踪情况:

var a={};
bValue=1;
Object.defineProperty(a,"b",{
 set:function(value){
 bValue=value;
 console.log("setted");
 },
 get:function(){
 return bValue;
 }
});
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方法,并生成一个新的数组赋值给数据,这样数据双向绑定就会触发。

实现简单的对数组的变化的监听

var arrayPush = {};
(function(method){
 var original = Array.prototype[method];
 arrayPush[method] = function() {
 // this 指向可通过下面的测试看出
 console.log(this);
 return original.apply(this, arguments)
 };
})('push');

var testPush = [];
testPush.__proto__ = arrayPush;
// 通过
输出,可以看出上面所述 this 指向的是 testPush // [] testPush.push(1); // [1] testPush.push(2);

在官方文档,所需监视的只有 push()、pop()、shift()、unshift()、splice()、sort()、reverse() 7 种方法。我们可以遍历一下:

var arrayProto = Array.prototype
var arrayMethods = Object.create(arrayProto)

;[
 'push',
 'pop',
 'shift',
 'unshift',
 'splice',
 'sort',
 'reverse'
].forEach(function(item){
 Object.defineProperty(arrayMethods,item,{
 value:function mutator(){
 //缓存原生方法,之后调用
 console.log('array被访问');
 var original = arrayProto[item] 
 var args = Array.from(arguments)
 original.apply(this,args)
 // console.log(this);
 },
 })
})

完整代码

function Observer(data){
 this.data = data;
 this.walk(data);
}

var p = Observer.prototype;
var arrayProto = Array.prototype
var arrayMethods = Object.create(arrayProto)

;[
 'push',
 'pop',
 'shift',
 'unshift',
 'splice',
 'sort',
 'reverse'
].forEach(function(item){
 Object.defineProperty(arrayMethods,item,{
 value:function mutator(){
 //缓存原生方法,之后调用
 console.log('array被访问');
 var original = arrayProto[item] 
 var args = Array.from(arguments)
 original.apply(this,args)
 // console.log(this);
 },
 })
})

p.walk = function(obj){
 var value;
 for(var key in obj){
 // 通过 hasOwnProperty 过滤掉一个对象本身拥有的属性 
 if(obj.hasOwnProperty(key)){
 value = obj[key];
 // 递归调用 循环所有对象出来
 if(typeof value === 'object'){
 if (Array.isArray(value)) {
 var augment = value.__proto__ ? protoAugment : copyAugment 
 augment(value, arrayMethods, key)
 observeArray(value)
 }
 new Observer(value);
 }
 this.convert(key, value);
 }
 }
};

p.convert = function(key, value){
 Object.defineProperty(this.data, key, {
 enumerable: true,
 configurable: true,
 get: function(){
 console.log(key + '被访问');
 return value;
 },
 set: function(newVal){
 console.log(key + '被修改,新' + key + '=' + newVal);
 if(newVal === value) return ;
 value = newVal;
 }
 })
}; 

var data = {
 user: {
 // name: 'zhangsan',
 age: function(){console.log(1)}
 },
 apg: [{'a': 'b'},2,3]
}

function observeArray (items) {
 for (var i = 0, l = items.length; i < l; i++) {
 observe(items[i])
 }
}

//数据重复Observer
function observe(value){
 if(typeof(value) != 'object' ) return;
 var ob = new Observer(value)
 return ob;
}

//辅助方法
function def (obj, key, val) {
 Object.defineProperty(obj, key, {
 value: val,
 enumerable: true,
 writable: true,
 configurable: true
 })
}

// 兼容不支持__proto__的方法
//重新赋值Array的__proto__属性
function protoAugment (target,src) {
 target.__proto__ = src
}
//不支持__proto__的直接修改相关属性方法
function copyAugment (target, src, keys) {
 for (var i = 0, l = keys.length; i < l; i++) {
 var key = keys[i]
 def(target, key, src[key])
 }
}

var app = new Observer(data);
// data.apg[2] = 111;
data.apg.push(5);
// data.apg[0].a = 10;
// console.log(data.apg);

下载本文
显示全文
专题