《JavaScript高級程序設計》讀書筆記


函數
函數對任何語言來說都是一個核心的概念。通過函數可以封裝任意多條語句,而且可以在任何地方、任何時候調用執行。ECMAScript 中的函數使用function 關鍵字來聲明,后跟一組參數以及函數體。
函數的基本語法如下所示:

function functionName(arg0, arg1,...,argN) {
  statements
}
以下是一個函數示例:
function sayHi(name, message) {
  alert("Hello " + name + "," + message);
}

這個函數可以通過其函數名來調用,后面還要加上一對圓括號和參數(圓括號中的參數如果有多個,可以用逗號隔開)。調用sayHi()函數的代碼如下所示:
sayHi("Nicholas", "how are you today?");

這個函數的輸出結果是"Hello Nicholas,how are you today?"。函數中定義中的命名參數name和message 被用作了字符串拼接的兩個操作數,而結果最終通過警告框顯示了出來。
ECMAScript 中的函數在定義時不必指定是否返回值。實際上,任何函數在任何時候都可以通過return 語句后跟要返回的值來實現返回值。請看下面的例子:
function sum(num1, num2) {
  return num1 + num2;
}

這個sum()函數的作用是把兩個值加起來返回一個結果。我們注意到,除了return 語句之外,沒有任何聲明表示該函數會返回一個值。調用這個函數的示例代碼如下:
var result = sum(5, 10);
這個函數會在執行完return 語句之后停止並立即退出。因此,位於return 語句之后的任何代碼都永遠不會執行。例如:
function sum(num1, num2) {
  return num1 + num2;
  alert("Hello world"); // 永遠不會執行
}
在這個例子中,由於調用alert()函數的語句位於return 語句之后,因此永遠不會顯示警告框。
當然,一個函數中也可以包含多個return 語句,如下面這個例子中所示:
function diff(num1, num2) {
  if (num1 < num2) {
    return num2 - num1;
  } else {
    return num1 - num2;
  }
}

這個例子中定義的diff()函數用於計算兩個數值的差。如果第一個數比第二個小,則用第二個數減第一個數;否則,用第一個數減第二個數。代碼中的兩個分支都具有自己的return 語句,分別用於執行正確的計算。
另外,return 語句也可以不帶有任何返回值。在這種情況下,函數在停止執行后將返回undefined值。這種用法一般用在需要提前停止函數執行而又不需要返回值的情況下。比如在下面這個例子中,就不會顯示警告框:
function sayHi(name, message) {
  return;
  alert("Hello " + name + "," + message); //永遠不會調用
}

注:推薦的做法是要么讓函數始終都返回一個值,要么永遠都不要返回值。否則,如果函數有時候返回值,有時候有不返回值,會給調試代碼帶來不便。

嚴格模式對函數有一些限制:
 不能把函數命名為eval 或arguments;
 不能把參數命名為eval 或arguments;
 不能出現兩個命名參數同名的情況。
如果發生以上情況,就會導致語法錯誤,代碼無法執行。

 

理解參數

arguments 對象只是與數組類似(它並不是Array 的實例),因為可以使用方括號語法訪問它的每一個元素(即第一個元素是arguments[0],第二個元素是argumetns[1],以此類推),使用length 屬性來確定傳遞進來多少個參數。在前面的例子中,sayHi()函數的第一個參數的名字叫name,而該參數的值也可以通過訪問arguments[0]來獲取。因此,那個函數也可以像下面這樣重寫,即不顯式地使用命名參數:
function sayHi() {
  alert("Hello " + arguments[0] + "," + arguments[1]);
}

這個重寫后的函數中不包含命名的參數。雖然沒有使用name 和message 標識符,但函數的功能依舊。這個事實說明了ECMAScript 函數的一個重要特點:命名的參數只提供便利,但不是必需的。另外,在命名參數方面,其他語言可能需要事先創建一個函數簽名,而將來的調用必須與該簽名一致。但在ECMAScript 中,沒有這些條條框框,解析器不會驗證命名參數。
通過訪問arguments 對象的length 屬性可以獲知有多少個參數傳遞給了函數。下面這個函數會在每次被調用時,輸出傳入其中的參數個數:
function howManyArgs() {
  alert(arguments.length);
}
howManyArgs("string", 45); //2
howManyArgs(); //0
howManyArgs(12); //1

