Raymond巍达

导航

 

监听一个对象的变化是实现watcher与双向数据绑定的基础,我们来一起看看如何监听一个对象的变化。

在这里我们可以用到ES5中Object的defineProperty属性来做到对一个对象进行监听,那么先简单认识一下defineProperty的用法。

 1     let obj = {};
 2     let nameVal = 'friday';
 3     
 4     Object.defineProperty(obj, 'name', {
 5         configurable : true,//是否是可配置的----是否可以更改enumerable,writable等
 6         enumerable : true//是否可被for in枚举
 7         get : function() {
 8             return nameVal.toUpperCase();
 9         },
10         set : function(newVal) {
11             nameVal = newVal;
12         }
13     });

这里需要注意下name并不用定义在obj自身当中,只要保证get与set函数拿到外层定义的nameVal值,即相当于obj本身定义了name属性,如果想监控自身本身就拥有的name属性可以如下写法。

 1     let obj = {name : 'friday'};
 2     let val = obj.name;
 3     
 4     Object.defineProperty(obj, 'name', {
 5         configurable : true,
 6         enumerable : true,
 7         get : function() {
 8             return val.toUpperCase();
 9             //注意这里不能写成return obj.name.toUpperCase();会造成死循环无限执行getter造成泄漏
10         },
11         set : function(newVal) {
12             if(val === newVal) return;
13             val = newVal;
14         }
15     });

 

基本的监听对象变化就如上面的代码呈现的一样,但是还有一个问题如果对象的属性依然是对象,这种情况该如何处理比如下面的这种结构。

1     let obj = {
2         user : {
3             name : 'friday',
4             gender : 'male'
5         },
6         info : {
7             age : 2    
8         }
9     };

答案是我们可以使用递归算法来监听每一个对象

 1     var Observer = function(data) {
 2         //此处的this.data = data;为了方便后面原型上拿取对象
 3         this.data = data;
 4         this.walk(data);
 5     };
 6 
 7     Observer.prototype.walk = function(obj) {
 8 
 9         let val;
10         for(let key in obj) {
11             //因为for in循环会枚举原型链上的key所以用hasOwnProperty来过滤
12             if(obj.hasOwnProperty(key)) {
13                 val = obj[key];
14                 if(typeof val === 'object') {
15                     new Observer(val);
16                 }
17                 //注意此处使用一个闭包,因为如果直接使用Object.defineProperty最终返回的val值永远是遍历拿到的最后一个val,当然改写的方式不止一种,我们也可以不用闭包直接利用let的特性将let写进for in循环中,或者将这个匿名函数闭包直接定义在原型上,在此处我们推荐后一种方式
18                 (function(key, val){
19                     Object.defineProperty(obj, key, {
20                         configurable : true,
21                         enumerable : true,
22                         get : function() {
23                             console.log('你访问了' + key);
24                             return val;
25                         },
26                         set : function(newVal) {
27                             console.log('你更改了' + key);
28                             if(val === newVal) return;
29                             val = newVal;
30                         }
31                     });
32                 })(key, val);
33             }
34         }
35 
36     };

将闭包函数定义在原型中

 1         Observer.prototype.walk = function (obj) {
 2 
 3         let val;
 4         for(let key in obj) {
 5             if(obj.hasOwnProperty(key)) {
 6                 val = obj[key];
 7                 if(typeof val === 'object') {
 8                     new Observer(val);
 9                 }
10                 this.convert(key, val);
11             }
12         }
13 
14     };
15 
16     Observer.prototype.convert = function (key, val) {
17 
18         Object.defineProperty(this.data, key, {
19             configurable : true,
20             enumerable : true,
21             get : function () {
22                 console.log('你访问了' + key);
23                 return val;
24             },
25             set : function (newVal) {
26                 console.log('你更改了' + key + '=' + newVal);
27                 if(val === newVal) return;
28                 val = newVal;
29             }
30         })
31 
32     };
33 
34     new Observer(obj);

上面的代码遗留了两个问题 1. 不能监听数组的变化 2. 如果重新set的属性是对象话,新对象不具有setter与getter方法

 

 

posted on 2016-10-28 10:54  Raymond巍达  阅读(2110)  评论(0编辑  收藏  举报