JavaScript中的memoization(memoizing) 技術介紹
memoization 一詞是Donald Michie 根據拉丁語memorandum杜撰的一個詞。相應的動詞、過去分詞、ing形式有memoiz、memoized、memoizing.
Memoization 是 一種將函式返回值快取起來的方法,Memoization 原理非常簡單,就是把函式的每次執行結果都放入一個鍵值對(陣列也可以,視情況而定)中,在接下來的執行中,在鍵值對中查詢是否已經有相應執行過的值,如 果有,直接返回該值,沒有才 真正執行函式體的求值部分。很明顯,找值,尤其是在鍵值對中找值,比執行函式快多了。現代 JavaScript 的開發也已經大量使用這種技術。
- 一個簡單的使用memoization的例子
我們知道,在不同的瀏覽器中,xmlHttpRequest物件的具體實現都不同。需要判斷何種瀏覽器以執行具體的方法。這裡就有一個使用memoization來實現的例子。
- function createXHRObject = function(){
- //先把三個匿名函式快取起來。
- var methods = [
- function(){return new XMLHttpRequest();},
- function(){return new ActiveXObject("Msxml2.XMLHTTP");},
- function(){return new ActiveXObject("Microsoft.XMLHTTP");}
- ];
- for(var i=0,len=methods.length;i<len;i++){
- try{//這裡用try catch來代替了條件判斷,通常我不贊成這種寫法
- methods[i]();
- }
- catch(e){
- continue;//如果報異常,則執行下一次迴圈
- }
- // 把createXHRObject 與能正常執行的匿名函式對應起來,再呼叫createXHRObject不用再檢測瀏覽器了
- createXHRObject = method[i];
- return method[i];
- }
- }
以上是一個簡單的例子,第一次執 行createXHRObject()的時候,會迴圈判斷methods 中的方法,獲取一個能正確執行的,並將createXHRObject的引用指向這個方法。以後再使用這個方法的時候,不用去判斷,直接自動獲取正確的方 法。這省去了頻繁的ajax呼叫中瀏覽器的檢測。
當然,這個方法看上去效率的提升不是特別明顯,我之所以寫上來,是因為能比較清晰的理解memoization是如何實現的。在遞迴呼叫的時候,memoization的威力才能更好的顯現。
一個遞迴的例子:
- function fib(n) {
- if (n < 2) {
- return n;
- }
- return fib(n - 1) + fib(n - 2);
- }
這是一個經典的斐波納契序列,fib(20) 會把fib這個方法執行21891次,如果是fib(40),這會執行331160281次。
再看看如何使用memoization來實現,
- var iterMemoFib = (function() {
- var cache = [1, 1];
- var fib = function(n) {
- if (n >= cache.length) {
- //將一個遞迴轉換成了一個
- for (var i = cache.length; i <= n; i++) {
- cache[i] = cache[i - 2] + cache[i - 1];
- }
- }
- return cache[n-1];
- }
- return fib;
- })();
將Function的原型擴充套件memoize 和unmemoize 方法,這樣你可以對任何函式實現memoize和解除memoize,當然,這個方法要慎,對一些不是頻繁執行的函式,沒必要快取:
- Function.prototype.memoize = function() {
- var pad = {};
- var self = this;
- var obj = arguments.length > 0 ? arguments[i] : null;
- var memoizedFn = function() {
- // 把引數作為陣列儲存,作為鍵,把函式執行的結果作為值快取起來
- var args = [];
- for (var i = 0; i < arguments.length; i++) {
- args[i] = arguments[i];
- }
- if (!(args in pad)) {
- pad[args] = self.apply(obj, arguments);
- }
- return pad[args];
- }
- memoizedFn.unmemoize = function() {
- return self;
- }
- return memoizedFn;
- }
- Function.prototype.unmemoize = function() {
- alert(”Attempt to unmemoize an unmemoized function.”);
- return null;
- }
使用方法:
fib.memoize();參考文件: