资讯专栏INFORMATION COLUMN

BiuJS[v1.0]说明文档(2):数据劫持

Terry_Tai / 2198人阅读

摘要:是一个轻巧的框架它实现了数据的双向绑定并提供一些基本的指令帮助你提升效率,比如,,,,是的,如你所见,以开头的指令是它的独特标识行左右的代码量,让应用的开发和加载的一瞬完成仓库订阅清单前文说到提供了一个强大的接口我们就用它来劫持数据不过在此

BiuJS
BiuJS是一个轻巧的mvvm框架
它实现了数据的双向绑定
并提供一些基本的指令帮助你提升效率,比如$for$model$if$click$style
是的,如你所见,以$开头的指令是它的独特标识
1000行左右的代码量,让应用的开发和加载biu的一瞬完成
BiuJS仓库: https://github.com/veedrin/biu
订阅清单

前文说到JavaScript提供了一个强大的接口Object.defineProperty

我们就用它来劫持数据

不过在此之前,我们还有一点准备工作要做

还记得前文提过的数组(订阅清单)吗?我们要造一个数组来装东西

其实很简单

function Dep() {
    this.subs = [];
}

Dep.prototype.addSub = function(sub) {
    this.subs.push(sub);
};

Dep.prototype.notify = function(newValue) {
    for (let i = 0; i < this.subs.length; i++) {
        this.subs[i].update(newValue);
    }
};

真的就是造个数组这么简单

再来一个添加数组成员的方法,一个遍历数组的方法

因为Dep.prototype.notify触发的开关在setter那里,也就是说前者依赖后者。Dep取其依赖之意

为了方便,我们还有一个取巧的地方,把订阅者挂到Dep名下,作为Dep的静态属性缓存起来

function Dep() {
    this.subs = [];
}

Dep.target = null;

Dep.prototype.addSub = function(sub) {
    this.subs.push(sub);
};

Dep.prototype.notify = function(newValue) {
    for (let i = 0; i < this.subs.length; i++) {
        this.subs[i].update(newValue);
    }
};
劫持对象属性

然后再回过头来说劫持的事

BiuJS主方法传进来的data是一个对象,我们把它转成数组再遍历

Object.keys(data).forEach((key) => {
    let value = data[key];
    let dep = new Dep;

    Object.defineProperty(data, key, {
        enumerable: true,
        configurable: true,
        get: function() {
            Dep.target && dep.addSub(Dep.target);
            return value;
        },
        set: function(newValue) {
            if (value === newValue) {
                return;
            }
            value = newValue;
            dep.notify(newValue);
        }
    });
});

每个属性都会得到一个订阅清单的实例,用来放它的订阅者

getter负责把订阅者添加进来,setter负责通知订阅者更新

订阅者是什么时候挂到Dep.target上的?这个我们暂且不表

有一个问题,data可能是嵌套的对象,而且可能嵌套的很深,所以我们要用递归深度劫持

Observer.prototype.observe = function(data) {
    if (!data || typeof data !== "object") {
        return;
    }
    let self = this;

    Object.keys(data).forEach((key) => {
        let value = data[key];
        // do something
        self.observe(value);
    });
};

递归停止的条件就是(嵌套)data为空,或者(嵌套)data不是对象

劫持数组方法

问题又来了,假如(嵌套)data的值是数组呢?

我们来看一个例子

let data = {
    key: [1, 2]
};

let value = data.key;

Object.defineProperty(data, "key", {
    enumerable: true,
    configurable: true,
    get: function() {
        return value;
    },
    set: function(newValue) {
        value = newValue;
        console.log("captured");
    }
});

data.key = [1, 2, 3]; // 控制台打印"captured"
data.key.push(3); // 控制台没有打印

数组本身变化是可以被捕捉到的,用方法操作数组,setter也很绝望啊

不要悲伤,不要绝望,数组开外挂,我们就查外挂

Object.defineProperty是对象的方法呀,难道要让数组变性吗?

是的

let arrayObject = Object.create(Array.prototype);

console.log(arrayObject); // Array{}

有了它,我们就可以查外挂了

let arrayProto = Array.prototype;
let arrayObject = Object.create(arrayProto);
let methods = ["push", "pop", "unshift", "shift", "slice", "splice", "concat"];

