js 函數閉包內部返回函數體調用方法難點解答


今天在網上,看到一篇關於js函數難點的文章,js函數的一些難點。在那上面提了一下,關於js函數返回另一個函數的問題,並附上了一道面試題:

var add = function(x){  
    var sum = 1;  
    var tmp = function(x){  
        sum = sum + x;  
    return tmp;  
    }  
    tmp.toString = function(){  
        return sum;  
    }  
    return tmp;  
} // alert(add(1)(2)(3)) --> 6  

接下來,就來詳細的解讀返回另一個函數的問題。

 

之所以寫這篇文章是因為,在那里面有一點讓我感到奇怪,那就是最后的調用方式

add(1)(2)(3)  

由於在java中,我沒有見到過這樣的函數調用方式,所以引起了我的注意,我決定去研究研究;下面就將我的研究分享出來,當然如果你對此已經有了深刻的認識,你可以選擇跳過,或者對於不足的地方,給出指點微笑。好了閑話不多說,進入正題。

 

我們來看一個最簡單的例子:

function create1(pro) {  
    console.log("pro : " + pro);  
    return function(obj1, obj2){  
        console.log(obj1 + " -- " + obj2);  
        return obj1 + obj2;  
    }  
}  

我構建了一個簡單的函數create1,並且有一個返回值,返回值是一個內部函數。函數構建完了,接下來進行調用:

var c1 = create1("pro"); // 創建函數  

如果按照我之前的理解,當我調用了這個方法后,應該會打印出 pro : pro,接着然后報錯的。如果你看完過后,也跟我有一樣的想法,那恭喜你想多了或者有了固型思維微笑

。真實的是當我們通過上面的代碼調用的時候,日志是打印出了  pro : pro ,但是並沒有報錯,並且我們反復來回的調用過后,也只是來回的打印相同的日志。這也就說明這個時候,只是進入了create1()方法,並沒有進入到該函數的內部函數內。通過面試題的啟發,我在試着調用了一次,發現打印出了后續的。

c1(1, 2); // 調用函數  

 

這樣就打印出了下面的日志;這說明其實我們一開始調用方法的時候,其實是並沒有進入到里層的函數的只是進入了外層函數體,

我們只有再調用才能進入里層函數體,並且這個時候,我們重復上面的調用,他只會是調用里層的函數體,並沒有外面的函數體。

 

類似這種函數返回另一個函數的,我們第一次調用只是構建了一個外層函數體對象,只有有后續的調用,才能調用內層函數體,並且重復調用,只會重復內層函數體。
不要急,還沒有完,后面還有……

接下來,我們看一看另一種情況,我們先聲明一個函數,用來做加法運算:

function infun(obj1, obj2) {  
    console.log(obj1 + " -- " + obj2);  
        return obj1 + obj2;  
}  

 

然后再聲明一個函數,在該函數中調用上面聲明的函數:

function create2(pro) {  
    console.log("pro = " + pro);  
        return infun(obj1, obj2); // 這個時候,會報錯  
}  

 

最后是調用:

var c1 = create2("pro");   

 

查看日志:

pro = pro ‌Uncaught ReferenceError: obj1 is not defined 
會發現,打印出了一條日志后,接着拋出了異常。對方法做一下改動,

 
function create2(pro) {  
    console.log("pro = " + pro);  
    var obj1 = 1, obj2 = 2;  
    return infun(obj1, obj2); // 這個時候,在內部設了傳的變量,則不報錯了,事實是調用了外面已經聲明過的函數,ifun(obj1,obj2),而obj1與obj2兩個傳參變量由於是在調用已聲明過的ifun函數,所以不能當做聲明的傳參變量,
                  //只當傳參的字符串變量(准確點應該說,與obj1 obj2這兩變量名本身沒任何關系了,名字可以為任何變量名,前提是已經聲明過賦過值),而函數內部和全局都未定義這兩變量的值,所以拋出異常,is not defined 
}


在調用會發現正常運行,並且打印出了兩條日志記錄。

這說明,類似於這種,在一個函數內返回一個已經聲明的函數,其實是調用已經聲明的函數,跟上面的情況是不一樣的。
好了,現在回過頭來,仔細看看開頭的面試題,就會發現一切都明了了:

// 聲明一個函數表達式  
var add = function(x){  
    var sum = 1;  
    // 在函數表達式內部有一個求和的內部函數  
    var tmp = function(x){  
        sum = sum + x;// 求和  
        return tmp;  
    }  
    // 構建一個函數體的toString()函數  
    tmp.toString = function(){  
        return sum;  
    }  
    return tmp; // 返回的是一個函數體,如果該函數體有toString()方法,則會調用函數體的toString()方法  
}  


然后再來看看調用:

alert(add(1)(2)(3))  

重點:add(1)(2)(3) 
1、函數add(1)第一次調用,其實是只聲明了 var sum=1;這個變量,然后返回了tmp函數體,用於后面調用tmp函數
2、函數add(1)(2)第二次調用才真正的把參數傳進來使用了,即第一次傳的 1 是沒地方用的,沒意義第二次傳的 2 是給第一次返回的tmp函數體傳的參、即用在sum=sum+x上----sum=1+2
3、函數add(1)(2)(3)第三次調用和第二次一樣,由於tmp函數體內部 return tmp  返回了本身,所以后面可以繼續調用tmp函數,也就是除第一次調用傳參無效外,后面可以調用無數次,sum值會不斷累加
4、toString是tmp函數體附帶的屬性方法函數,會隨着主體函數toString執行一次調用一次

 

結果為6,至於原因就跟我們第一種討論的情況一樣,接下來,我們反復調用:

// 以下結果輸出為:6  
alert(add(10)(2)(3))  
alert(add(100)(2)(3))  
// 下面的結果輸出變了  
alert(add(1)(3)(3))  
alert(add(1)(2)(5)) 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM