手机浏览 RSS 2.0 订阅 膘叔的简单人生 , 腾讯云RDS购买 | 超便宜的Vultr , 注册 | 登陆
浏览模式: 标准 | 列表分类:Javascript

JavaScript中的memoization(memoizing) 技术介绍.

膘叔写在前面的话,关于memoization好象在jquery里被应用到了。但应该是1.2.6的事情了,最初的ajax是没有缓存的。后来增加了Data缓存。(记不清了,希望没有记错)
对于memoization的记忆大约是在07年,好象有一个red开头的国内的某个网站,把菲伯拉契数列用memoization实现了一遍,当时记得的测试结果,一个是3000多ms,一个是1000多ms,这已经不是一个量级的了。
于是后来,我看了一下,根据这个原理,写了一个gdata的cache,缓存一些我经常使用的函数和数据。现在当然是不用了。呵呵

以下是原文内容,它来自playgoogle的http://www.cssrain.cn/default.asp?id=1392:

最近在读《JavaScript 设计模式》一书,其中工厂模式中提到了memoizing技术,今天仔细整理了一下memoization 相关的资料,与大家共享。

memoization 一词是Donald Michie 根据拉丁语memorandum杜撰的一个词。相应的动词、过去分词、ing形式有memoiz、memoized、memoizing.

Memoization 是 一种将函数返回值缓存起来的方法,Memoization 原理非常简单,就是把函数的每次执行结果都放入一个键值对(数组也可以,视情况而定)中,在接下来的执行中,在键值对中查找是否已经有相应执行过的值,如 果有,直接返回该值,没有才 真正执行函数体的求值部分。很明显,找值,尤其是在键值对中找值,比执行函数快多了。现代 JavaScript 的开发也已经大量使用这种技术。

  •  一个简单的使用memoization的例子

我们知道,在不同的浏览器中,xmlHttpRequest对象的具体实现都不同。需要判断何种浏览器以执行具体的方法。这里就有一个使用memoization来实现的例子。

  1. function createXHRObject = function(){
  2.     //先把三个匿名函数缓存起来。
  3.     var methods = [
  4.         function(){return new XMLHttpRequest();},
  5.         function(){return new ActiveXObject("Msxml2.XMLHTTP");},
  6.         function(){return new ActiveXObject("Microsoft.XMLHTTP");}
  7.     ];
  8.     for(var i=0,len=methods.length;i<len;i++){
  9.         try{//这里用try catch来代替了条件判断,通常我不赞成这种写法
  10.             methods[i]();
  11.         }
  12.         catch(e){
  13.             continue;//如果报异常,则执行下一次循环
  14.         }
  15.         // 把createXHRObject 与能正常执行的匿名函数对应起来,再调用createXHRObject不用再检测浏览器了
  16.         createXHRObject = method[i];
  17.         return method[i];
  18.     }
  19. }

以上是一个简单的例子,第一次执 行createXHRObject()的时候,会循环判断methods 中的方法,获取一个能正确执行的,并将createXHRObject的引用指向这个方法。以后再使用这个方法的时候,不用去判断,直接自动获取正确的方 法。这省去了频繁的ajax调用中浏览器的检测。

当然,这个方法看上去效率的提升不是特别明显,我之所以写上来,是因为能比较清晰的理解memoization是如何实现的。在递归调用的时候,memoization的威力才能更好的显现。

一个递归的例子

  1. function fib(n) {
  2. if (n < 2) {
  3. return n;
  4. }
  5. return fib(n - 1) + fib(n - 2);
  6. }

 这是一个经典的斐波纳契序列,fib(20) 会把fib这个方法执行21891次,如果是fib(40),这会执行331160281次。

再看看如何使用memoization来实现,

  1.  var iterMemoFib = (function() {
  2.     var cache = [1, 1];
  3.     var fib = function(n) {
  4.         if (n >= cache.length) {
  5.             //将一个递归转换成了一个
  6.             for (var i = cache.length; i <= n; i++) {
  7.                 cache[i] = cache[i - 2] + cache[i - 1];
  8.             }
  9.         }
  10.         return cache[n-1];
  11.     }
  12.     return fib;
  13. })();

将Function的原型扩展memoize unmemoize 方法,这样你可以对任何函数实现memoize和解除memoize,当然,这个方法要慎,对一些不是频繁执行的函数,没必要缓存:

  1. Function.prototype.memoize = function() {
  2.     var pad  = {};
  3.     var self = this;
  4.     var obj  = arguments.length > 0 ? arguments[i] : null;
  5.  
  6.     var memoizedFn = function() {
  7.         // 把参数作为数组保存,作为键,把函数执行的结果作为值缓存起来
  8.         var args = [];
  9.         for (var i = 0; i < arguments.length; i++) {
  10.             args[i] = arguments[i];
  11.         }
  12.         if (!(args in pad)) {
  13.             pad[args] = self.apply(obj, arguments);
  14.         }
  15.         return pad[args];
  16.     }
  17.     memoizedFn.unmemoize = function() {
  18.         return self;
  19.     }
  20.     return memoizedFn;
  21. }
  22. Function.prototype.unmemoize = function() {
  23.     alert("Attempt to unmemoize an unmemoized function.");
  24.     return null;
  25. }
  26.  

使用方法:

fib.memoize();

参考文档:

  1. Memoizing functions in JavaScript
  2. JavaScript Memoization
  3. 提升JS性能:将递归转换为迭代
  4. MemoizationFrom Wikipedia, the free encyclopedia

如何在多个页面使用同一个HTML片段

我个人是觉得文章内的方法最有意思,既让代码显示在本页,又让这些代码不会被解析。以前我怎么没有想到过??(使用textarea来使代码完整存放在本页)
看来我的思维还是已经僵化了。。。
原文地址为:http://www.cnblogs.com/sanshi/archive/2009/08/07/1541380.html,是博客园sanshi的文章。

原文如下:

问题描述
有一个比较复杂的HTML片段(A),如果把这个HTML片段嵌入到其他页面中(B,C,D....)。
问题的关键是在HTML片段中有大量的JavaScript逻辑需要处理,比如说分页,点击事件响应等。

对于这个问题,我们用一个简单的例子来说明:
“页面上有一个按钮,点击此按钮引入一个HTML片段,此HTML片段中有分页按钮。”

1. 使用IFrame
主页面,点击一个按钮向页面引入一个IFrame:

XML/HTML代码
  1. <script type="text/javascript">  
  2.     $(function() {  
  3.         $("#clickToInsert").click(function() {  
  4.             $("#placeholder").html('<iframe src="iframe.htm"></iframe>');  
  5.         });  
  6.     });  
  7. </script>  
  8. <input type="button" id="clickToInsert" value="Insert HTML" />  
  9. <div id="placeholder">  
  10. </div>  
IFrame页面,模拟分页的情况:
XML/HTML代码
  1. <script type="text/javascript">  
  2.     $(function() {  
  3.         var parent = $("#complex_page_segment");  
  4.         $(".previous", parent).click(function() {  
  5.             $(".content", parent).html("Previous Page Content");  
  6.         });  
  7.         $(".next", parent).click(function() {  
  8.             $(".content", parent).html("Next Page Content");  
  9.         });  
  10.     });  
  11. </script>  
  12. <div id="complex_page_segment">  
  13.     <input type="button" value="Previous Page" class="previous" />  
  14.     <input type="button" value="Next Page" class="next" />  
  15.     <div class="content">  
  16.         Page Content</div>  
  17. </div>  
