JS偏函数、组合函数、缓存函数

闭包的一些新玩法 偏函数,组合函数,缓存函数

(function( window ) { 
"use strict"; 
 
var functools = window.functools || {}; 
 
/** 
 * partial function 
 */ 
functools.curry = function( f, ctx ) { 
    var args = Array.prototype.slice.call( arguments, 2 ); 
    return function() { 
        var newArgs = args.concat(Array.prototype.slice.call( arguments )); 
        return f.apply( ctx, newArgs ); 
    } 
} 
 
/** 
 * right-left partial function 
 */ 
functools.rcurry = function( f, ctx ) { 
    var args = Array.prototype.slice.call( arguments, 2 ); 
    return function() { 
        var newArgs = Array.prototype.slice.call( arguments ).concat( args ); 
        return f.apply( ctx, newArgs ); 
    } 
} 
 
/** 
 * composite function 
 * @description `compose(f, g, x)(y) = f(g(y), x)` 
 */ 
functools.compose = function( f, g, f_ctx, g_ctx ) { 
    var args_for_f = Array.prototype.slice.call( arguments, 4 ); 
    return function() { 
        var args_for_g = Array.prototype.slice.call( arguments ); 
        var mid = g.apply( g_ctx, args_for_g ); 
        args_for_f.unshift(mid); 
        return f.apply( f_ctx, args_for_f); 
    } 
} 
 
/** 
 * Memoizer, and allows multiple arguments. 
 * @description Recursion needs to override function literal.  
 *              e.g. `func = memoize( func, null )` 
 */ 
functools.memoize = function( f, ctx ) { 
    var memo = {}; 
    return function() { 
        var key = Array.prototype.join.call(arguments, "_"); 
        var res = memo[ key ]; 
        if ( res === undefined ) { 
            res = f.apply( ctx, arguments ); 
            memo[ key ] = res; 
        } 
        return res; 
    } 
} 
 
/** 
 * Return the result and runtime of the function. 
 */ 
functools.timeit = function( f, ctx ) { 
    var args = Array.prototype.slice.call( arguments, 2 ); 
    var started = +new Date(); 
    var result = f.apply( ctx, args ); 
    var runtime = +new Date() - started; 
    return { 
        result: result, 
        runtime: runtime 
    } 
} 
 
function extend( a, b ) { 
    for ( var prop in b ) { 
        if ( b[ prop ] === undefined ) { 
            delete a[ prop ]; 
        // Avoid "Member not found" error in IE8 caused by setting window.constructor 
        } else if ( prop !== "constructor" || a !== window ) { 
            a[ prop ] = b[ prop ]; 
        } 
    } 
    return a; 
} 
 
if ( window.functools === undefined ) { 
    extend( window, functools ); 
    window.functools = functools; 
} 
 
// get at whatever the global object is, like window in browsers 
}( (function() {return this;}.call()) )); 

测试代码

<!DOCTYPE html> 
<html> 
<head> 
    <meta charset="utf-8"> 
    <title>functools test case</title> 
    <link rel="stylesheet" href="./qunit/qunit-1.11.0.css"> 
</head> 
<body> 
    <div id="qunit"></div> 
    <div id="qunit-fixture"></div> 
    <script src="./qunit/qunit-1.11.0.js"></script> 
    <script src="./functools.js"></script> 
    <script> 
        test( "curry test case", function() { 
            function sum() { 
                var sum = 0; 
                for ( var i = 0; i < arguments.length; i++ ) { 
                    sum +=  arguments[i]; 
                } 
                return sum; 
            } 
            var sumBase10 = curry( sum, null, 1, 2, 3, 4 ); 
 
            var value = sumBase10( 20, 30, 40 ); 
            equal( value, 100, "We expect value to be 100" ); 
        }); 
 
        test( "rcurry test case", function() { 
            function between( value, low, high, allowEqual ) { 
                if ( value > low && value < high ) { 
                    return true; 
                } 
                if ( allowEqual && ( value == low || value == high) ) { 
                    return true; 
                } 
                return false; 
            } 
            var betweenClosed = rcurry( between, null, true ), 
                between10and20 = rcurry( betweenClosed, null, 10, 20 ); 
 
            ok( between10and20( 10 ) , "10 is between 10 and 20" ); 
            ok( between10and20( 20 ) , "20 is between 10 and 20" ); 
            equal( between10and20( 9 ), false, "9 is not between 10 and 20" ); 
            equal( between10and20( 21 ), false, "21 is not between 10 and 20" ); 
        }); 
 
        test( "compose test case", function() { 
            /* only one argument */ 
            function trim( s ) { 
                return s.replace( /(^\s+)|(\s+$)/g, "" ); 
            } 
            function upper( s ) { 
                return s.toUpperCase(); 
            } 
            var trimUpper = compose( upper, trim, null, null ); 
 
            var value = trimUpper( "  trim and upper  " ); 
            equal( value, "TRIM AND UPPER", "trimUpper test case" ); 
 
            /* multiple arguments */ 
            function f( x, y ) { 
                return x + y; 
            } 
            var g = f; 
            var fg = compose( f, g, null, null, "F" ); 
 
            equal( fg( "X", "Y" ), "XYF", "We expect value to be XYF" ); 
        }); 
 
        test( "memoize test case", function() { 
            /* only one argument */ 
            function fib( n ) { 
                if ( n == 0 ) return 0; 
                if ( n == 1 ) return 1; 
                return fib( n - 1) + fib( n - 2 ); 
            } 
            var res_old = timeit( fib, null, 30 ); 
            fib = memoize( fib, null ); // override 
            var res_new = timeit( fib, null, 30 ); 
 
            equal( res_new.result, res_old.result, 
                [ "It's expected value.", res_old.runtime, res_new.runtime ].join("|") 
            ); 
 
            /* multiple arguments */ 
            function func( a, b ) { 
                func.counter++; 
                if ( a == 1 ) return 1; 
                return a + b + func( a - 1, b - 1 ); 
            } 
            func = memoize( func, null ); // override 
 
            func.counter = 0; // declare 
            var a = func( 5, 4 ), // = 25 
                a_cnt = func.counter; // = 5 
            func.counter = 0; // reset 
            var b = func( 6, 5 ), // 6 + 5 + func( 5, 4 ) << cache 
                b_cnt = func.counter; // = 1 
 
            equal( b, 36, [ "It's expected value.", a_cnt, b_cnt ].join("|") ); 
        }); 
    </script> 
</body> 
</html> 

转载请注明来源:新一 » JS偏函数、组合函数、缓存函数

赞 (0) 评论 (0) 分享 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址