[翻譯]函數:聲明和表達式


  昨天看了一篇文章《命名函數表達式探秘》,但我對函數表達式和聲明之間的區別還是不太清楚,所以查了些資料,翻譯了這篇文章

  文章很基礎,但是講的很清晰。

  以下譯文:

  

函數:聲明和表達式

函數,像變量一樣,可以在代碼的任意地方定義它。
JS 提供了幾個方法去定義它們。
     1.函數聲明     (Function Declaration)
     2.函數表達式  (Function Expression)
     3.通過調用new Function 返回。

語法
     創建一個函數的基本方法是通過一個函數聲明。語法如下:
     
function f(arg1, arg2, ...) {
   ... code ...
}
     
     實現起來像這樣子:
          
1 function sayHi(name) {
2   alert("Hi, "+name)
3 }
4  
5 sayHi('John')
     
     上面的例子聲明了一個帶有一個參數name,函數名為sayHi的函數,並且調用了它。

返回值

     我們用return語句來返回一個值。
     
1 function sum(a, b) {
2   return a+b
3 }
4  
5 var result = sum(2,5)
6 alert(result)

     如果函數不返回任何東西,它的結果 會是一個特殊的值,undefined。你可以從下面的代碼看到。

     
1 function getNothing() {
2   // no return
3 }
4  
5 var result = getNothing()
6  
7 alert(result)
     
     一個空的返回也是一樣:
     
1 function getNothing() {
2   return
3 }
4 alert( getNothing() === undefined ) // true

局部變量

     一個函數可以包括變量,通過var來定義。這些變量被稱為局部變量,而且只能在函數內部使用。
     
1 function sum(a, b) {
2   var sum = a + b
3  
4   return sum
5 }

函數聲明

     函數的聲明的解析是在預執行(pre-execution)階段,也就是瀏覽器准備執行代碼的時候。
     
     因此,通過函數聲明來定義函數,可以在定義前或后被調用。
     
     下面的代碼是可以跑的:

     
1 function sayHi(name) {
2   alert("Hi, "+name)
3 }
4  
5 sayHi("John")

     換個順序也可以:
     
     
1 sayHi("John")
2  
3 function sayHi(name) {
4   alert("Hi, "+name)
5 }

     一個函數可以在代碼任意位置聲明。

     


比如,我們可能要根據條件來聲明不同的函數:

