2012-09-06 28 views
2

在另一個線程我開始了評論,有人這樣說:命名一個函數(而不是離開它)和創建對它的引用有什麼區別?

@adlwalrus肯定。試試這個:var foo = function bar(){};的console.log(FOO);但請注意,酒吧只是功能名稱(這是什麼意思,我不確定我是否確切地自己)而不是對它的引用,所以不能通過執行bar()來調用它。分配(甚至命名的函數)與聲明函數不同。提升(碰到範圍頂部)只適用於聲明,分配將保持原樣。 - valentinas 6小時前

如果不能用bar()調用它,函數名會起什麼作用?

+1

你的名字和你的地址有什麼區別? –

+0

除了名稱/地址類比之外,實際上可以在bar()的作用域內調用bar(),但在bar()的作用域外,必須使用foo()。 – jeremy

+0

@HotLicks - 這種比喻毫無意義。 'foo'和'bar'在這裏都是清晰的名字。他們都提到相同的*地址*。這只是在新功能的範圍內纔可用。 –

回答

3

有兩種方法可以在JavaScript中創建函數,即「函數聲明」和「函數表達式」。我相信這是Doug Crockford最好的解釋,他指出,除非「函數」是給定行上的第一組字符,否則您正在執行函數表達式(不是聲明)。

功能聲明是挑剔的生物。當你看到它們時你會認出它們。他們是這樣的:

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

他們總是一個名字(它的報應)和名字被局部範圍在其下函數聲明爲詞法環境。所以如果你在全局上下文中執行一個函數聲明,那麼這個函數可以通過它的全局名稱被引用。如果你在一個函數中執行它,那麼該函數的名字只能在該函數中被引用,並且在該函數中聲明的任何函數都可以被引用。

我認爲這種聲明函數的方法中最重要的方面就是函數初始化會被提升到當前詞法上下文的頂部。因此,你應該永遠,永遠使用函數聲明中的條件,比如這個:

//DON'T DO THIS! 
if (x) { 
    function foo() { return 1; } 
} else { 
    function foo() { return 2; } 
} 

foo(); //will always be 2, regardless of the value of x. 

函數表達式略有不同。通常情況下,他們直接分配給一個變量,像這樣:

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

這是幾乎相同的函數聲明,除了上述初始化不懸掛。所以你可以做到以下幾點:

var foo; 
if (x) { 
    foo = function() { return 1; }; 
} else { 
    foo = function() { return 2; }; 
} 

foo(); //will be 1 or 2, depending on the truthy-ness of x. 

那麼,回到原來的問題。函數表達式也可以有一個名稱,儘管它不是必需的,並且它沒有作用於聲明函數的上下文(與函數聲明一樣)。相反,它將範圍限定在函數自己的詞彙上下文中。這在某些情況下非常有用。我個人最喜歡的是這個模式:

(function foo() { 
    //Do something. 

    setTimeout(foo, 1000); 
}()); 

foo; //undefined 

因爲單詞「功能」前的括號,這是一個函數表達式和名稱僅供內部作用域。但沒關係,因爲我們只需要在內部調用它(通過setTimeout())。結果是該函數將立即執行一次,然後在執行完成後每秒鐘左右重新執行一次。這比使用setInterval()更安全,因爲它會在重新計劃自身之前一直等待執行完畢,從而防止可能導致錯誤執行和/或「支配」JavaScript線程的重疊。

從本質上講,命名函數表達式的使用是有限的,但是當你需要它時,它是非常強大的。

+0

這是一個令人難以置信的信息答案。它被接受了,我認爲有一種「禮物」聲望的方式,如果有的話我會這樣做,但我找不到它。我希望你能成爲我的JS老師。如果你寫了一本教科書,我會買它。你有博客嗎? – wwaawaw

+0

@adlwalrus我很欣賞讚美。我沒有博客或書籍或類似的東西。但我建議您查看Doug Crockford的JavaScript講座(搜索「Crockford on JavaScript」)。它們非常棒,是我對JavaScript所瞭解最多的源代碼。 :-) – Pete

+0

你讀過/你會推薦/ The Good Parts /?我的一個朋友擁有它,所以我可以借用它,如果我想。 – wwaawaw

4

用於調用自身的函數。

var x = function y(val){ 
      if (val){ 
       console.log(val); 
       y(val-1); 
      } 
     }; 
x(5); 
> 3 
> 2 
> 1 
y(3); 
> ReferenceError: y is not defined 
+0

好吧,那麼他們就不會與其他任何東西發生衝突,真的。 IE,你可以有: 'var x = function z(v){if(v){console.log(v); z(v-1);}}; var y = function z(v){if(v){console.log(v); z(v-1);}};' – wwaawaw

+0

注意兩個內部都標記爲'z'的函數。 – wwaawaw

+0

@adlwalrus是的,這是正確的見http://jsfiddle.net/mowglisanu/gAHXg/ – Musa

4

你指的是一個名爲函數表達式。規範要求這些函數的名稱僅在新函數的範圍內可用。 Spec quote

在FunctionExpression標識符可以從內部 的FunctionExpression的函數體中引用允許函數遞歸調用 本身。但是,與FunctionDeclaration不同,FunctionExpression中的 標識符不能被引用,並且 也不會影響包含FunctionExpression的範圍。

在另一方面,結果表達的是新的功能,它可以被保存和參考的任何位置的參考。

很多細節在這裏:

我讀了整個事情。

至於好處,另一個是它使它們更易於在調試器中識別。

+0

非常有趣,也很複雜。 – wwaawaw

相關問題