对象的监听
es5
的Object.defineProperty
是关键,一个已经定义好的对象,我们可以利用Object.defineProperty
方法重写它的属性,添加get
,set
方法。
var obj = {
name:"小明",
age:18,
say(){
return "hello";
}
}
for(let key in obj){
let value = obj[key]; //利用闭包,存值
Object.defineProperty(obj,key,{
get(){
return value;
},
set(val){
console.log('改变了');
value = val;
},
})
}
复制代码
get
,set
回调里可以塞入我们想要的操作。当然,上述代码如果要严谨点的话,还应当区分是否监听原型链上的属性。可以用hasOwnProperty
方法或者Object.keys()
方法排除原型链上的属性。
数组的监听
因为种种历史原因,数组类型是没法用上面的方法进行监听的,而且因为种种历史原因和实现方式的缘故,数组里的元素改变是没法直接监听到的,(比如 arr[3] = 9
),所以只能退而求其次监听数组的方法。 实现思路很简单,在Array
和需要监听的数组之间多构造一个数组对象,将监听数组的原型__proto__
指向构造的数组对象即可,构造的数组对象自然可以覆写数组方法,并添加自己的操作。
var overRideArr = []; //构造的数组对象
overRideArr['push'] = function(){
console.log("监听操作");
return [].push.apply(this,arguments)
}
var arr = ['a','b','c']; // 待监听的数组
arr.__proto__ = overRideArr; //改变原型指向
复制代码
为什么要使用__proto__
而不是创建一个继承Array
的类,并在其内部改写对应方法呢? 因为除了 new Array()
,别的方式无法返回数组类型,即使构造了一个类,new 出来的也是会是个对象。所以通过改变实例的原型指向,是目前监听数组最实用的方式。 哈?你说别的方式?当然就是直接覆写某个实例数组了,比如这样:
var arr = ['a','b','c']; // 待监听的数组
arr['push'] = function(){
console.log("监听操作");
return [].push.apply(this,arguments)
}
复制代码
这种方法只有不支持__proto__
属性的时候才会用,远没有使用__proto__
方便。
另外如果你真的很喜欢new的方式,其实可以包装一下,弄个形式,但是内里还是上面的代码。
function OverArr(arr){
arr.__proto__ = overRideArr;
return arr;
}
var arr2 = ['a','b','c']; // 待监听的数组
arr2 = new OverArr(arr2);
//arr2 = OverArr(arr2); 这种写法也一样的
复制代码