一、前言

在"模拟Vue之数据驱动3"中,我们实现了为每个对象扩展一个$set方法,用于新增属性使用,这样就可以监听新增的属性了。

当然,数组也是对象,也可以通过$set方法实现新增属性。

但是,对于数组而言,通常我们是通过push之类的方法吧。

PS:Vue中明确指出push、pop、shift、unshift、splice、sort、reverse方法为变异方法,可以通过它们监听属性变化,触发视图更新(详情见here

下面,我们就一起来实现这些Array的变异方法吧。

注:我们将Array变异方法实现,也写在extendObj.js中的,因为数组也是对象嘛。

二、Array变异方法实现

要实现这些变异方法,毫无疑问,我们会重写它们,那在Array.prototype里面重写吗?

当然不是,这样不就影响了所有数组对象的原型链了么!

为了避免这种情况,且,我们只是想在监听数据对象上继承这些变异数组方法,那么细心的你会发现,其实与我们在"模拟Vue之数据驱动3"中实现$set方法类似了。

首先,我们创建arrKeys对象用于保存需要变异的数组方法以及恒定对象extendArr,如下:

let arrKeys = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'];
const extendArr = [];

接着,就是在extendArr对象上,一一监听arrKeys中的方法了,与$set方法类似,整体结构如下:

!function(){
    arrKeys.forEach(function(key){
        proxyObject(extendArr, key, function(){
            //TODO
        });
    });
}();

注:proxyObject方法其实核心就是Object.defineProperty,详见"模拟Vue之数据驱动3".

接下来,就是实现核心部分代码了,重写这些方法的目的,是为了监听数据变化,所以要在方法原有功能不变的情况下,重写它们,Array.xxx.apply即可实现原有功能。

且,push、unshift、splice这三个方法可以在原数组中,新增属性,故而,我们需要监听新增属性以及它们的属性值,这里就和$set方法完全一样了,通过$Observer,即可利用observe以及convert方法实现了。

实现代码如下: 

!function(){
    arrKeys.forEach(function(key){
        proxyObject(extendArr, key, function(){
            console.log('Fun ' + key + ' is observed');
            let result;
            let arrProto = Array.prototype;
            let ob = this.$Observer;
            let arr = arrProto.slice.call(arguments);
            let inserted;
            let index;
            switch(key){
                case 'push': {
                    inserted = arr;
                    index = this.length;
                    break;
                }
                case 'unshift': {
                    inserted = arr;
                    index = 0;
                    break;
                }
                case 'splice': {
                    inserted = arr.slice(2);
                    index = arr[0];
                    break;
                }
            }
            result = arrProto[key].apply(this, arguments);
            if(inserted){
                inserted.forEach(val => {
                    ob.observe(val);
                    ob.convert(index++, val);
                });
            }
            return result;
        });
    });
}();
代码稍长,请自行打开

相关文章: