【讀書筆記】《JavaScript權威指南》第8章函數

函數定義(掌握)

  函數使用function關鍵字來定義,它可以用在函數定義表達式或者函數聲明語句裏。在兩種形式中,函數定義都從function關鍵字開始,其後跟隨這些組成部分:

  • 函數名稱標識符。函數名稱是函數聲明語句必需的部分。它的用途就像變量的名字,新定義的函數對象會賦值給這個變量。對函數定義表達式來說,這個名字是可選的:如果存在,該名字只存在於函數體中,並指代該函數對象本身。
  • 一對圓括號,其中包含由0個或者多個用逗號隔開的標識符組成的列表。這些標識符是函數的參數名稱,它們就像函數體中的局部變量一樣。
  • 一對花括號,其中包含0條或多條JavaScript語句。這些語句構成了函數體:一旦調用函數,就會執行這些語句。

  函數聲明語句“被提前”到外部腳本或外部函數作用域的頂部,所以以這種方式聲明的函數,可以被在它定義之前出現的代碼所調用。
  不過,以表達式定義的函數就另當別論了,爲了調用一個函數,必須要能引用它,而要使用一個以表達式方式定義的函數之前,必須把它賦值給一個變量。變量的聲明提前了,但給變量賦值是不會提前的,所以,以表達式方式定義的函數在定義之前無法調用。

函數調用(掌握)

  構成函數主體的JavaScript代碼在定義之時並不會執行,只有調用該函數時,它們纔會執行。有4種方式來調用JavaScript函數:

  • 作爲函數
  • 作爲方法
  • 作爲構造函數
  • 通過它們的cal1()和apply()方法間接調用

函數調用

printprops({x:1});
var total=distance(o,0,2,1)+distance(2,1,3,5);
var probability=factorial(5)/factorial(13);

  對於普通的函數調用,函數的返回值成爲調用表達式的值。如果該函數返回是因爲解釋器到達結尾,返回值就是undefined。如果函數返回是因爲解釋器執行到一條return語句,返回值就是return之後的表達式的值,如果return語句沒有值,則返回undefined。
  根據ECMAScript3和非嚴格的ECMAScript5對函數調用的規定,調用上下文(this的值)是全局對象。然而,在嚴格模式下,調用上下文則是undefined。
  以函數形式調用的函數通常不使用this關鍵字。不過,“this”可以用來判斷當前是否是嚴格模式。

/定義並調用一個函數來確定當前腳本運行時是否爲嚴格模式
var strict=(function(){ return !this;}());

方法調用

  對方法調用的參數和返回值的處理,和上面所描述的普通函數調用完全一致。但是,方法調用和函數調用有一個重要的區別,即:調用上下文。下面是一個具體的例子:

