zoukankan      html  css  js  c++  java
  • js 高阶函数 闭包

    摘自  https://www.cnblogs.com/bobodeboke/p/5594647.html

    建议结合另外一篇关于闭包的文章一起阅读:http://www.cnblogs.com/bobodeboke/p/6127650.html

    一、闭包

    闭包某种程度上就是函数的内部函数,可以引用外部函数的局部变量。当外部函数退出后,如果内部函数依旧能被访问到,那么内部函数所引用的外部函数的局部变量就也没有消失,该局部变量的生存周期就被延续。

    一个经典的例子如下:

    复制代码
    <script>
        //this丢失现象
        document.addEventListener('DOMContentLoaded',function(){
        var divs=document.getElementsByTagName('div');
            console.log(divs);
            for (var i = 0; i < divs.length; i++) {
                divs[i].onclick=function(){
                    alert(i);
                }
            };    
        },false);
        
            
        </script>
    </head>
    <body>
        <div id="div1">testDiv</div>
        <div id="div2">div2</div>
    </body>
    复制代码

    上面的代码中,因为div节点的Onclick事件是异步触发的,当事件被触发的时候,for循环早已结束,此时变量i的值已经是循环结束时候的2;如果想要达到想要的效果,需要采用闭包的形式,具体如下:

    复制代码
        var divs=document.getElementsByTagName('div');
            //console.log(divs);
            for (var i = 0; i < divs.length; i++) {
                (function(i){
                    divs[i].onclick=function(){
                        alert(i);
                    }
                })(i);
            };    
        },false);
    复制代码

    或者这种写法经过测试也是可行的:

    复制代码
        var divs = document.getElementsByTagName('div');
        for (var i = 0, len = divs.length; i < len; i++) {
          divs[i].onclick = (function(i) {
            return function() {
              alert(i);
            };
          })(i);
        }
    复制代码

    注意不要写成下面这样,这和第一种并没有本质区别:

    复制代码
        var divs=document.getElementsByTagName('div');
            //console.log(divs);
            for (var i = 0; i < divs.length; i++) {
                divs[i].onclick=function(){
                    (function(i){
                        alert(i);
                    })(i); //此时蹦出来的都是最后一个i值
                }
            };    
        },false);
    复制代码

    也不要写成这种:

    复制代码
        var divs = document.getElementsByTagName('div');
        for (var i = 0, len = divs.length; i < len; i++) {
          divs[i].onclick = (function(i) {
            return function(i) {
              alert(i);// 此时弹出来的是[object MouseEvent]
            };
          })(i);
        }
    复制代码

    二、高阶函数

    高阶函数是至满足下列条件之一的函数:

      1)函数可以作为参数被传递(如回调函数等);

      2)函数可以作为返回值输出;

    高阶函数还应用于以下场景:

      1)高阶函数实现AOP

      (AOP面向切面编程,其主要作用是把一些跟核心业务逻辑模块无关的功能抽离出来,这些无关模块通常包括日志统计,安全控制,异常处理等,然后再将这些支撑模块“动态织入”到另一个函数中去),在java中通常是适用反射和动态代理模式来实现AOP,而js中可以很方便的利用高阶函数实现AOP编程。 下例实际上就是针对函数的装饰者模式;

    复制代码
            Function.prototype.before=function(beforeFn){
                //假设调用的时候一般是fna.before(fnb);则这里的this是fna
                var self=this;
                //console.log(this);
                //这里的this是装饰之后的函数调用的上下文,例子上f(3)调用时,没有显式的上下文,因此此时是window
                //arguments即真正调用的时候传入的参数,此时beforeFn与self传入的是同一个参数,在例子中就是3
                return function(){
                    //console.log(this);
                    //console.log(arguments);
                    beforeFn.apply(this,arguments);
                    
                    return self.apply(this,arguments);
                }
            };
            Function.prototype.after=function(afterFn){
                var self=this;
                return function(){
                    var ret=self.apply(this,arguments);
                    afterFn.apply(this,arguments);
                    return ret;
                };
            };
            function fna(a){
                console.log(1+a);
            }
            function fnb(a){
                console.log(2+a);
            }
            var f=fna.before(fnb);
            f(3);
    复制代码

      2)函数柯里化(currying)

    函数柯里化currying又称为部分求值,一个currying的函数会先接受一些参数,接收了这些参数以后,该函数并不会立即求值,而是继续返回另外一个函数,刚才传入的参数在函数形成的闭包中被保存,待函数真正需要求值的时候,之前传入的所有参数都会被一次性的用于求值。

    下面是一个通用的函数currying的实现:

    复制代码
            var currying=function(fn){
                var args=[];
                return function(){
                    if(arguments.length>=1){
                        [].push.apply(args,arguments);
                        //其实这里有没有返回值不是必须的
                        //return arguments.callee;
                        //return fn;
                    }else{
                        return fn.apply(this,args);
                    }
                };
            };
            function cost(){
                var money=0;
                for(var i=0;i<arguments.length;i++){
                    money+=arguments[i];
                }
                console.log(money);
                return money;
            }
            var cost=currying(cost);
            cost(200);//未真正求值
            cost();//进行真正求值
    复制代码

        3)函数节流

      针对一些被频繁调用的函数,如onresize,mousemove等,它们共同的特征是函数被触发的频率太高,事实上可能并不需要以这么高的频率调用,下面的代码可以对此类函数指定触发的间隔。

    复制代码
            var throttle=function(fn,interval){
                var timer,
                firstTime=true;
                return function(){
                    if(firstTime){
                    //第一次的时候,不延迟执行
                        fn.apply(this,arguments);
                        return firstTime=false;
                    }
                    
                    if(timer){
                        return false;
                    }
                    //延时一段时间之后执行
                    timer=setTimeout(function(){
                        //清除定时器
                        clearTimeout(timer);
                        timer=null;
                        fn.apply(this,arguments);
                    },interval||1000);
                    
                };
            };
            var i=1;
             
            window.onresize=throttle(function(){
                console.log(i++);
            }); 
    复制代码

      4)分时函数

    页面短时间内进行大量DOM操作会造成页面卡主的情况,比如需要循环在页面上新增1000个DOM节点,一种解决方案是下面的timeChunk函数,让原本1s钟创建1000个节点的操作,改为每200ms创建8个节点。

    timeChunk接收三个参数,第一个参数是创建节点需要的数据,第二个参数是封装创建逻辑的函数,第三个参数表示每一批创建的节点数量。

    复制代码
            var timeChunk=function(ary,fn,count){
                var timer;
                return function(){
                    var operation=function(){
                        for(var i=0;i<Math.min(count||1,ary.length);i++){
                            var curData=ary.shift();
                            fn(curData);
                        }
                    };
                    timer=setInterval(function(){
                        if(ary.length<=0){
                            clearInterval(timer);
                            timer=null;
                            return;
                        }
                        operation();
                    },200);
                }
            };
    复制代码

     另外一篇关于高阶函数的文章

    https://www.cnblogs.com/goloving/p/8361705.html

  • 相关阅读:
    C#操作REDIS例子
    A C# Framework for Interprocess Synchronization and Communication
    UTF8 GBK UTF8 GB2312 之间的区别和关系
    开源项目选型问题
    Mysql命令大全——入门经典
    RAM, SDRAM ,ROM, NAND FLASH, NOR FLASH 详解(引用)
    zabbix邮件报警通过脚本来发送邮件
    centos启动提示unexpected inconsistency RUN fsck MANUALLY
    rm 或者ls 报Argument list too long
    初遇Citymaker (六)
  • 原文地址:https://www.cnblogs.com/FineDay/p/10238073.html
Copyright © 2011-2022 走看看