Vue 发布订阅模型的简单类比

93 阅读1分钟

参考链接 www.bilibili.com/video/BV1LE…

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
<script>
    // 全局的 event 对象, 提供 on, off, emit 方法
    var event = (function () {
        eventObjs = {};

        return {
            /** 注册事件, 可以连续注册, 可以注册多个事件 */
            on: function (type, handler) {
                (eventObjs[type] || (eventObjs[type] = [])).push(handler);
            },

            /** 移除事件,
             * - 如果没有参数, 移除所有事件,
             * - 如果只带有 事件名 参数, 就移除这个事件名下的所有事件,
             * - 如果带有 两个 参数, 那么就是表示移除某一个事件的具体处理函数
             * */
            off: function (type, handler) {
                if (arguments.length === 0) { // 没有参数移除所有的事件
                    eventObjs = {};
                } else if (arguments.length === 1) { // 只有事件的类型, 移除该事件的所有处理函数
                    eventObjs[type] = [];
                } else if (arguments.length === 2) { // 移除 type 事件的 handler 处理函数
                    // 使用循环移除所有的 该函数 对应的 type 事件
                    let _events = eventObjs[type];
                    if (!_events) return;
                    // 倒着循环 数组的 序号不会受到影响
                    for (let i = _events.length - 1; i >= 0; i--) {
                        if (_events[i] === handler) {
                            _events.splice(i, 1);
                        }
                    }
                }
            },

            /**
             * 发射事件, 触发事件, 包装参数 传递给事件处理函数
             */
            emit: function (type) {
                let args = Array.prototype.slice.call(arguments, 1); // 或 arguments 从 1 开始后的所有参数, 返回的是数组
                let _events = eventObjs[type];
                if (!_events) return;
                for (let i = 0; i < _events.length; i++) {
                    // 如果要绑定上下文就需要使用 call 或 apply
                    _events[i].apply(null, args);
                }
            },
        }
    }());

</script>
</body>

</html>