1 sayHi()
2  
3 if (1) {
4   function sayHi() {  alert(1)  }
5 else {
6   function sayHi() {  alert(2)  } // <--
7 }
在寫這篇文章的時候(作者本人沒有給出他這這篇文章的時間——譯者注),嘗試在不同瀏覽器,火狐會拋出異常,其他兩個瀏覽器都會給出結果 2。

這是因為聲明是在代碼執行前解析的。根據規范(p.10.5),后面函數會覆蓋前面存在的同名函數。

到了執行時期,聲明會被忽略。所以if語句沒有起作用。

  函數表達式

     在JS里面,函數function和number、string一樣,是一階值(原文為:first-class value,有人也譯作:‘一等公民’)
     
     只要可以放置變量的地方,都可以放置函數。在那個‘地方’用函數表達式的語法來聲明:function (arguments) {}

     例如,你可以這樣使用變量:
     var f = 5
     也可給f賦值一個函數表達式:
     
1 var f = function(name) {
2     alert("Hi, " + name + "!");
3 }

     

'function' 關鍵字什么時候用作 表達式,什么時候又用作 聲明

規則很簡單

當js解析器看到function出現在主碼流( main code flow 不知如何翻譯),function被認為是聲明。

當 function 作為語句(statement)的一部分出現的,都會是表達式。

(譯者認為,可以看能不能加分號來判斷,能加分號的都為語句(statement)。)
     函數表達式會在執行流( execution flow)執行到它的時候才會創建函數。所以,函數表達式必須被執行了(此時函數才定義了)才能被調用。

     
01 var sayHi
02  
03 // sayHi() <-- can't call here, there is no sayHi yet
04  
05 if (1) {
06   sayHi = function() {  alert(1)  }
07 else {
08   sayHi = function() {  alert(2)  }
09 }
10  
11 sayHi()

     上面的例子,在所有瀏覽器執行的結果都一樣。

請用函數聲明

     有經驗的開發者,會將函數通過表達式來定義。
... code ...
var f = function() { ... }
...
     
     函數聲明會更短更有可讀性。
... code ...
function f() { ... }
...

     此外函數這樣定義可以在定義前調用。
     
     在你真正需要用函數表達式的使用它!特別是在有條件判斷的時候定義函數。

函數是一個值

     函數在JS里面是一個基本類型。我們甚至可以輸出它:
     
1 function f() { alert(1) }
2  
3 alert(f)
     
     上面的例子會輸出這個function,通常是輸出這個函數的源代碼

     論是函數聲明還是表達式,對函數來說,只是創建的時間不同而已。

傳遞一個函數
     
     想所有值一樣,不管是以哪種方式定義,函數可以被賦值,作為參數傳遞到另外的函數等等。
     
1 function sayHi(name) {
2   alert("Hi, "+name)
3 }
4  
5 var hi = sayHi // assign a function to another variable
6  
7 hi("dude")     // call the function

     函數的傳遞是引用類型。也就是說,函數會存在內存的某個位置,sayhi是它的一個引用(如果你懂指針的話)。當把sayhi賦給hi的時候,兩者都會引用到同一個函數。

     函數可以接受另一個函數作為參數。
     
1 function runWithOne(f) {  // runs given function with argument 1
2   f(1)
3 }
4  
5 runWithOne(
6   function(a){ alert(a) }
7 )

     邏輯上,函數是一個行為(action)。所以傳遞一個函數就是傳遞了一個可以被其他程序啟動的行為。這個特性在js中廣泛使用。

     在上面的例子中,我們創建了一個沒有名字的函數,而且沒有把它賦給其他變量。這種函數被稱為匿名函數。

就地運行

     我們可以使用函數表達式創建一個函數並馬上執行它。

     
1 (function() {
2  
3   var a, b    // local variables
4   
5   // ...      // and the code
6  
7 })()

     我們涉及到局部變量的時候常用這種技巧。我們不想讓局部變量污染到全局變量,所以我們把代碼包在一個函數里面。

     在代碼執行后,全局變量還是干凈的。這是一個最佳實踐。

     為什么函數被包在括號里面?這是因為js只能就地運行函數表達式。

     函數聲明不能像上面一樣執行。
          
1 function work() {
2   // ...
3 }()  // syntax error

     即使我們把名字去掉,JS還是會發現function這個關鍵字,把它解析成函數聲明。
     
1 function() { // error, function declaration without name.
2   // ...
3 }()

     所以,只有把函數包在括號面才可行。然后解析器會認為它是語句中的一部分,把它提升為函數表達式。

     如果函數明顯就是一個表達式,那么也不用包起來。
     
1 var result = function(a,b) { return a+b }(2,2)
2 alert(result) // 4

     上面的代碼,函數會被創建並且馬上執行。

     


下面代碼的結果是啥?為啥?

1 var a = 5
2  
3 (function() {
4   alert(a)
5 })()

P.S. 想清楚哦童鞋,有陷阱哦~Ok, 我已經提醒過你了。 Smile

Solution

答案是 error. Try it:

1 var a = 5
2  
3 (function() {
4   alert(a)
5 })()
因為var a = 5 后面沒有加分號,JS會這樣處理的。   
1 var a = 5(function() {
2   alert(a)
3 })()

所以,JS嘗試把5當做函數來調用,這會導致錯誤。真正能用的代碼如下。

1 var a = 5;
2  
3 (function() {
4   alert(a)
5 })()

這應該是JS沒有分號的最危險的陷阱了。

還有一種方法去定義還是就是直接調用Function構造器:

1 var sayHi = new Function('name'' alert("Hi, "+name) ');
2  
3 sayHi("Benedict");

它很少用,基本不用。

命名函數表達式

     一個函數表達式可以有名字:
     
var f = function sayHi(name) {
  alert("Hi, "+name)
}

     這個語法被稱作命名函數表達式(named function expression or NFE), 但是在IE<9下表現都不正常。
     在支持這個語法的瀏覽器下,這個函數的名字只可以在函數內部被訪問。
1 var f = function sayHi(name) {
2   alert(sayHi) // outputs function
3 }
4  
5 alert(sayHi) // error: undefined variable 'sayHi'

     IE在這種情況下卻創造了兩個函數對象:sayHi 和 f
1 var f = function g() {  }
2  
3 // false in IE, error (g is undefined) in other browsers
4 alert(f=== g)

     NFE 存在的目的是讓匿名函數可以遞歸。
     看下面的包含在setTimeout里面的factorial函數:

setTimeout(function factorial(n) {
  return n == 1 ? n : n*factorial(n-1)
}, 100)

下面代碼的結果是什么?為什么?

function g() { return 1 } )
 
alert(g)
Solution

答案是 error:

1 function g() { return 1 } )
2  
3 alert(g)

關鍵點是要理解(function ... )是一個函數表達式而不是函數定義.

所以這是一個命名函數表達式。

命名表達式的名字只能在函數內部訪問。

除了IE<9 ,其他瀏覽器都支持 NFEs, 所以它們會拋出 ‘undefined variable’ 異常, 因為函數名 g 只能在內部被訪問。

IE<9 不支持 NFE, 所以它們會輸出整個函數(源代碼).

 函數命名
     函數是行為(action),所以應該起一個動詞做名字像get,red,calculateSum這些。
     下面的情況是允許短函數名的:
     1.函數在內嵌函數里面暫時使用的,和變量的邏輯一樣。
     2.函數會在整個代碼里的多次被調用。一方面,你不會忘了它是干嘛用的,另一反面,你可以少些點代碼。
     現實世界中,像'$','$$','$A',這些,會經常在JS函數庫里被使用。

     其他情況下,還是應該用動詞,或者動詞做開頭的函數名字

總結

     函數在JS里面是基本類型。它們可以根據需要被賦值,傳遞和調用。
     一個不返回任何東西的函數其實返回了undefined
     用動詞做函數名字。

     

Function Declaration Function Expression
function 在主代碼流里面 function 作為函數表達式的一部分
函數在代碼執行前創建,可以在定義前被調用。 當代碼執行到的是創建,只能在定義后調用。
  可以就地調用

轉載:
http://www.cnblogs.com/Xdoable/archive/2011/09/08/2171512.html


免責聲明!

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



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