今天在讀《JavaScript語言精粹》的時候,關於函數的一個部分,始終覺得有點難以理解,代碼如下: 1: var obj = (function(){ 2: var value = 0; 3: 4: return { 5: increment:function(inc){ 6: value += typeof inc == "number"?inc:1; 7: }, 8: getValue:function(){ 9: return value; 10: } 11: }; 12: }());注意代碼最後的標黃色的一對括弧,書中是這樣描述的“我們並沒有把一個函數賦值給obj,我們是把調用該函數後返回的結果賦值給它,該函數返回一個包含兩個方面的對象,並且這些方法繼續享有訪問value變數的特權”。其實像這樣代碼,用的也比較多,只是從來沒有仔細琢磨過,如果去掉了最後的一對括弧,那麼,返回的就是一個函數(對象)了。 如果我們在代碼中這樣調用上面的obj,則可以直接使用,執行typeof obj 返回結果為”object”,如: 1: obj.increment(1); 2: alert(obj.getValue());//1 3: obj.increment(2); 4: alert(obj.getValue());//3如果去掉最後的一對括弧,執行typeof obj 返回結果為“function”,需要使用先聲明初始化的方式,才能使用這個對象,如: 1: var o = obj(); 2: o.increment(1); 3: alert(o.getValue());//1 4: o.increment(2); 5: alert(o.getValue());//3這樣似乎理解了有沒有那對括弧的區別。但是作者後面舉的Fibonacci例子,才讓我恍然大悟,原來它還有這麼個好的用處。即,函數可以將先前的操作結果記錄在某個對象裡,從而避免無謂的重複運算。 我們通過普通的遞迴方式,計算Fibonacci數值,代碼如下: 1: var totalTimes = 0; 2: 3: var fibonacci = function (n){ 4: totalTimes +=1; 5: return n<2?n:fibonacci(n-1) + fibonacci(n-2); 6: } 7: 8: function calculateFibonacci(){ 9: totalTimes = 0; 10: for(var i =0;i<11;i++){ 11: document.getElementById("rlt").innerHTML +="//"+i+":"+fibonacci(i)+"<br/>"; 12: } 13: document.getElementById("rlt").innerHTML+="//totalTimes:"+totalTimes+"<br/>"; 14: }連續執行兩次,我們會看到如下的結果: 1: //0:0 2: //1:1 3: //2:1 4: //3:2 5: //4:3 6: //5:5 7: //6:8 8: //7:13 9: //8:21 10: //9:34 11: //10:55 12: //totalTimes:453 13: //0:0 14: //1:1 15: //2:1 16: //3:2 17: //4:3 18: //5:5 19: //6:8 20: //7:13 21: //8:21 22: //9:34 23: //10:55 24: //totalTimes:453是的,兩次都是執行了453次。因為每次都要重新從0,1開始計算。 但是,如果我們使用了如下代碼(注意它最後的一對括弧): 1: var totalTimes = 0; 2: var fibonacciMemo = function(){ 3: var meno = [0,1]; 4: var fib = function(n){ 5: totalTimes +=1; 6: var result = meno[n]; 7: if(typeof result != "number"){ 8: result = fib(n-1) + fib(n-2); 9: meno[n] = result; 10: } 11: return result; 12: 13: }; 14: return fib; 15: }(); 16: 17: function calculateFibonacciEx() 18: { 19: totalTimes = 0; 20: for(var i =0;i<11;i++){ 21: document.getElementById("rlt").innerHTML +="//"+i+":"+fibonacciMemo(i)+"<br/>"; 22: } 23: document.getElementById("rlt").innerHTML+="//totalTimes:"+totalTimes+"<br/>"; 24: }連續執行兩次,我們看到的結果卻是這樣: 1: //0:0 2: //1:1 3: //2:1 4: //3:2 5: //4:3 6: //5:5 7: //6:8 8: //7:13 9: //8:21 10: //9:34 11: //10:55 12: //totalTimes:29 13: //0:0 14: //1:1 15: //2:1 16: //3:2 17: //4:3 18: //5:5 19: //6:8 20: //7:13 21: //8:21 22: //9:34 23: //10:55 24: //totalTimes:11是的,第一次執行了29次計算,第二次只執行了11次計算。原因在於我們把結果儲存在了meno這個數組中。 當然,我們也可以通過其它方式,如全域變數的形式,將結果儲存下來,但是全域變數正是作者極力反對的應用和儲存模式之一,而且這個計算結果的儲存,本來就是對外透明的,調用它的人需要的只是計算結果,並不需要知道其中如何儲存,或者說我們並不需要這個計算函數以外的其它任何(全域)變數,或者任何後來維護這段代碼的人,不需要被告知在其它不知道的地方,有計算的中間結果需要他來做多餘的維護工作! 當然,我們也可以不用去省掉那400多次計算,不用關心程式的效率,或者不用擔心使用者的瀏覽器有多慢! 其實到現在也沒有相通,它們之間用處的區別或是優缺點在哪兒,因為始終沒想明白說這件事的Point在哪兒,所以,博文的標題總覺得沒說清楚哈!希望大家給予指點!