執行以上代碼會依次出現3 個警告框,分別顯示2、0 和1。由此可見,開發人員可以利用這一點讓函數能夠接收任意個參數並分別實現適當的功能。請看下面的例子:
function doAdd() {
  if(arguments.length == 1) {
    alert(arguments[0] + 10);
  } else if (arguments.length == 2) {
    alert(arguments[0] + arguments[1]);
  }
}
doAdd(10); //20
doAdd(30, 20); //50

函數doAdd()會在只有一個參數的情況下給該參數加上10;如果是兩個參數,則將那個參數簡單相加並返回結果。因此,doAdd(10)會返回20,而doAdd(30,20)則返回50。雖然這個特性算不上完美的重載,但也足夠彌補ECMAScript 的這一缺憾了。
另一個與參數相關的重要方面,就是arguments 對象可以與命名參數一起使用,如下面的例子所示:
function doAdd(num1, num2) {
  if(arguments.length == 1) {
    alert(num1 + 10);
  } else if (arguments.length == 2) {
    alert(arguments[0] + num2);
  }
}

在重寫后的這個doAdd()函數中,兩個命名參數都與arguments 對象一起使用。由於num1 的值與arguments[0]的值相同,因此它們可以互換使用(當然,num2 和arguments[1]也是如此)。
關於arguments 的行為,還有一點比較有意思。那就是它的值永遠與對應命名參數的值保持同步。
例如:

function doAdd(num1, num2) {
  arguments[1] = 10;
  alert(arguments[0] + num2);
}

每次執行這個doAdd()函數都會重寫第二個參數,將第二個參數的值修改為10。因為arguments對象中的值會自動反映到對應的命名參數,所以修改arguments[1],也就修改了num2,結果它們的值都會變成10。不過,這並不是說讀取這兩個值會訪問相同的內存空間;它們的內存空間是獨立的,但它們的值會同步。另外還要記住,如果只傳入了一個參數,那么為arguments[1]設置的值不會反應到命名參數中。這是因為arguments 對象的長度是由傳入的參數個數決定的,不是由定義函數時的命名參數的個數決定的。
關於參數還要記住最后一點:沒有傳遞值的命名參數將自動被賦予undefined 值。這就跟定義了變量但又沒有初始化一樣。例如,如果只給doAdd()函數傳遞了一個參數,則num2 中就會保存undefined 值。
嚴格模式對如何使用arguments 對象做出了一些限制。首先,像前面例子中那樣的賦值會變得無效。也就是說,即使把arguments[1]設置為10,num2 的值仍然還是undefined。其次,重寫arguments 的值會導致語法錯誤(代碼將不會執行)。

 

沒有重載
ECMAScript 函數不能像傳統意義上那樣實現重載。而在其他語言(如Java)中,可以為一個函數編寫兩個定義,只要這兩個定義的簽名(接受的參數的類型和數量)不同即可。如前所述,ECMAScirpt函數沒有簽名,因為其參數是由包含零或多個值的數組來表示的。而沒有函數簽名,真正的重載是不可能做到的。
如果在ECMAScript 中定義了兩個名字相同的函數,則該名字只屬於后定義的函數。請看下面的例子:
function addSomeNumber(num){
  return num + 100;
}
function addSomeNumber(num) {
  return num + 200;
}
var result = addSomeNumber(100); //300

在此,函數addSomeNumber()被定義了兩次。第一個版本給參數加100,而第二個版本給參數加200。由於后定義的函數覆蓋了先定義的函數,因此當在最后一行代碼中調用這個函數時,返回的結果就是300。

如前所述,通過檢查傳入函數中參數的類型和數量並作出不同的反應,可以模仿方法的重載。

 


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com