2. AJAX返回页面片段,并注册事件
注:我们通过textarea来模拟返回的HTML片段。
XML/HTML代码
  1. <script type="text/javascript">  
  2.     $(function() {  
  3.         $("#clickToInsert").click(function() {  
  4.             $("#placeholder").html($("#clone").val());  
  5.             var parent = $("#complex_page_segment");  
  6.             $(".previous", parent).click(function() {  
  7.                 $(".content", parent).html("Previous Page Content");  
  8.             });  
  9.             $(".next", parent).click(function() {  
  10.                 $(".content", parent).html("Next Page Content");  
  11.             });  
  12.         });  
  13.     });  
  14. </script>  
  15. <input type="button" id="clickToInsert" value="Insert HTML" />  
  16. <div id="placeholder">  
  17. </div>  
  18. <textarea id="clone" style="display: none;">  
  19. <div id="complex_page_segment">  
  20.     <input type="button" value="Previous Page" class="previous" />  
  21.     <input type="button" value="Next Page" class="next" />  
  22.     <div class="content">Page Content</div>  
  23. </div>  
  24. </textarea>  
由于我们需要在多个页面引用同一个HTML片段,这种方法导致大量事情处理被重复性的拷贝粘贴,明显我们需要将公共的方法提取出来。

3. AJAX返回页面片段,并调用页面片段中的函数注册事件
XML/HTML代码
  1. <script type="text/javascript">  
  2.     $(function() {  
  3.         $("#clickToInsert").click(function() {  
  4.             $("#placeholder").html($("#clone").val());  
  5.             init_complex_page_segment();  
  6.         });  
  7.     });  
  8. </script>  
  9. <input type="button" id="clickToInsert" value="Insert HTML" />  
  10. <div id="placeholder">  
  11. </div>  
  12. <textarea id="clone" style="display: none;">  
  13. <script type="text/javascript">  
  14.     function init_complex_page_segment() {  
  15.         var parent = $("#complex_page_segment");  
  16.         $(".previous", parent).click(function() {  
  17.             $(".content", parent).html("Previous Page Content");  
  18.         });  
  19.         $(".next", parent).click(function() {  
  20.             $(".content", parent).html("Next Page Content");  
  21.         });  
  22.     }  
  23. </script>  
  24. <div id="complex_page_segment">  
  25.     <input type="button" value="Previous Page" class="previous" />  
  26.     <input type="button" value="Next Page" class="next" />  
  27.     <div class="content">Page Content</div>  
  28. </div>  
  29. </textarea>  

其实我们可以更进一步,完全没必要手工调用这个函数,而是可以在返回的HTML片段中让其自动执行。

4. AJAX返回页面片段,其事件自动注册

XML/HTML代码
  1. <script type="text/javascript">  
  2.     $(function() {  
  3.         $("#clickToInsert").click(function() {  
  4.             $("#placeholder").html($("#clone").val());  
  5.         });  
  6.     });  
  7. </script>  
  8. <input type="button" id="clickToInsert" value="Insert HTML" />  
  9. <div id="placeholder">  
  10. </div>  
  11. <textarea id="clone" style="display: none;">  
  12. <script type="text/javascript">  
  13.     $(function() {  
  14.         var parent = $("#complex_page_segment");  
  15.         $(".previous", parent).click(function() {  
  16.             $(".content", parent).html("Previous Page Content");  
  17.         });  
  18.         $(".next", parent).click(function() {  
  19.             $(".content", parent).html("Next Page Content");  
  20.         });  
  21.     });  
  22. </script>  
  23. <div id="complex_page_segment">  
  24.     <input type="button" value="Previous Page" class="previous" />  
  25.     <input type="button" value="Next Page" class="next" />  
  26.     <div class="content">Page Content</div>  
  27. </div>  
  28. </textarea>  
最后一种方法和第一种IFrame的方式是我们所推荐的。

 

JavaScript链式调用的设计

链式调用,在PHP中使用的时候往往被人称作:连贯操作。

如果经常用ZF的人,会在Select类中很明显的看到这一点。目前连贯操作已经几乎被各大框架所引用(无非是以前不返回值,现在返回$this。哈哈)

以下是cssrain网站联盟中的karryplaygoogle)写的JS的连贯操作(链式调用),其实在jQuery中,这样的操作太常见了。

