JS偏函數、組合函數、緩存函數
一、JavaScript代碼
[附件]/functools.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 newArgs = Array.prototype.slice.call( arguments ).concat( args );
* 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 );
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 = {};
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 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()) ));
二、QUnit測試代碼
[附件]/functools.html
<!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 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() {
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("|")
);
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>
ps:附件解壓,打開functools.html即可測試。
<a href="http://down.51cto.com/data/2362319" target="_blank">附件:http://down.51cto.com/data/2362319</a>
本文轉自winorlose2000 51CTO部落格,原文連結:http://blog.51cto.com/vaero/1138913,如需轉載請自行聯系原作者