zoukankan      html  css  js  c++  java
  • 《javascript设计模式与开发实践》阅读笔记(3)—— 高阶函数的其他应用

    高阶函数的其他应用

    1.currying

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

    var cost = (function(){
        var args = [];
        return function(){
            if ( arguments.length === 0 ){
                var money = 0;
                for ( var i = 0, l = args.length; i < l; i++ ){
                    money += args[ i ];    
                }
                return money;
            }else{
                [].push.apply( args, arguments );
            }
        }
    })();
                        
    cost( 100 ); // 未真正求值
    cost( 200 ); // 未真正求值
    cost( 300 ); // 未真正求值
    console.log( cost() ); // 求值并输出:600

    带参数不求值 不带参数求值

        var currying = function( fn ){
            var args = [];
            return function(){
                if ( arguments.length === 0 ){
                    return fn.apply( this, args );
                }else{
                    [].push.apply( args, arguments );
                    //arguments.callee 在哪一个函数中运行,它就代表哪个函数,主要用在匿名函数中
                    return arguments.callee;     
                }
            }
        };
    
        var cost = (function(){
            var money = 0;
            return function(){
                for ( var i = 0, l = arguments.length; i < l; i++ ){
                    money += arguments[ i ];
                }
                return money;
            }
        })();
    
        var cost = currying( cost ); // 转化成currying 函数  cost函数有对参数的集中处理
    
        cost( 100 ); // 未真正求值
        cost( 200 ); // 未真正求值
        cost( 300 ); // 未真正求值
        alert ( cost() ); // 求值并输出:600

    第二个例子更为通用,把柯里化的过程抽离了出来。

    2.uncurrying

    柯里化的过程是逐步传参,逐步缩小函数的适用范围,逐步求解的过程。而反柯里化的作用在与扩大函数的适用性,使本来作为特定对象所拥有的功能的函数可以被任意对象所用。

    obj.func(arg1, arg2)  =====>  func(obj, arg1, arg2)

    动态语言比较容易实现这一点,下面是实现方式之一:

        var uncurrying= function (fn) {
            return function () {
                var args=[].slice.call(arguments,1);
                return fn.apply(arguments[0],args);        
            }    
        };    
    
        
        var test="a,b,c";
        var split_new=uncurrying( String.prototype.split );
    
        test.split(",");       //["a","b","c"]
        split_new(test,',');   //["a","b","c"]            

    3.函数节流

    减少不必要的函数自主调用,比如window.onresize事件  mousemove的拖曳事件等

    实现方法之一:

    将即将被执行的函数用setTimeout延迟一段时间执行。如果该次延迟执行还没有完成,则忽略接下来调用该函数的请求。函数接受2个参数,第一个参数为需要被延迟执行的函数,第二个参数为延迟执行的时间。

    var throttle = function ( fn, interval ) {
        var __self = fn, // 保存需要被延迟执行的函数引用
        timer, // 定时器
        firstTime = true; // 是否是第一次调用
    
        return function () {
            var args = arguments,
            __me = this;
            if ( firstTime ) { // 如果是第一次调用,不需延迟执行
                __self.apply(__me, args);
                return firstTime = false;
            }
            if ( timer ) { // 如果定时器还在,说明前一次延迟执行还没有完成
                return false;
            }
            timer = setTimeout(function () { // 延迟一段时间执行
                clearTimeout(timer);
                timer = null;
                __self.apply(__me, args);
            }, interval || 500 );
        };
    };
    
    window.onresize = throttle(function(){
        console.log( 1 );
    }, 500 );

    4. 分时函数

    数据过大时,比如好友列表中有上千的好友需要渲染,浏览器只会一口气全部去渲染出来,这很有可能占据大量的性能,造成严重卡顿。这里可以人为控制,将1000ms渲染1000个变成200ms渲染8个,分批渲染,减小压力

    var timeChunk = function( ary, fn, count ){
        var obj,
            t;
        var len = ary.length;
        var start = function(){
            for ( var i = 0; i < Math.min( count || 1, ary.length ); i++ ){  //确定执行的数量,然后遍历执行
                var obj = ary.shift();  //取出第一个元素
                fn( obj );              //执行函数
            }
        };
    
        return function(){
            t = setInterval(function(){
                if ( ary.length === 0 ){ // 如果数组长度为0,表示结束
                    return clearInterval( t );
                }
                start();
            }, 200 ); // 分批执行的时间间隔,也可以用参数的形式传入
        };
    };
    
    var ary = [];
    for ( var i = 1; i <= 1000; i++ ){
        ary.push( i );
    };  //这里是随便给一些数据,数据一共1000个 
    
    var renderFriendList = timeChunk( ary, function( n ){
        var div = document.createElement( 'div' );
        div.innerHTML = n;
        document.body.appendChild( div );
    }, 8 );
    
    renderFriendList();

    4.惰性加载函数

    在Web 开发中,因为浏览器之间的实现差异,一些嗅探工作总是不可避免。比如我们需要一个在各个浏览器中能够通用的事件绑定函数addEvent,常见的写法如下:

    var addEvent = function( elem, type, handler ){
        if ( window.addEventListener ){
            return elem.addEventListener( type, handler, false );
        }
        if ( window.attachEvent ){
            return elem.attachEvent( 'on' + type, handler );
        }
    };

    这个函数的缺点是,当它每次被调用的时候都会执行里面的if 条件分支,造成不必要的性能浪费。

    第二种方案如下,把它提前到到代码加载的时候,自执行一次,只调用一次判断,把内部的逻辑预先确定好。

    var addEvent = (function(){
        if ( window.addEventListener ){
            return function( elem, type, handler ){
                elem.addEventListener( type, handler, false );
            }
        }
        if ( window.attachEvent ){
            return function( elem, type, handler ){
                elem.attachEvent( 'on' + type, handler );
            }
        }
    })();

    这种方案也有个问题,也许我们从头到尾都不会使用addEvent函数,这一步就是个多余操作。

    第三种方案就是惰性载入函数方案,addEvent依然被声明为一个普通函数,在函数里依然有一些分支判断。但是在第一次进入条件分支之后,在函数内部会重写这个函数,重写之后的函数就是我们期望的addEvent函数,在下一次进入addEvent函数的时候,addEvent函数里不再存在条件分支语句:

     1 var addEvent = function( elem, type, handler ){
     2     if ( window.addEventListener ){
     3         addEvent = function( elem, type, handler ){           //和前面方案的区别在于这里重写了自己
     4             elem.addEventListener( type, handler, false );
     5         }
     6     }else if ( window.attachEvent ){
     7         addEvent = function( elem, type, handler ){
     8             elem.attachEvent( 'on' + type, handler );
     9         }
    10     }
    11     addEvent( elem, type, handler );      //这里只会第一次执行一次,后面都是直接执行重写后的
    12 };
    13 
    14 
    15 var div = document.getElementById( 'div1' );
    16 
    17 addEvent( div, 'click', function(){
    18     alert (1);
    19 });
    20 addEvent( div, 'click', function(){
    21     alert (2);
    22 });

    因为javascript自身的特点,闭包和高阶函数应用极多,在JavaScript中,很多设计模式都是通过闭包和高阶函数实现的。很重要的一点是,相对于模式的实现过程,我们更关注的是模式可以帮助我们完成什么

  • 相关阅读:
    Javascript FP-ramdajs
    微信小程序开发
    SPA for HTML5
    One Liners to Impress Your Friends
    Sass (Syntactically Awesome StyleSheets)
    iOS App Icon Template 5.0
    React Native Life Cycle and Communication
    Meteor framework
    RESTful Mongodb
    Server-sent Events
  • 原文地址:https://www.cnblogs.com/grey-zhou/p/6015183.html
Copyright © 2011-2022 走看看