之前经常使用 Vue,后来不满足于仅仅使用它,我想了解其内部实现原理,所以就尝试学习其源码,获益匪浅。
- 动态数据绑定就是 Vue 最为基础,最为有用的一个功能。给定任意一个对象,如何监听其属性的读取与变化?也就是说,如何知道程序访问了对象的哪个属性,又改变了哪个属性? 举个例子。
let app1 = new Observer({
name: 'yyy',
age: 20
});
let app2 = new Observer({
university: 'uestc',
major: 'computer'
});
// 要实现的结果如下:
app1.data.name // 你访问了 name
app.data.age = 100; // 你设置了 age,新的值为100
app2.data.university // 你访问了 university
app2.data.major = 'science' // 你设置了 major,新的值为 science
实现这样的一个 Observer,要求如下:
- 传入参数只考虑对象,不考虑数组。
- new Observer返回一个对象,其 data属性要能够访问到传递进去的对象。
- 通过 data 访问属性和设置属性的时候,均能打印出对应的信息。
- 如果传入参数对象是一个“比较深”的对象(也就是其属性值也可能是对象),那该怎么办。
要实现上述目标,首先要定义一个对象的构造函数,然后定义原型方法,遍历对象本身的各个属性,并为每个属性绑定get和set方法。考虑到第4个问题,在程序中加入了递归的思想。
实现代码如下:
// 首先定义观察者构造函数
function Observer(data){
this.data=data;
this.walk(data);
}
// walk()函数用于深层次遍历对象的各个属性
// 采用的是递归的思路
// 因为我们要为对象的每一个属性绑定get和set
Observer.prototype.walk = function(obj){
let val;
for(let key in obj){
// 这里为什么要用hasOwnProperty进行过滤呢?
// 因为for...in 循环返回的是对象原型链上的所有能够通过对象访问的、可枚举的属性
// hasOwnProperty只在属性存在于实例中时才返回true
// 而我们想要的正是这个对象本身拥有的属性,所以要这么做。
if(obj.hasOwnProperty(key)){
val = obj[key];
// 这里进行判断,当访问的属性是一个对象时,继续new Observer,直到到达最底层的属性位置
if(typeof val ==='object'){
new Observer(val);
}
this.convert(key,val);
}
}
};
// 以自定义get和set函数
Observer.prototype.convert = function(key,val){
Object.defineProperty(this.data,key,{
enumerable:true,
configurable:true,
get:function(){
console.log('你访问了'+key);
return val;
},
set:function(newVal){
console.log('你设置了'+key);
console.log('新的'+key+'='+newVal);
if(newVal===val) return;
// 如果设置新的值是一个对象的话,继续new Observer,直到到达最底层的属性位置
if(typeof newVal === "object"){
new Observer(newVal);
};
val = newVal;
}
})
};
let data={
user:{
name: "yjm",
age: "21"
},
address: "成都"
};
let app =new Observer(data);
- 当访问的属性是一个对象的时候,对这个对象属性进行new Observer,直到到达最底层的属性位置,使‘深层次’的属性也能被访问到。
- 同时为对象设置setter的时候,如果设置新的值是一个对象的话,也继续new Observer,直到到达最底层的属性位置,使新设置的值对象的子属性也能继续响应 getter 和 setter。
效果如下:
如上图所示:无论是访问一个简单值或者是一个深层次的对象,都可以很好响应getter 和 setter。