一道JS閉包面試題


說明

最近看到這樣一段代碼

	 function fun(n,o){
	    console.log(o);
	    return {
	        fun:function(m){
	            return fun(m,n);
	        }
	    };
	 }

	 var a = fun(0);a.fun(1);a.fun(2);a.fun(3);
	 var b = fun(0).fun(1).fun(2).fun(3);
	 var c = fun(0).fun(1);c.fun(2);c.fun(3);
	 //問:三行a,b,c的輸出分別是什么?

覺得有點意思,和大家一起來聊聊。

我相信如果你不是非常理解JavaScript中的閉包,一定是不想看這段代碼的。

解釋

好的,我們暫時先不去想這段代碼,先看點簡單的

	function fun0(){
	    var a=1;
	    console.log(a);
	}
	function fun1(){
	    console.log(a);
	}
	fun0();    //1
	fun1();    //報錯 a is not defined

這段代碼,我相信大家應該知道最后為什么結果會是 1 和 報錯 的,在函數內聲明的變量只在函數體內定義,它們是局部變量,作用域是局部的,所以 函數 fun1 調用后,找不到a,就報錯了,JavaScript采用詞法作用域,函數的執行依賴於變量作用域,這個作用域是在函數定義時決定的,所以我們只要改改上面函數 fun1的位置,它就不會報錯了。

	function fun0(){
	    var a=1;
	    console.log(a);

	    //把fun1放在fun0中,就不報錯了
	    function fun1(){
	        console.log(a);
	    }
	    fun1();   //1
	}
	fun0();   //1

代碼改成這樣,只是把fun1 放在 fun0 中就不報錯了,函數調用后都輸出1
好了,我們來看最開始提到的代碼,先簡化一下

	function fun(n,o){
	    return {
	    }
	}

我們先看這段代碼,fun 調用后會怎么樣?
很明顯會返回一個空對象,記住,fun調用后會返回對象,這點很重要。

	function fun(n,o){
	    console.log(o);
	    return {
	        fun:function(m){
	            return fun(m,n);
	        }
	    };
	 }

	 var a = fun(0);

這里提一句,當調用函數的時候傳入的實參比函數聲明時指定的形參個數要少,剩下的形參都將設置為undefined值。
console.log(o); 輸出undefined
var a = fun(0); 那a是值是什么,是fun(0),返回的那個對象

	{
	    fun:function(m){
	        return fun(m,0);
	    }
	}

這個對象,有一個fun的方法,方法返回的結果就是最外面 fun 調用的結果。
結果

var a=fun(0),傳入一個參數0,那就是說,函數fun中參數 n 的值是0了,而返回的那個對象中,需要一個參數n,而這個對象的作用域中沒有n,它就繼續沿着作用域向上一級的作用域中尋找n,最后在函數fun中找到了n,n的值是0,這段話是本文的重點, 明白這段,那問題就容易解決了。


說到這里,這道題基本上可以解決了,希望大家能聽明白我上面說的話,下面的就簡單了。我們一步一步看。


現在我們知道 a 是

{
    fun:function(m){
        return fun(m,0);
    }
}

這樣的一個對象
a.fun(1); 會怎么樣?看代碼

{
    fun:function(1){
        return fun(1,0);
    }
}

a.fun(1); 返回的結果,就是 fun(1,0),返回的結果

 function fun(n,o){ //n的值為1,o的值為0
        console.log(o);
        return {
            fun:function(m){
                return fun(m,n);//n的值為1
            }
        };
}
fun(1,0);  //輸出0,並返回一個對象,這個對象有一個fun的方法,這個方法調用后,會返回外層fun函數調用的結果,並且外層函數的第二個參數是 n 的值,也就是1  

a.fun(2); 會怎么樣?看代碼

{
    fun:function(2){
        return fun(2,0);
    }
}

a.fun(2); 返回的結果,就是 fun(2,0),返回的結果

function fun(n,o){ //n的值為2,o的值為0
        console.log(o);
        return {
            fun:function(m){
                return fun(m,n); //n的值為2
            }
        };
}
fun(2,0);  //輸出0,並返回一個對象,這個對象有一個fun的方法,這個方法調用后,會返回外層fun函數調用的結果,並且外層函數的第二個參數是 n 的值,也就是2  

a.fun(3); 就不說了,一樣的。

var a = fun(0); a.fun(1); a.fun(2); a.fun(3);
var b = fun(0).fun(1).fun(2).fun(3);

把返回的對象,重新賦值給a,這樣兩行的結果就是一樣的了。
var c = fun(0).fun(1); c.fun(2); c.fun(3);
c 與他們的不同,只是var c = fun(0).fun(1); 之后用的是同一個對象罷了。

總結

說下結果

var a = fun(0); a.fun(1); a.fun(2); a.fun(3);
//undefined 0 0 0 
  
var b = fun(0).fun(1).fun(2).fun(3);
//undefined 0 1 2
  
var c = fun(0).fun(1); c.fun(2); c.fun(3);
//undefined 0 1 1

轉載來自http://www.cnblogs.com/xxcanghai/p/4991870.html
順便推薦幾篇講解閉包的文章


學習Javascript閉包(Closure)
Javascript閉包——懂不懂由你,反正我是懂了
JS閉包可被利用的常見場景


免責聲明!

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



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