首頁  >  文章  >  web前端  >  從function的定義看JavaScript的預先載入

從function的定義看JavaScript的預先載入

高洛峰
高洛峰原創
2016-11-28 15:26:151191瀏覽

在JavaScript中定義一個函數,有兩種寫法:

function ftn(){} // 第一种
var ftn = function(){} // 第二种

有人說,這兩種寫法是完全等價的。但在解析前,前一種寫法會被解析器自動提升到程式碼的頭部,因此違反了函數先定義後使用的原則,所以建議定義函數時候,全部採用後一種寫法。

看完這句話,我第一個感覺是兩個在使用時候是完全一致的,只是解析上有所差異。但是他的解釋「前一種寫法會被解析器自動提升到程式碼的頭部」讓我很困惑。

如是我有了下面第一個測試:

<script type="text/javascript">
function ftn()
{
    alert(&#39;old&#39;);
}
 
var b = ftn;
 
function ftn()
{
    b();
    alert(&#39;new&#39;);
}
 
ftn();//浏览器报内存溢出
</script>

接下來我做了第二個測試:

<script type="text/javascript">
var ftn = function()
{
    alert(&#39;old&#39;);
}
 
var b = ftn;
 
var ftn = function()
{
    b();
    alert(&#39;new&#39;);
}
 
ftn();//old,new依次弹出
</script>

網上的對這個解釋是:第一種方式,剛開始其實沒有重新定義ftn這個Function而在裡面執行了其本身。第二種方式,ftn=function()這裡沒有執行到Function裡面的程式碼ftn已經被重新定義了,所以這裡的重定義是有效的。

如果不怎麼清楚,那麼我再做了一個下面的測試:

<script type="text/javascript">
function ftn()
{
    alert(&#39;old&#39;);
}
 
var b = ftn;
 
function ftn()
{
    b();
    alert(&#39;new&#39;);
}
 
alert(b);//结果是重新定义的ftn内容
</script>

測試結果發現,重新定義ftn後,b的內容也會隨著改變。下面我又做了兩外一個測試:

<script type="text/javascript">
var ftn = function()
{
    alert(&#39;old&#39;);
}
 
var b = ftn;
 
var ftn = function()
{
    b();
    alert(&#39;new&#39;);
}
 
alert(b);//结果是老的ftn内容
</script>

這樣就很有意思了,在JavaScript裡面除了基本資料類型,其他類型都是對象,對像是存在堆裡面,它的別名是存在棧裡面的地址,後一種測試很明顯可以用這樣的原理來理解。那麼前面的測試為什麼b會隨著ftn的重新定義而改變了呢?

我有一種新解釋,不知道對不對,在所有的講JavaScript書裡都會提到,JavaScript裡面是沒有方法重載的,後面定義的重名function會覆蓋前面的function,var b = ftn;這句話是把b和ftn的引用指向同一個堆裡面的內存,而重新定義function ftn(){}後,新的function對象覆蓋了老的對象,而b和ftn引用的堆地址空間沒變,如果真是這樣,那麼這種寫法就合理了:

<script type="text/javascript">
function ftn()
{
    alert(&#39;old&#39;);
}
 
var b = ftn;
 
var ftn = function()
{
    b();
    alert(&#39;new&#39;);
}
 
alert(b);//老的ftn
alert(ftn);//新的ftn
ftn();//old ,new
</script>

這樣新的ftn在棧裡面的地址改變了,指向了新的function對象的定義,而原來的function沒有被覆蓋,還保存,所以b還是老的ftn引用的地址。

剛剛寫了一篇對JavaScript裡面function理解的文章,回頭再思考下我這邊文章的內容,覺得自己通過測試的結果的理解還是有點問題,其實理解還是要從編譯,運行的原理進行思考。 JavaScript都是在執行程式碼時候才編譯程式碼,因此我們var定義的型別可以不定,我們封裝的物件還會再加入屬性和方法,因此可以這麼理解我標題所帶來的問題,javascript一般的語言,例如定義一個變數var obj = new Object()只是做了一個很初的處理,在JavaScript裡面叫做預編譯,這種預編譯的能力很弱,弱到你可以隨便更改,而不會影響程式的運行,當物件被呼叫時候,JavaScript解釋器才會進行編譯,然後執行程式碼,這和java很不一樣,java是先把程式碼編譯好,呼叫他的時候才運行,這就是腳本語言的特點,所以腳本語言大多不快。但是當你這麼定義函數:fonction ftn(){},這種就把程式碼進行了編譯,也就是執行過了,這種寫法和java函數的定義很相似了。這是我的新解,我覺得這個解釋比較合理。

JavaScript的「編譯」只是檢查有沒有程式碼錯誤,不會執行程式碼,你可以試試在function裡面隨便寫東西測試一下。預先加載,先是function,再是var。上面的程式碼中,只有demo1和demo3受到影響,demo1和demo3的原始碼順序function - var - function,應用預載後的順序:function - function - var,預先載入後的完整程式碼:

<script type="text/javascript">
//demo1
function ftn()
{
    alert(&#39;old&#39;);
}
function ftn()
{
    b();
    alert(&#39;new&#39;);
}
var b = ftn;
 
ftn();//浏览器报内存溢出
</script>
<script type="text/javascript">
//demo3
function ftn()
{
  alert(&#39;old&#39;);
}
function ftn()
{
  b();
  alert(&#39;new&#39;);
}
var b = ftn;
 
alert(b);//结果是重新定义的ftn内容
</script>

預先載入大概就這樣了吧。

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn