Javascript Memoizer浅析


Posted in Javascript onOctober 16, 2014

以下来自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都是树分支:

Javascript Memoizer浅析

(注:上面图中的“结果”也是一个闭包,只不过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]; 

    }; 

}

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

Javascript 相关文章推荐
禁止你的左键复制实用技巧
Jan 04 Javascript
自动刷新网页,自动刷新当前页面,JS调用
Jun 24 Javascript
在表单提交前进行验证的几种方式整理
Jul 31 Javascript
Javascript Object 对象学习笔记
Dec 17 Javascript
javascript 构造函数方式定义对象
Jan 02 Javascript
JQuery实现鼠标移动图片显示描述层的方法
Jun 25 Javascript
JavaScript动态添加事件之事件委托
Jul 12 Javascript
BootStrap Fileinput的使用教程
Dec 30 Javascript
canvas实现弧形可拖动进度条效果
May 11 Javascript
jQuery实现的表格前端排序功能示例
Sep 18 jQuery
详解Javascript中new()到底做了些什么?
Mar 29 Javascript
JS异步执行结果获取的3种解决方式
Feb 19 Javascript
让JavaScript和其它资源并发下载的方法
Oct 16 #Javascript
JavaScript实现继承的4种方法总结
Oct 16 #Javascript
JavaScript实现网页截图功能
Oct 16 #Javascript
JavaScript跨域方法汇总
Oct 16 #Javascript
js阻止事件追加的具体实现
Oct 15 #Javascript
用原生js做个简单的滑动效果的回到顶部
Oct 15 #Javascript
原生的html元素选择器类似jquery选择器
Oct 15 #Javascript
You might like
PHPMYADMIN 简明安装教程 推荐
2010/03/07 PHP
PHP中数字检测is_numeric与ctype_digit的区别介绍
2012/10/04 PHP
如何用php生成扭曲及旋转的验证码图片
2013/06/07 PHP
PHP基于数组实现的分页函数实例
2014/08/20 PHP
ThinkPHP中图片按比例切割的代码实例
2019/03/08 PHP
Laravel 微信小程序后端实现用户登录的示例代码
2019/11/26 PHP
【消息提示组件】,兼容IE6/7&amp;&amp;FF2
2007/09/04 Javascript
jQuery 验证插件 Web前端设计模式(asp.net)
2010/10/17 Javascript
有效提高JavaScript执行效率的几点知识
2015/01/31 Javascript
JS选项卡动态替换banner图片路径的方法
2015/05/11 Javascript
AngularJS基础 ng-disabled 指令详解及简单示例
2016/08/01 Javascript
浅析Javascript ES6中的原生Promise
2016/08/25 Javascript
浅谈node的事件机制
2017/10/09 Javascript
AngularJS遍历获取数组元素的方法示例
2017/11/11 Javascript
angular2组件中定时刷新并清除定时器的实例讲解
2018/08/31 Javascript
详解Vue-Router源码分析路由实现原理
2019/05/15 Javascript
Laravel admin实现消息提醒、播放音频功能
2019/07/10 Javascript
three.js 制作动态二维码的示例代码
2020/07/31 Javascript
windows及linux环境下永久修改pip镜像源的方法
2016/11/28 Python
Python 实现简单的shell sed替换功能(实例讲解)
2017/09/29 Python
PyQt5每天必学之滑块控件QSlider
2018/04/20 Python
python常用函数与用法示例
2019/07/02 Python
基于python+selenium的二次封装的实现
2020/01/06 Python
Python如何将字符串转换为日期
2020/07/31 Python
Columbia美国官网:美国著名的户外服装品牌
2016/11/24 全球购物
全球性的女装店:storets
2019/06/12 全球购物
大学生涯自我鉴定
2014/01/16 职场文书
函授毕业生自我鉴定范文
2014/03/25 职场文书
应届生求职自荐信范文
2014/04/07 职场文书
《真想变成大大的荷叶》教学反思
2014/04/14 职场文书
经典禁毒标语
2014/06/16 职场文书
Go各时间字符串使用解析
2021/04/02 Golang
详解Java实践之适配器模式
2021/06/18 Java/Android
OpenCV实现普通阈值
2021/11/17 Java/Android
mysql的Buffer Pool存储及原理
2022/04/02 MySQL
Go结合Gin导出Mysql数据到Excel表格
2022/08/05 Golang