以下为全文内容:

 用过jQuery的朋友一定对jQuery中方法的链式调用印象深刻,最近发布的YUI3也支持了方法的链式调用。这是一个非常不错的语法特性, 能让代码更加简洁、易读。很多时候链式调用可以避免多次重复使用一个对象变量,从而减少代码,而js是一种客户端执行的脚本语言,减少代码就减少了js文 件的大小,减少了服务器的压力。链式调用这么多优点,它是如何实现的呢?这篇文章就是想探讨一下这个问题。

链式调用例子如:$("p").append("test").fadeIn("fast");

看一段jQuery的源码:

append: function() {
        return this.domManip(arguments, true, function(elem){
            if (this.nodeType == 1)
                this.appendChild( elem );
        });
    }

以上是jQuery中append方法的实现。append方法是向每个匹配的元素内部追加内容的方法,很明显,属于赋值操作,但是却有返回值,返回的是当前操作的dom(通常返回this指针)。这是链式调用的关键,你会发现这不过是个语法小技巧而已。

很明显,在赋值器方法中(或者本身没有返回值的方法中)很容易实现链式调用,而取值器相对来说不好实现链式调用,因为你需要取值器返回你需要的数据而不是this指针(当然,如果你坚持要实现链式方法,也可以用回调函数来实现)。

设计一个简单的支持链式调用的类:

JavaScript代码
  1. function Dog(name,color){  
  2.         this.name=name||"";  
  3.         this.color=color||"";  
  4. }  
  5. Dog.prototype.setName=function(name){  
  6.         this.name=name;  
  7.         return this;  
  8. };  
  9. Dog.prototype.setColor(color){  
  10.         this.color=color;  
  11.         return this;  
  12. };  
  13. Dog.prototype.yelp(){  
  14.         alert("我的名字叫:"+this.name+",我的颜色是:"+this.color);  
  15.         return this;  
  16. };  

使用方式:

JavaScript代码
  1. var dog = new Dog();  
  2. dog.setName("旺财").setColor("白色").yelp();  

取值器你也想支持链式调用?
那就用回调函数来实现,将本来应该返回的值直接传给回调函数,而return仍然返回this指针。接着上面的Dog类写一个方法:

JavaScript代码
  1. Dog.prototype.getName(callback){  
  2.         callback.call(this,this.name);  
  3.         return this;  
  4. }  

使用方式:

JavaScript代码
  1. function showName(name){  
  2.         alert(name);  
  3. }  
  4. dog.setName("旺财").getName(showName).setColor("白色");  

Tags: 链式, 连贯操作

javascript所有on事件集合

文章来自http://www.knowsky.com/537382.html,虽然不满这个网站的广告,但还是有些东西值得一观

