JS的平凡之路--模仿Vue写个简单的对象监听

本篇实现的仅仅是对象监听,不包含数组。关于数组的下一篇会讲。

一、用什么来监听对象?

  Vue给了我们很好的回答,那就是defineProperty。defineProperty方法有以下几个参数:

  • obj: 你要设置属性的对象;
  • propName: 你设置的属性的名称;
  • descriptor: 描述符对象;

  这个descriptor对象可以设置的属性就比较多了:

  • value: 设置属性的值;
  • writable: 默认为false,如果该值为false,则不能再修改属性的值;
  • enumerable: 默认为false,如果该值为false,则不能被枚举;
  • configurable: 默认为false,如果该值为false,那么descriptor不能再被修改,也不能使用delete;
  • get与set:存取器方法,默认为undefined。

  其他的descriptor对象理解应该不难,看了MDN上的教程,应该会发现对于set和get方法的使用,总是会在外部声明一个变量来维护值的变化,是不是很变扭,然后就会有人尝试用this来调用属性,这时就会报执行栈溢出,因为它递归的执行自己,导致栈溢出。

  这里我们可以通过set和get内部维护的'_'开头的私有变量来解决:

            const obj = {};
            Object.defineProperty(obj,'name', {
                get () {
                    console.log('读取name的值为 => ' + this._name);
                    return this._name;
                },
                set (newValue) {
                    this._name = newValue;
                    console.log('设置name的值为 => ' + this._name);
                }
            })
            obj.name = 'xiaoming'; // '设置name的值为 => xiaoming'
            obj.name; // '读取name的值为 => xiaoming'复制代码

  两种方法看着喜好用吧。

二、实现对象的监听

  首先我们得有一个对象:

            const obj = {
                a: 1,
                b: {
                    c: 2,
                    d: 3
                }
            };复制代码

  接下来我们要将它变成一个响应式对象,让它的取值与赋值操作多在我们的监听下,首先我们要通过一个Observer来包装我们需要响应化的对象:

            function Observer(value) {
                /**
                 * 构造函数防止没有使用'new'关键字创建
                 */
                if (!(this instanceof Observer)) {
                    return new Observer(value);
                }
                this.value = value;
                // 这一篇仅仅讨论对象的监听
                if (!Array.isArray(value)) {
                    this.walk(value);
                }
            }

            Observer.prototype.walk = function (obj) {
                const keys = Object.keys(obj);
                for (let i = 0, max = keys.length; i < max; i++) {
                    const key = keys[i];
                    //将对象响应化
                    defineReactive(obj, key, obj[key]);
                }
            }复制代码

  接下来核心就是采用defineProperty方法来监听set和get方法,但是这里我们要注意的是采用递归创建Observer对象的方式,解决对象嵌套的问题:

            function defineReactive(obj, key, val) {
                //获取对象的描述器对象
                const property = Object.getOwnPropertyDescriptor(obj, key);
                //这里上面强调了configurable属性的重要性
                if (property && property.configurable === false) {
                    return;
                }

                const setter = property && property.set;
                const getter = property && property.get;

                //解决对象嵌套的问题
                observe(val);

                Object.defineProperty(obj, key, {
                    enumerable: true,
                    configurable: true,
                    get () {
                        const value = getter ?  getter.call(obj) : val;
                        log('读取',obj,key,value);
                        return value;
                    },
                    set (newValue) {
                        const value = getter ? getter.call(obj) : val;
                        if (setter) {
                            setter.call(obj, newValue);
                        } else {
                            val = newValue;
                        }
                        log('设置',obj,key,value);
                    }
                })
            }

            function observe(obj) {
                if (!isObject(obj)) {
                    return;
                }
                return new Observer(obj);
            }复制代码

  这里基本上对象的监听已经完成了,下面来试试:

        Observer(obj);
        obj.b.d = 10;复制代码

控制台输出结果

  但是这里还存在一个问题,因为我们还有这种操作:

        obj.f = 10;复制代码

  这时obj.f不再被我们监听,对于这种情况在Vue中我们要通过Vue.set方法设置,这里我们也可以写了set方法:

            function set(target, key, val) {
                if(!target.hasOwnProperty(key)) {
                    target[key] = val;
                    defineReactive(target, key, val);
                }
            }复制代码

  这里是个简易的set方法,主要思想就是使新属性响应化。

三、总结

  通过源码的学习,使你在使用Vue的一些api更加得心应手。

  PS:源码用的flow.js,看起来有点费劲,但是我应该会坚持下去。(下一篇会讲数组的监听)

参考资料
Vue core部分observer
熊建刚博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值