Javascript Memoizer浅析_javascript技巧

以下来自John Hann的实现,这段代码引起了我的注意,它用巧妙的方法把方法调用的结果缓存起来了。

代码解析:

复制代码 代码如下:

// memoize: 使用memoization来缓存的通用方法
// func: 要被缓存的方法
// context: 方法执行上下文
// Note: 方法必须是外部可访问的,参数是可字符序列化的
function memoize (func, context) {
    function memoizeArg (argPos) { //参数表示原始方法中参数的位置
        var cache = {}; //这个缓存的key是参数,value是执行结果
        return function () { //返回一个函数闭包
            if (argPos == 0) { //第一个参数,如果参数在缓存的key中不存在,就执行原始函数并且存储执行结果
                if (!(arguments[argPos] in cache)) {
                    cache[arguments[argPos]] = func.apply(context, arguments);
                }
                return cache[arguments[argPos]];
            }
            else { //不是第一个参数,如果参数在缓存的key中不存在,就递归执行memoizeArg方法,原始方法中参数的位置-1
                if (!(arguments[argPos] in cache)) {
                    cache[arguments[argPos]] = memoizeArg(argPos - 1);
                }
                return cache[arguments[argPos]].apply(this, arguments);
            }
        }
    }
    var arity = func.arity || func.length; //func参数的长度,javascript中用length属性,其它的用arity属性
    return memoizeArg(arity - 1); //从最后一个参数开始递归
}

使用:

复制代码 代码如下:

var mem = memoize(func, this);  
alert(mem.call(this,1,1,2));  
alert(mem.call(this,2,1,2));  
alert(mem.call(this,3,1,3));  
alert(mem.call(this,2,2,4));

看似简单,再一看好像也并不易懂,可是如果能对闭包的使用比较熟悉的话,就很好理解了。经过上面几次mem.call的调用之后,形成的是一棵树,每个节点都是一个闭包,每个闭包内有一个cache,每个cache的key都是树分支:

(注:上面图中的“结果”也是一个闭包,只不过argPos为0而已)

不过方法有诸多,比如limboy说:

复制代码 代码如下:

function Memoize(fn){
    var cache = {};
    return function(){
        var key = [];
        for( var i=0, l = arguments.length; i < l; i++ )
            key.push(arguments[i]);
        if( !(key in cache) )
            cache[key] = fn.apply(this, arguments);
        return cache[key];
    };
}

实现更简易,不过把参数push到一个数组内,再把数组当key,而key是只支持字符串型的,因此这点在使用上需要注意(比如一个对象tostring之后可能只看到”[object Object]“了),它的功能比上面那个要弱一些。

改进这一点也不难,把参数另立一个对象即可,而原cache对象和这个另立的参数对象使用一个ID关联起来:

复制代码 代码如下:

function Memoize(fn){
    var cache = {}, args = {};
    return function(){
        for( var i=0, key = args.length; i < key; i++ ) {
            if( equal( args[i], arguments ) )
                return cache[i];
        }
        args[key] = arguments;
        cache[key] = fn.apply(this, arguments);
        return cache[key];
    };
}

还有一些其他的办法,都可以写成简洁的函数式方法。

时间: 2024-09-27 05:47:35

Javascript Memoizer浅析_javascript技巧的相关文章

javascript动画浅析_javascript技巧

动画原理 所谓的动画,就是通过一些列的运动形成的动的画面.在网页中,我们可以通过不断的改变元素的css值,来达到动的效果. 用到的公式 总距离S = 总时间T * 速度V 即: V = S/T 当前距离s = S/T * 已耗时t 即: s = S * (t/T) 即:当前距离 = 总距离 * (已耗时/总时间) 即:动画元素开始值 + (动画元素结束值 - 动画元素开始值) * (当前时间-开始时间) / (动画需要时间) + 值的格式 有了上面这些公式,我们就能利用javascript的se

JavaScript事件用法浅析_javascript技巧

