讀古今文學網 > 編寫高質量代碼:改善JavaScript程序的188個建議 > 建議63:比較函數調用和引用本質 >

建議63:比較函數調用和引用本質

在被調用之前,JavaScript函數僅是詞法意思上的結構,沒有實際的價值,在預編譯函數時,也僅是簡單地分析函數的詞法、語法結構,並根據函數標識符預定一個函數佔據的內存空間,其內部結構和邏輯並沒有被運行。但是,一旦函數被調用執行,其上下文環境也會隨之產生。可以說,上下文環境是函數運行期的一個動態環境,它是一個動態概念,與函數的靜態性是截然不同的概念。每個函數都有一個獨立的上下文環境(即執行環境)。先看下面這個示例中變量a和變量b是否相等。


function f{

var x=5;

return x;

}

var a=f;

var b=f;

alert(a===b);


「alert(a===b);」的返回值為true,說明變量a與變量b完全相同。繼續觀察下面的示例。


function f{

var x=5;

return function{

return x;

}

}

var a=f;

var b=f;

alert(a===b);


「alert(a===b);」的返回值為false,說明變量a與變量b不完全相同。這就是函數引用和函數調用的區別,下面的示意圖能夠很好地說明它們的異同,分別如圖3.1和圖3.2所示。

圖 3.1 函數引用示意圖 圖 3.2 函數調用示意圖

下面介紹函數引用與函數調用的本質區別。當引用函數時,多個變量內存儲的是函數的相同入口指針。因此,對於同一個函數來說,不管有多少個變量引用該函數,這些變量的值都是相同的,都為該函數的入口指針地址。例如,針對上面第二個示例來說,如果變量a和b都引用函數f,而不是調用,則它們是完全相同的。

相反,函數調用是執行該函數,並把返回的值傳遞給變量a和b。也就是說,變量a和b存儲的是值,而不是函數的入口指針地址。

有這麼一個問題:在第一個示例中,如果變量a和b也都調用函數f,它們卻相同(如下所示),而在下面這個示例中的調用卻不相同。


function f{

var x=5;

return x;

}

var a=f;

var b=f;

alert(a===b);//true


在第一個示例中,函數調用後返回的是值類型數據(即數值5),兩個數值5自然是相同的。但是在第二個示例中,函數調用返回值是一個閉包涵數,即引用類型的數據。雖然返回的閉包結構是完全相同的,但由於它們存儲在不同變量中,即它們的地址指針是完全不同的,因此也無法相同。再看下面這個示例:


function F{

this.x=5;

}

var a=new F;

var b=new F;

alert(a===b);


「alert(a===b);」的返回值為false,說明變量a與變量b不完全相同。


function F{

this.x=function{

return 5;

}

}

var a=new F;

var b=new F;

alert(a===b);


「alert(a===b);」的返回值為false,說明變量a與變量b不完全相同。

函數與實例的關係,如圖3.3所示。通過new運算符可以複製函數的結構,從而實現函數實例化的目的。實例化的過程實際就是對函數結構進行複製和初始化的操作過程。因此,當實例化函數F並賦值給變量a時,a所引用的函數結構並非原來函數的結構,而是內存運行區中另一塊函數結構,自然它們是完全不相同的。

圖 3.3 函數實例化過程示意圖