zoukankan      html  css  js  c++  java
  • JS在项目中用到的AOP, 以及函数节流, 防抖, 事件总线

    1. 项目中在绑定事件的时候总想在触发前,或者触发后做一些统一的判断或逻辑,在c#后端代码里,可以用Attribute, filter等标签特性实现AOP的效果,可是js中没有这种用法,归根到本质还是不支持类型的拦截和判断,所以没法实现,但是js的灵活就在于可以通过原型链, 高阶函数,闭包等特性来实现类似的效果,这里记录一下便于复习

        //AOP: after
        Function.prototype.after = function (afterFn) {
            var _self = this;
            return function () {
                var ret = _self.apply(this, arguments);
                if (ret === false) {
                    return false;
                }
                afterFn.apply(this, arguments);
                return ret;
            }
        }
    
    
        //AOP: before
        Function.prototype.before = function (beforeFn) {
            var _self = this;
            return function () {
                var ret = beforeFn.apply(this, arguments);
                if (ret === false) {
                    return false;
                }
                return _self.apply(this, arguments);
            }
        }

    2. 用法下面的例子是工程中实用, 在tab点击的时候,一般情况没打开一个tab要绑定一个或多个事件做点击,关闭等处理,这里实现的方法就是在父元素统一只绑定一个事件,都通过冒泡来触发,这种好处就是新加tab不用绑定事件,不好的就是可能tab层级复杂,需要做很多元素的判断来决定触发什么事件,比如说下面的关闭事件里面先判断后,要是没有触发,在判断触发点击事件,这样写的逻辑清晰,功能模块可以分开,利于解耦,效果也能很好实现!!!

                    _bindEvents: function () {
                        var g = this,
                            p = this.options,
                            container = g.tab.bar.container;
    
                        // 标签点击事件, 统一只绑定父元素一次
                        container.nav.bind("click.tab.nav", function (e) {
                            if (e.target === e.currentTarget) {
                                e.stopPropagation();
                                return;
                            }
                            g._tabCloseClick.after(g._tabBarClick).call(g, e);
                        });
                        
                      }    

    3. 当然还有函数的防抖和节流的实现:

    函数防抖就是为了让一个函数在一定的时间只执行一次,即使被多次触发,典型应用就是tab标签打开很多的时候,不停的拖动窗体大小或者改变位置,这样标签会有一些动画效果用来自适应窗体大小,但是要是频发的拖动窗体,这些动画效果就会被触发多次,导致拖完了后一段时间内,这种效果不停在重复,延迟严重,所以用debounce就可以在一定时间后触发一次效果就可以了,性能提升的同时,体验也很好

    函数节流 就是规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效,在项目中应用的例子就是在手风琴点击展开层级的时候,如果用户在短时间内重复快速点击多次,这样这个层级就会不停的触发多次,展开收起又展开,感觉发疯一样,所以这时候用throttle节流一下, 比如在300ms内就触发一次,不管你点了多少次,这样体验大大提高

        /**
        * 函数防抖, 在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时
        * @param fn {Function}   实际要执行的函数
        * @param delay {Number}  延迟时间,也就是阈值,单位是毫秒(ms)
        * @return {Function}     返回一个防抖了的函数
        */
        vango.utils.debounce = function (fn, delay) {
            var timer;
            return function () {
                var context = this;
                var args = arguments;
                clearTimeout(timer);
                timer = setTimeout(function () {
                    fn.apply(context, args);
                }, delay);
            }
        }
    
        /**
        * 函数节流,规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效
        * @param fn {Function}   实际要执行的函数
        * @param wait {Number}  延迟时间,也就是阈值,单位是毫秒(ms)
        * @return {Function}     返回一个节流了的函数
        */
        vango.utils.throttle = function (func, wait, options) {
            /* options的默认值
             *  表示首次调用返回值方法时,会马上调用func;否则仅会记录当前时刻,当第二次调用的时间间隔超过wait时,才调用func。
             *  options.leading = true;
             * 表示当调用方法时,未到达wait指定的时间间隔,则启动计时器延迟调用func函数,若后续在既未达到wait指定的时间间隔和func函数又未被调用的情况下调用返回值方法,则被调用请求将被丢弃。
             *  options.trailing = true; 
             * 注意:当options.trailing = false时,效果与上面的简单实现效果相同
             */
            var context, args, result;
            var timeout = null;
            var previous = 0;
            if (!options) options = {};
            var later = function () {
                previous = options.leading === false ? 0 : new Date();;
                timeout = null;
                result = func.apply(context, args);
                if (!timeout) context = args = null;
            };
            return function () {
                var now = new Date();;
                if (!previous && options.leading === false) previous = now;
                // 计算剩余时间
                var remaining = wait - (now - previous);
                context = this;
                args = arguments;
                // 当到达wait指定的时间间隔,则调用func函数
                // 精彩之处:按理来说remaining <= 0已经足够证明已经到达wait的时间间隔,但这里还考虑到假如客户端修改了系统时间则马上执行func函数。
                if (remaining <= 0 || remaining > wait) {
                    // 由于setTimeout存在最小时间精度问题,因此会存在到达wait的时间间隔,但之前设置的setTimeout操作还没被执行,因此为保险起见,这里先清理setTimeout操作
                    if (timeout) {
                        clearTimeout(timeout);
                        timeout = null;
                    }
                    previous = now;
                    result = func.apply(context, args);
                    if (!timeout) context = args = null;
                } else if (!timeout && options.trailing !== false) {
                    // options.trailing=true时,延时执行func函数
                    timeout = setTimeout(later, remaining);
                }
                return result;
            };
        };

     5. 对于一个系统来说事件的设计当然必不可少,平台中简单的实现了一下事件总线的机制,记录一下

        vango.event = (function () {
    
            var _callbacks = {};
    
            var on = function (eventName, callback) {
                if (!_callbacks[eventName]) {
                    _callbacks[eventName] = [];
                }
    
                _callbacks[eventName].push(callback);
            };
    
            var off = function (eventName, callback) {
                var callbacks = _callbacks[eventName];
                if (!callbacks) {
                    return;
                }
    
                var index = -1;
                for (var i = 0; i < callbacks.length; i++) {
                    if (callbacks[i] === callback) {
                        index = i;
                        break;
                    }
                }
    
                if (index < 0) {
                    return;
                }
    
                _callbacks[eventName].splice(index, 1);
            };
    
            var trigger = function (eventName) {
                var callbacks = _callbacks[eventName];
                if (!callbacks || !callbacks.length) {
                    return;
                }
    
                var args = Array.prototype.slice.call(arguments, 1);
                for (var i = 0; i < callbacks.length; i++) {
                    callbacks[i].apply(this, args);
                }
            };
    
            return {
                on: on,
                off: off,
                trigger: trigger
            };
        })();
  • 相关阅读:
    centos 6.4 FTP安装和配置
    常用正则表达式集锦
    同一服务器部署多个tomcat时的端口号修改详情
    介绍linux下vi命令的使用
    CentOS下安装两个或多个Tomcat7
    tomcat解析之简单web服务器(图)
    吻你
    用C++语言开发Android程序 配置开发环境
    内地开源镜像网站
    Android SDK Android NDK Android Studio 官方下载地址
  • 原文地址:https://www.cnblogs.com/ZengYunChun/p/9466650.html
Copyright © 2011-2022 走看看