Object.defineProperty对数组进行响应式化是有缺陷的,虽然我们可以监听到索引的改变。
function def (obj, key, val) {
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function() {
console.log('获取');
return val;
},
set: function(newVal) {
if(val === newVal) {
return;
}
val = newVal;
console.log("数据改变了");
}
});
}
var data = [1, 2, 3];
def(data, 0, 2);
我们可以查看data结构如下:
console.log(data[0])
// 获取
// 2
data[0] = 100
// 数据改变了
data.length = 0; // 控制台无任何输出
但是defineProperty不能检测到数组长度的变化,准确来说是通过改变length而增加的长度不能检测到。
Vue监听Array三部曲:
第一步:先获取原生Array的原型方法,因为拦截后还是需要原生的方法帮我们实现数组的变化;
第二步:对Array的原型方法使用Object.defineProperty做一些拦截操作;
第三步:把需要被拦截的Array类型的数据原型指向改造后原型。
在进行数据observer绑定的时候,我们先判断是否
// 判断是否有__proto__,因为部分浏览器是没有__proto__属性
var hasProto = '__proto__' in {};
var arrayProto = Array.prototype;
var arrayMethods = Object.create(arrayProto);
var methods = ['push', 'pop', 'shift', 'unshift', 'reverse', 'sort', 'splice'];
function def (obj, key, val, enumerable) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !!enumerable,
configurable: true,
writable: true
});
}
methods.forEach(function(method) {
var original = arrayMethods[method];
def(arrayMethods, method, function mutator () {
var args = [], len = arguments.length;
while(len--) {
args[len] = arguments[len];
}
var result = original.apply(this, args);
var ob = this.__ob__;
var inserted;
switch(method){
case 'push':
case 'unshift':
inserted = args;
break;
case 'splice':
inserted = args.slice(2);
break;
}
if (inserted) {
}
return result;
});
})
var arrayKeys = Object.getOwnPropertyNames(arrayMethods);
var Observer = function Observer (value) {
this.value = value;
def(value, '__ob__', this);
if (Array.isArray(value)) {
// 如果有__proto__,直接覆盖
if (hasProto) {
protoAugment(value, arrayMethods);
} else {
// 没有就把方法加到属性自身上
copyAugment(value, arrayMethods, arrayKeys);
}
}
}
// 原型的赋值
function protoAugment (target, src) {
target.__proto__ = src;
}
// 赋值
function copyAugment (target, src, keys) {
for (var i = 0, l = keys.length; i < l; i++) {
var key = keys[i];
def(target, key, src[key]);
}
}
参考资料: https://www.jb51.net/article/162584.htm