methods.forEach((method) => {
    let origin = arrayProto[method];

    Object.defineProperty(arrayObject, method, {
        enumerable: true,
        writable: true,
        configurable: true,
        value: function() {
            let args = Array.from(arguments);
            let result = origin.apply(this, args);
            dep.notify(Array.from(this));
            return result;
        }
    });
});

上面的意思就是来一个偷天换日,把数组的方法名的value换成我们自己定义的函数

因为是数组的方法,这里的this指向的就是被操作的数组本身

在内部还是用apply调用原方法,获得返回值返回给我们自己定义的函数

在外面看起来是一样的,只不过加了一条:悄悄的通知订阅者更新

这回我们不需要劳烦setter了,数组的方法不是绕过了setter么,我们只要拿到操作完后的新数组,递给订阅者就好了

简直就是碟中谍有没有!

当然,要达到效果,还需要最后一招:移花接木

methods.forEach((method) => {
    let origin = arrayProto[method];

    Object.defineProperty(arrayObject, method, {
        // do something
    });
});

arr.__proto__ = arrayObject;

把我们自己定义的方法嫁接到被监测数组的原型上

一招偷天换日,一招碟中谍,一招移花接木

殊不知,我们已经在数组的方法身上植入了芯片,外挂game over

最后,递归的时候,我们要判断一下,对象和数组走的是不同的路

if (!Array.isArray(value)) {
    this.observe(value);
} else {
    this.observeArray(value, dep);
}
写在后面

以上就是BiuJS劫持数据以及添加到订阅清单里的过程

欢迎到BiuJS仓库: https://github.com/veedrin/biu了解详情

更欢迎StarFork

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/92278.html

相关文章

  • BiuJS[v1.0]说明文档(1):总体结构

    摘要:是一个轻巧的框架它实现了数据的双向绑定并提供一些基本的指令帮助你提升效率,比如,,,,是的,如你所见,以开头的指令是它的独特标识行左右的代码量,让应用的开发和加载的一瞬完成仓库启动首先我们来看一下是如何启动的是的挂载点,它决定在多大范围的树 showImg(https://segmentfault.com/img/remote/1460000012478667?w=1920&h=926...

    崔晓明 评论0 收藏0
  • BiuJS[v1.0]说明文档(4):$if 指令

    摘要:是一个轻巧的框架它实现了数据的双向绑定并提供一些基本的指令帮助你提升效率,比如,,,,是的,如你所见,以开头的指令是它的独特标识行左右的代码量,让应用的开发和加载的一瞬完成仓库指令往下看之前,请大家沐浴更衣,因为我要讲的指令了中的已经被占用 showImg(https://segmentfault.com/img/remote/1460000012478667?w=1920&h=926...

    _ang 评论0 收藏0
  • BiuJS[v1.0]说明文档(3):文本编译

    摘要:如此循环,直到结束如果循环结束之后,比字符串的长度要小,那说明后面还有文本匹配失败了。 showImg(https://segmentfault.com/img/remote/1460000012478667?w=1920&h=926); BiuJS BiuJS是一个轻巧的mvvm框架它实现了数据的双向绑定并提供一些基本的指令帮助你提升效率,比如$for,$model,$if,$cli...

    lucas 评论0 收藏0
  • Web安全开发规范手册V1.0

    摘要:一背景团队最近频繁遭受网络攻击,引起了技术负责人的重视,笔者在团队中相对来说更懂安全,因此花了点时间编辑了一份安全开发自检清单,觉得应该也有不少读者有需要,所以将其分享出来。 一、背景 团队最近频繁遭受网络攻击,引起了技术负责人的重视,笔者在团队中相对来说更懂安全,因此花了点时间编辑了一份安全开发自检清单,觉得应该也有不少读者有需要,所以将其分享出来。 二、编码安全 2.1 输入验证 ...

    Yuqi 评论0 收藏0
  • Web安全开发规范手册V1.0

    摘要:一背景团队最近频繁遭受网络攻击,引起了技术负责人的重视,笔者在团队中相对来说更懂安全,因此花了点时间编辑了一份安全开发自检清单,觉得应该也有不少读者有需要,所以将其分享出来。 一、背景 团队最近频繁遭受网络攻击,引起了技术负责人的重视,笔者在团队中相对来说更懂安全,因此花了点时间编辑了一份安全开发自检清单,觉得应该也有不少读者有需要,所以将其分享出来。 二、编码安全 2.1 输入验证 ...

    Ryan_Li 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<