js中 var functionName = function() {} 和 function functionName() {} 兩種函數聲明的區別 (譯)


stackOverflow中看到了很久以前問的一個關於函數聲明的問題,問題對函數剖析的特別深。這里翻譯了一下組織成一篇小博文,加深一下對這兩種聲明方式的印象。雖是老調重彈,但是只要能幫助理解問題,不管多老,都是好的。

問:

 

js中有兩種聲明函數的方法,分別為:

var functionOne = function() {
    // Some code
};

function functionTwo() {
    // Some code
}

為什么會有兩種不同的方法?每個方法的優點和缺點分別是什么?有什么情況是一種方法能完成而另外一種方法不能完成的嗎?

答:

 

by @Greg

不同點在於functionOne只會在到達賦值的那一行才會被真正定義,而functionTwo會在 包含它的函數或script腳本 執行的時候馬上被定義。例如:

 1 <script>
 2   // Error undefined
 3   functionOne();
 4 
 5   var functionOne = function() {
 6   };
 7 
 8   // No error
 9   functionOne();
10 </script>
11 
12 <script>
13   // No error
14   functionTwo();
15 
16   function functionTwo() {
17   }
18 </script>

這也意味着你不能在條件語句中使用第二種方法定義函數:

<script>
  if (test) {
     // Error or misbehavior
     function functionThree() { doSomething(); }
  }
</script>

上面的例子無論test的值是真是假,實際上已經定義了functionThree函數。

 

by @Eugene Lazutkin

另外,也可以結合以上兩種函數定義方法:

var xyz = function abc(){};

xyz 函數將會正常定義。而 abc 函數不會在瀏覽器中定義(除了IE瀏覽器),但是在其函數體內可見:

var xyz = function abc(){
  // xyz is visible here
  // abc is visible here
}
// xyz is visible here
// abc is undefined here

如果你想在所有瀏覽器中使用函數別名,可以使用這種聲明方式:

function abc(){};
var xyz = abc;

這種情況下 xyz 和 abc 是同一個函數對象的別名:

console.log(xyz === abc); // prints "true"

使用第二種方式進行函數定義的一個說服性理由是,該函數對象有"name"屬性(IE不支持)。當你定義一個類似下面的函數:

function abc(){};
console.log(abc.name); // prints "abc"

函數的name屬性會被自動分配。但是當你以下面的方式定義函數:

var abc = function(){};
console.log(abc.name); // prints ""

它的name屬性為空——這里創建了一個匿名函數,並將它賦值給其他變量。

推薦使用第二種函數定義方法的另一個理由是,可以用一個簡短的內部名稱來引用函數本身,同時為外部用戶提供一個長的無沖突的別名:

// Assume really.long.external.scoped is {}
really.long.external.scoped.name = function shortcut(n){
  // Let it call itself recursively: 循環調用其本身
  shortcut(n - 1);
  // ...
  // Let is pass itself as a callback: 作為回調函數來傳參
  someFunction(shortcut);
  // ...
}

在上面的例子中我們可以用一個外部聲明來做和原函數相同的事,但是這樣做不方便(同時太慢)。

 

(引用函數本身的其他方法是使用arguments.callee,這種方法比較常見<不知道這么翻譯對不對>,並且在嚴格模式中不支持)。

實際上,javaScript對兩種函數聲明方法對待不同。這是一個函數聲明:

function abc(){}

這里的abc在目前的范圍中是處處都有定義的:

// We can call it here
abc(); // Works

// Yet, it is defined down there.
function abc(){}

// We can call it again
abc(); // Works

同樣,它也會透過一個return語句被提前定義:

// We can call it here
abc(); // Works
return;
function abc(){}

另外一個函數表達式:

var xyz = function(){};

這里的xyz在聲明行被定義:

// We can't call it here
xyz(); // UNDEFINED!!!

// Now it is defined
xyz = function(){}

// We can call it here
xyz(); // works

對函數(function)表達式和函數(Function)聲明,我更傾向 "函數表達式" 聲明(第一種方式),因為這種方式我可以控制可見性。當我定義這樣一個函數:

var abc = function(){};

我知道我定義了一個局部函數。當我定義一個這樣的函數:

abc = function(){};

我知道我定義了一個全局函數,並且我沒有在作用域鏈的任何位置定義abc<不太清楚如何翻譯>。然而像下面的定義方式:

function abc(){};

取決於上下文並且可能會讓你猜測該函數真實定義的地方,特別是在使用eval()方法的時候——答案是:依賴於其所在的瀏覽器。

 

問題地址:http://stackoverflow.com/questions/336859/var-functionname-function-vs-function-functionname

 

翻譯的不好,請勿拍!

 


免責聲明!

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



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