var calculator={//對象直接量
operand1:1,
operand2:1,
add:function(){
//注意this關鍵字的用法,this指代當前對象
this.result=this.operand1+this.operandz;
};
calculator.add();//這個方法調用計算1+1的結果
calculator.result //=>2

  需要注意的是,this是一個關鍵字,不是變量,也不是屬性名。JavaScript的語法不允許給this賦值。
  和變量不同,關鍵字this沒有作用域的限制,嵌套的函數不會從調用它的函數中繼承this。如果嵌套函數作爲方法調用,其this的值指向調用它的對象。如果嵌套函數作爲函數調用,其this值不是全局對象(非嚴格模式下)就是undefined(嚴格模式下)。很多人誤以爲調用嵌套函數時this會指向調用外層函數的上下文。如果你想訪問這個外部函數的this值,需要將this的值保存在一個變量裏,這個變量和內部函數都同在一個作用域內。通常使用變量self來保存this。

構造函數調用

  如果函數或者方法調用之前帶有關鍵宇new,構造函數調用和普通的函數調用以及方法調用在實參處理、調用上下文和返回值方面都有不同。
  構造函數調用創建一個新的空對象,這個對象繼承自構造函數的prototype屬性。構造函數試圖初始化這個新創建的對象,並將這個對象用做其調用上下文,因此構造函數可以使用this關鍵字來引用這個新創建的對象。注意,儘管構造函數看起來像一個方法調用,它依然會使用這個新對象作爲調用上下文。也就是說,在表達式new o.m()中,調用上下文並不是o。
  構造函數通常不使用return關鍵宇,它們通常初始化新對象,當構造函數的函數體執行完畢時,它會顯式返回。在這種情況下,構造函數調用表達式的計算結果就是這個新對象的值。然而如果構造函數顯式地使用return語句返回一個對象,那麼調用表達式的值就是這個對象。如果構造函數使用return語句但沒有指定返回值,或者返回一個原始值,那麼這時將忽略返回值,同時使用這個新對象作爲調用結果。

間接調用

  JavaScript中的函數也是對象,和其他JavaScript對象沒什麼兩樣,函數對象也可以包含方法。其中的兩個方法ca11()和apply()可以用來間接地調用函數。兩個方法都允許顯式指定調用所需的this值,也就是說,任何函數可以作爲任何對象的方法來調用,哪怕這個函數不是那個對象的方法。兩個方法都可以指定調用的實參。ca11()方法使用它自有的實參列表作爲函數的實參,apply()方法則要求以數組的形式傳入參數。

函數的實參和形參(掌握)

可選形參

  當調用函數的時候傳入的實參比函數聲明時指定的形參個數要少,剩下的形參都將設置爲undefined值。因此在調用函數時形參是否可選以及是否可以省略應當保持較好的適應性。爲了做到這一點,應當給省略的參數賦一個合理的默認值。

a=a || null;

  需要注意的是,當用這種可選實參來實現函數時,需要將可選實參放在實參列表的最後。那些調用你的函數的程序員是沒辦法省略第一個實參並傳入第二個實參的,它必須將undefined作爲第一個實參顯式傳入。同樣注意在函數定義中使用註釋/*optional*/來強調形參是可選的。

可變長的實參列表:實參對象

  當調用函數的時候傳入的實參個數超過函數定義時的形參個數時,沒有辦法直接獲得未命名值的引用。參數對象解決了這個問題。在函數體內,標識符arguments是指向實參對象的引用,實參對象是一個類數組對象,這樣可以通過數字下標就能訪問傳入函數的實參值,而不用非要通過名字來得到實參。

function max(/*...*/){
    var max=Number.NEGATIVE_INFINITY;
//遍歷實參,查找並記住最天值
    for(var i=0;i< arguments.length;i++)
        if(arguments[i]>max)max =arguments[i];
        //返回最大值
        return max;
}
var largest =max(1,10,100,2,3,1000,4,5,10000,6);//=>10000

  通過實參名字來修改實參值的話,通過arguments[]數組也可以獲取到更改後的值,在ECMAScript 5中移除了實參對象的這個特殊特性。在嚴格模式下還有一點(和非嚴格模式下相比的)不同,在非嚴格模式中,函數裏的arguments僅僅是一個標識符,在嚴格模式中,它變成了一個保留字。嚴格模式中的函數無法使用arguments作爲形參名或局部變量名,也不能給arguments賦值。

callee和caller屬性

  除了數組元素,實參對象還定義了callee和caller屬性。在ECMAScript 5嚴格模式中,對這兩個屬性的讀寫操作都會產生一個類型錯誤。而在非嚴格模式下,ECMAScript標準規範規定callee屬性指代當前正在執行的函數。caller是非標準的,但大多數瀏覽器都實現了這個屬性,它指代調用當前正在執行的函數的函數。通過caller屬性可以訪問調用棧。callee屬性在某些時候會非常有用,比如在匿名函數中通過callee來遞歸地調用自身。

var factorial=function(x){
    if(x<=1) return 1;
    return x* arguments. callee(x-1);
};

將對象屬性用做實參

  當一個函數包含超過三個形參時,對於程序員來說,要記住調用函數中實參的正確順序實在讓人頭疼。最好通過名/值對的形式來傳入參數,這樣參數的順序就無關緊要了。定義函數的時候,傳入的實參都寫入一個單獨的對象之中,在調用的時候傳入一個對象,對象中的名/值對是真正需要的實參數據。

作爲值的函數

  在JavaScript中,函數不僅是一種語法,也是值,也就是說,可以將函數賦值給變量,存儲在對象的屬性或數組的元素中,作爲參數傳人另外一個函數等。

自定義函數屬性

  JavaScript中的函數並不是原始值,而是一種特殊的對象,也就是說,函數可以擁有屬性。當函數需要一個“靜態”變量來在調用時保持某個值不變,最方便的方式就是給函數定義屬性,而不是定義全局變量,顯然定義全局變量會讓命名空間變得更加雜亂無章。

//計算階乘,並將結果緩存至函數的屬性中
function factorial(n){
    if(isFinite(n)8& n>o 8& n==Math.round(n)){//有限的正整數
        if(!(n in factorial))//如果沒有緩存結果
            factorial[n]=n* factorial(n-1);//計算結果並緩存之
            return factorial[n];//返回緩存結果
        }
        else return NaN;//如果輸入有誤
}
factorial[1]=1;//初始化緩存以保存這種基本情況

作爲命名空間的函數(掌握)

  比如,假設你寫了一段JavaScript模塊代碼,假定這段代碼定義了一個用以存儲中間計算結果的變量。這樣問題就來了,當模塊代碼放到不同的程序中運行時,你無法得知這個變量是否已經創建了,如果已經存在這個變量,那麼將會和代碼發生衝突。解決辦法當然是將代碼放入一個函數內,然後調用這個函數。這樣全局變量就變成了函數內的局部變量。

(function(){//mymodule()匿名的函數表達式
//模塊代碼
    }());//結束函數定義並立即調用它

閉包(掌握)

  和其他大多數現代編程語言一樣,JavaScript也採用詞法作用域(lexical scoping),也就是說,函數的執行依賴於變量作用域,這個作用域是在函數定義時決定的,而不是函數調用時決定的。
  爲了實現這種詞法作用域,JavaScript函數對象的內部狀態不僅包含函數的代碼邏輯,還必須引用當前的作用域鏈。函數對象可以通過作用域鏈相互關聯起來,函數體內部的變量都可以保存在函數作用域內,這種特性在計算機科學文獻中稱爲“閉包”。

var scope="global scope";//全局變量
function checkscope(){
    var scope="local scope";//局部變量
    function f(){ return scope;}//在作用域中返回這個值
    return f();
}
checkscope()//=>"local scope"

回想一下詞法作用域的基本規則:JavaScript函數的執行用到了作用域鏈,這個作用域鏈是函數定義的時候創建的。嵌套的函數f()定義在這個作用域鏈裏,其中的變量scope一定是局部變量,不管在何時何地執行函數f(),這種綁定在執行f()時依然有效。因此最後一行代碼返回“local scope”,而不是“global scope”。

實現閉包

  我們將作用域鏈描述爲一個對象列表,不是綁定的棧。每次調用JavaScript函數的時候,都會爲之創建一個新的對象用來保存局部變量,把這個對象添加至作用域鏈中。
  當函數返回的時候,就從作用域鏈中將這個綁定變量的對象刪除。如果不存在嵌套的函數,也沒有其他引用指向這個綁定對象,它就會被當做垃圾回收掉。
  如果定義了嵌套的函數,每個嵌套的函數都各自對應一個作用域鏈,並且這個作用域鏈指向一個變量綁定對象。但如果這些嵌套的函數對象在外部函數中保存下來,那麼它們也會和所指向的變量綁定對象一樣當做垃圾回收。
  但是如果這個函數定義了嵌套的函數,並將它作爲返回值返回或者存儲在某處的屬性裏,這時就會有一個外部引用指向這個嵌套的函數。它就不會被當做垃圾回收,並且它所指向的變量綁定對象也不會被當做垃圾回收。

var uniqueInteger=(function(){//定義函數並立即調用
    var counter=0;//函數的私有狀態
    return function(){ return counter++;};
}());
function counter(){
    var n=0;
    return{
        count:function(){return n++;},
        reset:function(){n=o;}
    };
}
var c=counter(),d=counter();//創建兩個計數器
d.count()//l=>0:它們互不干擾
c.reset()//reset()和count()方法共享狀態
C.count()//=>0:因爲我們重置了c
d.count()//=>1:而沒有重置d

  每次調用counter()都會創建一個新的作用域鏈和一個新的私有變量。因此,如果調用counter()兩次,則會得到兩個計數器對象,而且彼此包含不同的私有變量,調用其中一個計數器對象的count()或reset()不會影響到另外一個對象。

function counter(n){//函數參數n是一個私有變量
    return{
        //屬性getter方法返回並給私有計數器var遞增1
        get count(){return n++;},
        //屬性setter不允許n遞減
        set count(m){
        if(m>=n)n=m;
        else throw Error("count can only be set to a larger value");
        }
    };
}
var c=counter(1000);
c.count//=>1000
c.count//=>1001
c.count=2000
c.count//=>2000
c.count=2000//=>Error!

  需要注意的是,這個版本的counter()函數並未聲明局部變量,而只是使用參數n來保存私有狀態,屬性存取器方法可以訪問n。這樣的話,調用counter()的函數就可以指定私有變量的初始值了。

//這個函數返回一個總是返回v的函數
function constfunc(v){return function(){return v;};}
//創建一個數組用來存儲常數函數
var funcs=[];
for(var i=o;i<10;i++)funcs[i]=constfunc(i);
//在第5個位置的元素所表示的函數返回值爲5
funcs[5]()//=>5

  這段代碼利用循環創建了很多個閉包,當寫類似這種代碼的時候往往會犯一個錯誤:那就是試圖將循環代碼移入定義這個閉包的函數之內,看一下這段代碼:

//返回一個函數組成的數組,它們的返回值是0~9
function constfuncs(){
    var funcs=[];
    for(vari=0;i<10;i++)
        funcs[i]=function(){ return i;};
    return funcs;
}
var funcs=constfuncs();
funcs[5]()//返回值是10

  書寫閉包的時候還需注意一件事情,this是JavaScript的關鍵字,而不是變量。正如之前討論的,每個函數調用都包含一個this值,如果閉包在外部函數裏是無法訪問this的,除非外部函數將this轉存爲一個變量:

var self=this;//將this保存至一個變量中,以便嵌套的函數能夠訪問它

函數屬性、方法和構造函數(掌握)

length屬性

  在函數體裏,arguments.length表示傳入函數的實參的個數。而函數本身的1ength屬性則有着不同含義。函數的1ength屬性是隻讀屬性,它代表函數實參的數量,這裏的參數指的是“形參”而非“實參”,也就是在函數定義時給出的實參個數,通常也是在函數調用時期望傳入函數的實參個數。

prototype屬性

  每一個函數都包含一個prototype屬性,這個屬性是指向一個對象的引用,這個對象稱做“原型對象”(prototype object)。每一個函數都包含不同的原型對象。當將函數用做構造函數的時候,新創建的對象會從原型對象上繼承屬性。

call()方法和apply()方法

function classof(o){
    if(o===null) return "Null";
    if(o =a= undefined) return "Undefined";
    return Object. prototype. tostring. call(o). slice(8,-1);
}

  在ECMAScript5的嚴格模式中,ca11()和apply()的第一個實參都會變爲this的值,哪怕傳入的實參是原始值甚至是null或undefined。在ECMAScript3和非嚴格模式中,傳入的null和undefined都會被全局對象代替,而其他原始值則會被相應的包裝對象(wrapper object)所替代。apply()方法和cal1()類似,但傳入實參的形式和cal1()有所不同,它的實參都放入一個數組當中:需要注意的是,傳入apply()的參數數組可以是類數組對象也可以是真實數組。

f.apply(o,[1,2]);

bind()方法

  bind()是在ECMAScript5中新增的方法,但在ECMAScript3中可以輕易模擬bind()。從名字就可以看出,這個方法的主要作用就是將函數綁定至某個對象。當在函數f()上調用bind()方法並傳入一個對象0作爲參數,這個方法將返回一個新的函數。(以函數調用的方式)調用新的函數將會把原始的函數f()當做。的方法來調用。傳入新函數的任何實參都將傳入原始函數,比如:

function f(y){ return this.x+y;}//這個是待綁定的函數
var o={x:1};/將要綁定的對象
var g=f.bind(o);//通過調用g(x)來調用o.f(x)
g(2)//=>3

  ECMAScript 5中的bind()方法不僅僅是將函數綁定至一個對象,它還附帶一些其他應用:除了第一個實參之外,傳入bind()的實參也會綁定至this,這個附帶的應用是一種常見的函數式編程技術,有時也被稱爲“柯里化”(currying)。

var sum=function(x,y){ return x+y};//返回兩個實參的和值
//創建一個類似sum的新函數,但this的值綁定到nu11
//並且第一個參數綁定到1,這個新的函數期望只傳入一個實參
var succ=sum.bind(null,1);
succ(2)//=>3:x綁定到1,並傳入2作爲實參y
function f(y,z){ return this.x+y+z};//另外一個做累加計算的函數
var g=f.bind({x:1},2);//綁定this和y
g(3)//=>6:this.x綁定到1,y綁定到2,z綁定到3

  ECMAScript3版本的Function.bind()方法

if(!Function.prototype.bind){
Function.prototype.bind=function(o/*,args*/){
    //將this和arguments的值保存至變量中
    //以便在後面嵌套的函數中可以使用它們
    var self=this,boundArgs=arguments;
    //bind()方法的返回值是一個函數
    return function(){
        //創建一個實參列表,將傳入bind()的第二個及後續的實參都傳入這個函數
        var args=[],i;
        for(i=1;i< boundArgs.length;i++)args.push(boundArgs[i]);
        for(i=o;i<arguments.length;i++)args.push(arguments[i]);
        //現在將self作爲o的方法來調用,傳入這些實參
        return self.apply(o,args);
        };
    };
};

  ECMAScript5定義的bind()方法也有一些特性是上述ECMAScript3代碼無法模擬的。首先,真正的bind()方法返回一個函數對象,這個函數對象的length屬性是綁定函數的形參個數減去綁定實參的個數(1ength的值不能小於零)。
  再者,ECMAScript5的bind()方法可以順帶用做構造函數。如果bind()返回的函數用做構造函數,將忽略傳入bind()的this,原始函數就會以構造函數的形式調用,其實參也已經綁定。
  由bind()方法所返回的函數並不包含prototype屬性(普通函數固有的prot otype屬性是不能刪除的),並且將這些綁定的函數用做構造函數時所創建的對象從原始的未綁定的構造函數中繼承prototype。同樣,在使用instanceof運算符時,綁定構造函數和未綁定構造函數並無兩樣。

toString()方法

  和所有的JavaScript對象一樣,函數也有toString()方法,ECMAScript規範規定這個方法返回一個字符串,這個字符串和函數聲明語句的語法相關。實際上,大多數(非全部)的toString()方法的實現都返回函數的完整源碼。內置函數往往返回一個類似
“[native code]”的字符串作爲函數體。

Function()構造函數

  不管是通過函數定義語句還是函數直接量表達式,函數的定義都要使用function關字。但函數還可以通過Function()構造函數來定義,比如:

var f=new Function("x","y","return x*y;");

  Function()構造函數可以傳入任意數量的字符串實參,最後一個實參所表示的文本就是函數體;它可以包含任意的JavaScript語句,每兩條語句之間用分號分隔。傳入構造函數的其他所有的實參字符串是指定函數的形參名字的字符串。如果定義的函數不包含任何參數,只須給構造函數簡單地傳入一個字符串——函數體即可。
  關於Function()構造函數有幾點需要特別注意:

  • Function()構造函數允許JavaScript在運行時動態地創建並編譯函數。
  • 每次調用Function()構造函數都會解析函數體,並創建新的函數對象。如果是在一個循環或者多次調用的函數中執行這個構造函數,執行效率會受影響。相比之下,循環中的嵌套函數和函數定義表達式則不會每次執行時都重新編譯。
  • 最後一點,也是關於Function()構造函數非常重要的一點,就是它所創建的函數並不是使用詞法作用域,相反,函數體代碼的編譯總是會在頂層函數執行,正如下面代碼所示:
var scope="global";function constructFunction(){
    var scope="local";
    return new Function("return scope");//無法捕獲局部作用域
    }
//這一行代碼返回global,因爲通過Function()構造函數
//所返回的函數使用的不是局部作用域
constructFunction()();//=>"global"

  Function()構造函數在實際編程過程中很少會用到。

函數式編程(掌握)

使用函數處理數組

  自定義map()和reduce()函數

//對於每個數組元素調用函數f(),並返回一個結果數組
//如果Array.prototype.map定義了的話,就使用這個方法
var map=Array.prototype.map
?function(a,f){ return a.map(f);}//如果已經存在map()方法,就直接使用它
:function(a,f){//否則,自己實現一個
var results=[];
for(var i=0,len=a.length;i< len;i++){
    if(i in a)results[i]=f.call(null,a[i],i,a);
return results;
};
//使用函數f()和可選的初始值將數組a減至一個值
//如果Array.prototype.reduce存在的話,就使用這個方法
var reduce=Array.prototype.reduce
    ?function(a,f,initial){//如果reduce()方法存在的話
    if(arguments.1ength>2)
        return a.reduce(f,initial);//如果傳人了一個初始值
        else return a.reduce(f);//否則沒有初始值
    }
    :function(a,f,initial){//這個算法來自ES5規範
        var i=0,len=a.length,accumulator;
    //以特定的初始值開始,否則第一個值取自a
    if(arguments.1ength>2)accumulator=initial;
    else{//找到數組中第一個已定義的索引
        if(len==o)throw TypeError();
        while(i<len){
            if(i in a){
                accumulator =a[i++];
                break;
            }
            else i++;
        }
        if(i==1en)throw TypeError();
    }
    //對於數組中剩下的元素依次調用f()
    while(i<1en){
        if(i in a)
            accumulator=f.call(undefined,accumulator,a[i],i,a);
        i++;
    }
    return accumulator;
    };

高階函數

  所謂高階函數(higher-order function)就是操作函數的函數,它接收一個或多個函數作爲參數,並返回一個新函數,這裏是一個更常見的例子,它接收兩個函數f()和g(),並返回一個新的函數用以計算f(g())::

//返回一個新的可以計算f(g(...))的函數
//返回的函數h()將它所有的實參傳入g(),然後將g()的返回值傳入f()
//調用f()和g()時的this值和調用h()時的this值是同一個this
function compose(f,g){
    return function(){
        //需要給f()傳人一個參數,所以使用f()的cal1()方法
        //需要給g()傳入很多參數,所以使用g()的apply()方法
        return f.call(this,g.apply(this,arguments));
    };
}
var square=function(x){ return x*x;};
var sum=function(x,y){ return x+y;};
var squareofsum=compose(square,sum);
squareofsum(2,3)//=>25
//這個高階函數返回一個新的函數,這個新函數將它的實參傳入f()
//並返回f的返回值的邏輯非
function not(f){
    return function(){/返回一個新的函數
        var result=f.apply(this,arguments);//調用f()
        return !result;//對結果求反
    };
var even=function(x){//判斷a是否爲偶數的函數
    return x%2===0;
};
var odd =not(even);//一個新函數,所做的事情和even()相反
[1,1,3,5,5].every(odd);//=>true:每個元素都是奇數

不完全函數

  作者在本節討論的是一種函數變換技巧,即把一次完整的函數調用拆成多次函數調用,每次傳入的實參都是完整實參的一部分,每個拆分開的函數叫做不完全函數(partial function),每次函數調用叫做不完全調用(partial application),這種函數變換的特點是每次調用都返回一個函數,直到得到最終運行結果爲止,舉一個簡單的例子,將對函數f(1,2,3,4,5,6)的調用修改爲等價的f(1,2)(3,4)(5,6),後者包含三次調用,和每次調用相關的函數就是“不完全函數”。

記憶

  定義了一個階乘函數,它可以將上次的計算結果緩存起來。在函數式編程當中,這種緩存技巧叫做“記憶”(memorization)。下面的代碼展示了一個高階函數,memorize()接收一個函數作爲實參,並返回帶有記憶能力的函數。
  需要注意的是,記憶只是一種編程技巧,本質上是犧牲算法的空間複雜度以換取更優的時間複雜度,在客戶端JavaScript中代碼的執行時間複雜度往往成爲瓶頸,因此在大多數場景下,這種犧牲空間換取時間的做法以提升程序執行效率的做法是非常可取的。

//返回f()的帶有記憶功能的版本
//只有當f()的實參的字符串表示都不相同時它纔會工作
function memorize(f){
    var cache={};//將值保存在閉包內
    return function(){
    //將實參轉換爲字符串形式,並將其用做緩存的鍵
        var key=arguments.length +Array.prototype.join.call(arguments,",");
        if(key in cache) return cache[key];
        else return cache[key]=f.apply(this,arguments);
        };
    }

  memorize()函數創建一個新的對象,這個對象被當做緩存(的宿主)並賦值給一個局部變量,因此對於返回的函數來說它是私有的(在閉包中)。所返回的函數將它的實參數組轉換成字符串,並將字符串用做緩存對象的屬性名。如果在緩存中存在這個值,則直接返回它。
  否則,就調用既定的函數對實參進行計算,將計算結果緩存起來並返回,下面的代碼展示瞭如何使用memorize():

//返回兩個整數的最大公約數
//使用歐幾里德算法:http://en.wikipedia.org/wiki/Euclidean_algorithm
function gcd(a,b){//這裏省略對ab的類型檢查
    var t;//臨時變量用來存儲交換數值
    if(a<b)t=b,b=a,a=t;//確保a>=b
    while(bl=0)t=b,b=ab,a=t;//這是求最大公約數的歐幾里德算法
    return a;
    }
var gcdmemo=memorize(gcd);
gcdmemo(85,187)//=>17
//注意,當我們寫一個遞歸函數時,往往需要實現記憶功能
//我們更希望調用實現了記憶功能的遞歸函數,而不是原遞歸函數
var factorial=memorize(function(n){
    return(n<=1)?1:n*factorial(n-1);
});
factorial(5)//=>120.對於4~1的值也有緩存
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章