2013-09-30 24 views
6

在Stoyan Stefanov的「JavaScript Patterns」一書中,有一部分關於自定義函數。使用函數聲明時的自定義函數(也稱爲惰性函數定義)

var scareMe = function(){ 
    console.log("Boo!"); 
    scareMe = function(){ 
     console.log("Double Boo!"); 
    } 
} 

scareMe();//==>Boo! 
scareMe();//==>Double Boo! 

它按我的預期工作。但我修改scareMe功能如下:

function scareMe(){ 
    console.log("Boo!"); 
    function scareMe(){ 
     console.log("Double Boo!"); 
    } 
} 

scareMe();//==>Boo! 
scareMe();//==>Boo! 

問題

  1. 什麼是它們之間的區別?
  2. 在第二種情況下,爲什麼輸出不是「Double Boo!」,而是「Boo!」
+0

這種模式是驚人的。除了混淆之外,有沒有什麼實際用途? – GameAlchemist

+1

@GameAlchemist:是的。有時候我有一個功能去做一些我只想做一次的事情;初始化,說。我可以這樣定義該函數:'function init(){init = function(){};/* ... * /};'現在只要按名稱使用'init'而不是實際捕獲對原始函數的引用,它只會初始化一次。這可以通過一個布爾變量來完成,但這樣你就可以減少一個跟蹤變量。 – icktoofay

+0

有趣。因此,例如通過setInterval替換rAF可能會變成:requestAnimationFrame = function(cb){requestAnimationFrame = function(){}; return setInterval(cb,16); }。有點神祕,但優雅。 – GameAlchemist

回答

9

當覆蓋其自身的行爲創造它裏面的另一個功能scareMe,其覆蓋一個在上部範圍內調用第一scareMe功能,所以原來scareMe變化的定義,我已經看到這種做法被使用,如果你想要在應用程序中第一次設置,並想在設置完成後立即改變其行爲。

如果您已經定義:

var scareMe = function(){ 
    console.log("Boo!"); 
    var scareMe = function(){ //define it with var 
     console.log("Double boo!"); 
    } 
} 

scareMe();//==>Boo! 
scareMe();//==>Boo! //you will see the behavior as that of the second one. 

而且一次性設置的一種實際應用:

var scareMe = function(){ 
    console.log("Boo!"); 

    //I have done my job now. I am no longer needed. 
    scareMe = undefined; 

} 

scareMe();//==>Boo! 
scareMe();//==> oops error 

第二種情況,你要創建一個新的功能,名爲scareMe其範圍只內該函數不覆蓋自身。

試試這個,例如:

function scareMe(){ 
    console.log("Boo!"); 
    function scareMe(){ 
     console.log("Double bool!"); 
    } 
    scareMe(); //Now this invokes the once defined inside the scope of this function itself. 
} 

scareMe();//==>Boo! and Double bool! 
+0

我不認爲你所說的吊裝實際上是吊裝的。我認爲你的意思是說他們正在修改一個封閉的變量。 – icktoofay

+0

@ user2782160:你的回答實際上很不相同。我解釋你說你不能覆蓋一個錯誤的函數。你當然可以;只是在第二個片段中,它們不會覆蓋函數;他們正在遮蔽它。 – icktoofay

+0

@icktoofay實際上我的意思並不是真正的'scareMe'函數在全局範圍內,並且你在第一個例子中定義了另一個沒有'var'的函數,它被提升到全局scrope,這意味着它只是覆蓋了第一個scareMe的定義。看看它如何隨着我的答案中的第一個例子而改變。 – PSL

2

除了吊裝和調試性,可以考慮:

function f(/* ... */) { /* ... */ } 

等同於:如果我們翻譯

var f = function(/* ... */) { /* ... */ }; 

您第二個代碼示例使用第二種形式,我們得到:

var scareMe = function() { 
    console.log("Boo!"); 
    var scareMe = function() { 
     console.log("Double bool!"); 
    }; 
}; 

請注意,這是而不是與您的第一個片段相同;內部函數定義上有一個var。在內部var中,它會創建一個新的變量,該變量稱爲scareMe,該變量隱藏外部變量。

+1

在第二個例子中,我不認爲內部恐懼影子外部的一個,如果是這樣,輸出將是「雙噓!」,實際上,輸出是「噓!」。我認爲泰迪是對的。 – jason

+0

@jason:Teddy *是*對,但是當我說影子時,我的意思是說你有兩個同名的變量,而最內層的變量隱藏了最外層的變量;我不是故意說外面的一個完全改變了,只是它隱藏了。 – icktoofay

+0

是的,我明白你以後的意思,對不起:) – jason

4

在你第一次接觸時,scareMe是一個全局變量(在你的上下文中)。當在「雙重噓聲」中,你改變該全局變量的值,所以它的工作原理。 在第二種方法中,內部scareMe是一個局部變量,它不會改變全局值。 所以這是關於變量範圍。