IFE_02_dynamicDataBinding_03

总结:

  • 主要针对上一版的缺陷进行了优化

  • 为每个属性增加了propPath 属性,记录属性自身的path,同时watchEvents 也利用path 设置 key

  • dispatch watchEvents 时,由path 向上冒泡

  • 在 dispatch watchEvents 时,修正属性链上的 valueoldValue

  • 缺点:

    • CustomEvent 中的bubbles 目前是写死的

代码:

<!DOCTYPE html>
<html>
<head>
    <title>dymanic_data_binding_03</title>
    <meta charset="utf-8">
    <style type="text/css">
    </style>

    <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/styles/default.min.css" />
</head>
<body>
    <ol>要求:
        <li>
            <h3>firstName 和 lastName 作为 name 的属性,其中任意一个发生变化,都会得出以下结论:"name 发生了变化。"这种机制符合”事件传播“机制,方向是从底层往上逐层传播到顶层。</h3>
            <pre><code>
                var app2 = new Observer({
                    name: {
                        firstName: 'shaofeng',
                        lastName: 'liang'
                    },
                    age: 25
                });

                app2.$watch('name', function(newName) {
                    console.log('我的姓名发生了变化,可能是姓氏变了,也可能是名字变了。')
                });

                app2.data.name.firstName = 'hahaha'; // 输出:我的姓名发生了变化,可能是姓氏变了,也可能是名字变了。
                app2.data.name.lastName = 'blablabla'; // 输出:我的姓名发生了变化,可能是姓氏变了,也可能是名字变了。
            </code></pre>

            <pre><code>
                var app1 = new Observer({
                    name: {
                        firstName: 'shaofeng',
                        lastName: {
                            test: 1
                        }
                    },
                    age: 25
                });

                app1.$watch('name', function(newName) {
                    console.log('我的姓名发生了变化,可能是姓氏变了,也可能是名字变了。')
                });


                app1.data.name.lastName.test = 'fdfff';
                app1.data.name = 2;
                app1.data.name.firstName = 'hahaha'; // 此时不会报错,但是赋值也不会成功
            </code></pre>
        </li>
    </ol>

<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/highlight.min.js"></script>
<script type="text/javascript">
    hljs.initHighlightingOnLoad();

    function Observer (data) {
        this.watchEvents = {};

        this.defineProperty(this, 'data', data);
    }

    Observer.prototype.defineProperty = function (obj, key, data) {
        if (_getType(data) !== 'object') throw 'wrong data type';

        let _this = this;

        Object.keys(data).forEach(function (v, i) {
            let dataVal;

            if (_getType(obj[key]) !== 'object') {
                // obj[key] && (delete obj[key]); // @tag1 处进行处理
                obj[key] = {};
            }

            Object.defineProperty(obj[key], v, {
                get: function () {
                    // console.log(`你访问了 ${v}`);
                    return dataVal;
                },
                set: function (value) {
                    _this._setPath(obj, key, v);

                    let oldValue = obj[key][v],
                        curPath = _getPath(obj[key], v);

                    if (_getType(value) === 'object') {
                        if (Object.keys(value).length > 0) {
                            _this.defineProperty(obj[key], v, value);
                        } else {
                            /* @tag: 1 */
                            console.log(`你设置了 ${curPath},新的值为 ${JSON.stringify(value)}`);
                            dataVal = value;
                        }
                    } else {
                        if(v !== 'propPath') {
                            console.log(`你设置了 ${curPath},新的值为 ${value}`);
                        }
                        dataVal = value;
                    }


                    _this.watchEvents[`${curPath}`] = _polyfillCustomEvent(`$watch_${v}`, {
                        bubbles: true, // 具体使用方式???
                        detail: {
                            value: _getJsonObj(value),
                            oldValue: _getJsonObj(oldValue)
                        }
                    });

                    _this._dispatchEvent(`${curPath}`);
                },
                enumerable: true,
                configurable: true
            });

            obj[key][v] = data[v];
        }, obj);
    }

    Observer.prototype.$watch = function (key, callback) {
        let _this = this;

        // $watch 仅对 data 的第一层属性有效
        document.addEventListener(`$watch_${key}`, function (e) {
            (e.detail.oldValue !== e.detail.value) && callback(e.detail.value);
        })
    };

    Observer.prototype._dispatchEvent = function (eventPath) {
        let _this = this,
            event = this.watchEvents[eventPath],
            targetPropName = eventPath.substr(eventPath.lastIndexOf('_') + 1),
            parentPath = eventPath.substr(0, eventPath.lastIndexOf('_')),
            parentEvent, parentLiveValue;

        if (!event) return;

        if (parentPath) {
            parentEvent = this.watchEvents[parentPath];
            if (parentEvent) {
                parentLiveValue = JSON.parse(JSON.stringify(_this._getValueFromPath(parentPath)));
                parentEvent.detail.value = JSON.stringify(parentLiveValue);
                parentLiveValue[targetPropName] = event.detail.oldValue;
                parentEvent.detail.oldValue = JSON.stringify(parentLiveValue);
            }
        }

        document.dispatchEvent(event);

        if (event.bubbles) {
            this._dispatchEvent(parentPath);
        }
    }

    Observer.prototype._setPath = function (obj, key) {
        if (obj === this) return;

        obj[key].propPath = _getPath(obj, key);

        return obj[key].propPath;
    }

    Observer.prototype._getValueFromPath = function (path) {
        let pathArr = path.split('_');

        return pathArr.reduce((last, curVal, curIndex, arr) => {
            return last[curVal];
        }, this.data);
    }

    function _getJsonObj (value) {
        return _getType(value) === 'object' ? JSON.stringify(value) : value;
    }

    function _getPath (obj, key) {
        return `${obj.propPath ? `${obj.propPath}_` : ''}${key}`;
    }

    function _polyfillCustomEvent (event, params) {
        if (!CustomEvent) {
            function CustomEvent (event, params) {
                params = params || {
                    bubbles: false,
                    cancelable: false,
                    detail: undefined
                };

                let evt = document.createEvent('CustomEvent');
                evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
                return evt;
            }

            CustomEvent.prototype = window.Event.prototype;
        }
        return new CustomEvent(event, params);
    }

    function _getType (obj) {
        return Object.prototype.toString.call(obj).match(/ .+(?=\])/)[0].trim().toLowerCase();
    }
</script>
</body>
</html>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值