本文实例讲述了JavaScript事件用法.分享给大家供大家参考,具体如下: JavaScript通过事件与HTML交互. 事件流 事件流规定了事件的触发规则和顺序.DOM2规定了事件流包括三个阶段:事件捕获 -> 目标触发除 -> 事件冒泡.DOM2规定在事件捕获阶段不应调用事件处理程序,不过各大浏览器都不鸟它.DOM2级的事件处理程序操作函数对:addEventListener和removeEventListener的第三个参数则把这种事变成了DIY,这是一种妥协,同时让初学者认为dom的

JavaScript递归操作实例浅析_javascript技巧

本文实例分析了JavaScript递归操作.分享给大家供大家参考,具体如下: 问题 一个简单的递归,求n的阶乘: function factorial(n){ if (n<=1) { return 1; }else{ return factorial(n-1)*n; } } 如果像下面这样使用它,则会出错: var fcopy = factorial; factorial = null; alert(fcopy(3)); 因为fcopy指向的函数实体调用了factorial,而factorial

javascript的函数劫持浅析_javascript技巧

javascript的函数劫持是什么? 函数劫持,顾名思义,即在一个函数运行之前把它劫持下来,添加我们想要的功能.当这个函数实际运行的时候,它已经不是原本的函数了,而是带上了被我们添加上去的功能.这也是我们常见的钩子函数的原理之一. 乍一看上去,这很像是函数的改写.函数的改写也可以理解为是函数劫持的一种,但是这种方式太恶心了.作为一个劫持者,在绑票获得好处以后也应该遵守职业道德,把人原封不动地还回去,所以我们得在合适的地方把函数原本的功能给重新调用回来. 推而广之,其实"劫持"这一概念

浅析如何利用JavaScript进行语音识别_javascript技巧

一.基础用法 var recognition = new webkitSpeechRecognition(); recognition.onresult = function(event) { console.log(event) } recognition.start(); 这里操作实际会让用户授权页面开启麦克风,如果用户允许的话,用户可以开始说话了,如果你停说话了,onresult注册的时间 则会被触发,并会讲捕获的音频返回成一个JavaScript对象. 二.响应流 你需要等待用户准备好对

浅析2种JavaScript继承方式_javascript技巧

js继承方法最主要的是2种,一种是通过原型的方式,一种是通过借用call&apply的构造函数方式.1.原型(prototype): function Body(name,age){// 创建一个Body类 this.name = name;// 赋予基础属性name.age this.age = age; } Body.prototype.sayName =function() {// 给原型定义一个sayName的方法 console.log(this.name); } var a = ne

Javascript自执行匿名函数(function() { })()的原理浅析_javascript技巧

函数是JavaScript中最灵活的一种对象,这里只是讲解其匿名函数的用途.匿名函数指没有指定函数名或指针的函数,自执行匿名函数只是其中一种,下文中称这种函数为:自执行函数 下面是一个最常见的自执行函数: // 传统匿名函数 (function() { alert('hello'); })(); 这段代码的执行效果就是在页面再载入时弹出:"hello" 是什么促使它自动执行的?,来看下面的代码 // 在传统写法上去掉小括号,并在前面加上运算符 ~,!,+,- ~function(){

Javascript类型系统之undefined和null浅析_javascript技巧

前面的话 一般的程序语言,表示空的只有null,但javascript的设计者Brendan Eich却设计了一个undefined,这无疑增加了程序复杂度,但这样做也是有一定原因的.本文将详细介绍javascript中的undefined和null 历史原因 1995年JavaScript诞生时,最初像Java一样,只设置了null作为表示"无"的值.根据C语言的传统,null被设计成可以自动转为0 但是,JavaScript的设计者Brendan Eich,觉得这样做还不够,有两个

JavaScript iframe的相互操作浅析_javascript技巧

iframe元素也就是文档中的文档,或者好像浮动的框架(frame).关于iframe的操作一直是一个疑难点,网上有很多这方面的文章介绍.下面我结合自己的经验,整理一下笔记记录下来.一.页面三个页面:父页面和两个子页面,两个子页面在父页面的两个iframe里.1.父页面MainForm.aspx <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="MainForm.aspx.cs&quo