事件 描述
onactivate 当对象设置为活动元素时触发。
onafterprint 对象所关联的文档打印或打印预览后立即在对象上触发。
onbeforeactivate 对象要被设置为当前元素前立即触发。
onbeforecut 当选中区从文档中删除之前在源对象触发。
onbeforedeactivate 在 activeElement 从当前对象变为父文档其它对象之前立即触发。
onbeforeeditfocus 在包含于可编辑元素内的对象进入用户界面激活状态前或可编辑容器变成控件选中区前触发。
onbeforepaste 在选中区从系统剪贴板粘贴到文档前在目标对象上触发。
onbeforeprint 对象的关联文档打印或打印预览前在对象上触发。
onbeforeunload 在页面将要被卸载前触发。
onclick 在用户用鼠标左键单击对象时触发。
oncontextmenu 在用户使用鼠标右键单击客户区打开上下文菜单时触发。
oncontrolselect 当用户将要对该对象制作一个控件选中区时触发。
oncut 当对象或选中区从文档中删除并添加到系统剪贴板上时在源元素上触发。
ondblclick 当用户双击对象时触发。
ondeactivate 当 activeElement 从当前对象变为父文档其它对象时触发。
ondrag 当进行拖曳操作时在源对象上持续触发。
ondragend 当用户在拖曳操作结束后释放鼠标时在源对象上触发。
ondragenter 当用户拖曳对象到一个合法拖曳目标时在目标元素上触发。
ondragleave 当用户在拖曳操作过程中将鼠标移出合法拖曳目标时在目标对象上触发。
ondragover 当用户拖曳对象划过合法拖曳目标时持续在目标元素上触发。
ondragstart 当用户开始拖曳文本选中区或选中对象时在源对象上触发。
ondrop 当鼠标按钮在拖曳操作过程中释放时在目标对象上触发。
onfilterchange 当可视滤镜更改状态或完成转换时触发。
onfocusin 当元素将要被设置为焦点之前触发。
onfocusout 在移动焦点到其它元素之后立即触发于当前拥有焦点的元素上触发。
onkeydown 当用户按下键盘按键时触发。
onkeypress 当用户按下字面键时触发。
onkeyup 当用户释放键盘按键时触发。
onload 当浏览器装入对象后立即触发。
onlosecapture 当对象失去鼠标捕捉时触发。
onmousedown 当用户用任何鼠标按钮单击对象时触发。
onmouseenter 当用户将鼠标指针移动到对象内时触发。
onmouseleave 当用户将鼠标指针移出对象边界时触发。
onmousemove 当用户将鼠标划过对象时触发。
onmouseout 当用户将鼠标指针移出对象边界时触发。
onmouseover 当用户将鼠标指针移动到对象内时触发。
onmouseup 当用户在鼠标位于对象之上时释放鼠标按钮时触发。
onmousewheel 当鼠标滚轮按钮旋转时触发。
onmove 当对象移动时触发。
onmoveend 当对象停止移动时触发。
onmovestart 当对象开始移动时触发。
onpaste 当用户粘贴数据以便从系统剪贴板向文档传送数据时在目标对象上触发。
onpropertychange 当在对象上发生对象上发生属性更改时触发。
onreadystatechange 当对象状态变更时触发。
onresizeend 当用户更改完控件选中区中对象的尺寸时触发。
onresizestart 当用户开始更改控件选中区中对象的尺寸时触发。
onscroll 当用户滚动对象的滚动条时触发。
onselect 当当前选中区改变时触发。
onselectstart 对象将要被选中时触发。
onunload 在对象卸载前立即触发。

json2select的全国城市数据

怎么说呢,这个东西不太适合存储ID进数据库的用户,因为我在打开该文件后发现,里面的key全是t,s之类的,作者shawphy也建议换成j,s,q之类的key,但对于普通 用户来说太累了。

当然,这个数据应该是配合json2select插件而来的,所以我们不能奢求什么。
如果确实需要,到这里下载数据,然后自己通过程序生成json文件,修改一下json2select插件。当然更能做的就是问一下google,百度,谁有三级联动的代码。

原文如下:

去年写过一个小插件,json2select 一直想要转换出配套的数据,但总有这样那样的问题之后,终于拖到了现在。
好在一切悲剧的日子已经过去了,新时代已经来临。猛击这里
三级联动,直辖市精确到城镇街道,一般城市到达区。强烈推荐。

这一切都要感谢 Asfman ,辛勤的工作,当然也少不了感谢搜狐白社会,是他们提供了这么好的数据,也要感谢我们伟大祖国,我们中华民族地大物博,省市也很多……

这套数据有100K多一点,但可以想办法压缩
把其中重复字符串替换掉,把街道替换成j,市替换成s,区替换成q,都可以减少文件体积。要用的时候先用正则替换后,再eval执行字符串就能得到数据了,可以达到68K左右
另外,保存成gb2312的话也可以减小体积。目前我手头的是52K左右大小了。但可能导致乱码,慎用。

进一步,如果通过词频分析,找出重复最多的前20个,替换的话,相信可以把体积维持在30K以下了。但手头没工具,最近也很忙,所以大家自己发挥吧。
我粗略压缩后的东西我就不单独发布了。