From 218934fa2ed1c702a6d3923d2aa2cc6b43c48684 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:43:23 -0500 Subject: initial commit --- .../control_flow_and_error_handling/index.html | 429 ++++++++++ .../guide/details_of_the_object_model/index.html | 720 ++++++++++++++++ .../guide/expressions_and_operators/index.html | 934 +++++++++++++++++++++ .../web/javascript/guide/functions/index.html | 442 ++++++++++ .../javascript/guide/grammar_and_types/index.html | 697 +++++++++++++++ files/zh-tw/web/javascript/guide/index.html | 116 +++ .../guide/indexed_collections/index.html | 450 ++++++++++ .../web/javascript/guide/introduction/index.html | 180 ++++ .../guide/iterators_and_generators/index.html | 193 +++++ .../javascript/guide/keyed_collections/index.html | 156 ++++ .../guide/loops_and_iteration/index.html | 337 ++++++++ .../javascript/guide/numbers_and_dates/index.html | 383 +++++++++ .../guide/regular_expressions/index.html | 700 +++++++++++++++ .../web/javascript/guide/using_promises/index.html | 256 ++++++ .../guide/working_with_objects/index.html | 499 +++++++++++ 15 files changed, 6492 insertions(+) create mode 100644 files/zh-tw/web/javascript/guide/control_flow_and_error_handling/index.html create mode 100644 files/zh-tw/web/javascript/guide/details_of_the_object_model/index.html create mode 100644 files/zh-tw/web/javascript/guide/expressions_and_operators/index.html create mode 100644 files/zh-tw/web/javascript/guide/functions/index.html create mode 100644 files/zh-tw/web/javascript/guide/grammar_and_types/index.html create mode 100644 files/zh-tw/web/javascript/guide/index.html create mode 100644 files/zh-tw/web/javascript/guide/indexed_collections/index.html create mode 100644 files/zh-tw/web/javascript/guide/introduction/index.html create mode 100644 files/zh-tw/web/javascript/guide/iterators_and_generators/index.html create mode 100644 files/zh-tw/web/javascript/guide/keyed_collections/index.html create mode 100644 files/zh-tw/web/javascript/guide/loops_and_iteration/index.html create mode 100644 files/zh-tw/web/javascript/guide/numbers_and_dates/index.html create mode 100644 files/zh-tw/web/javascript/guide/regular_expressions/index.html create mode 100644 files/zh-tw/web/javascript/guide/using_promises/index.html create mode 100644 files/zh-tw/web/javascript/guide/working_with_objects/index.html (limited to 'files/zh-tw/web/javascript/guide') diff --git a/files/zh-tw/web/javascript/guide/control_flow_and_error_handling/index.html b/files/zh-tw/web/javascript/guide/control_flow_and_error_handling/index.html new file mode 100644 index 0000000000..fd658f1c77 --- /dev/null +++ b/files/zh-tw/web/javascript/guide/control_flow_and_error_handling/index.html @@ -0,0 +1,429 @@ +--- +title: 流程控制與例外處理 +slug: Web/JavaScript/Guide/Control_flow_and_error_handling +tags: + - Beginner + - Guide + - JavaScript + - 初學者 + - 指南 +translation_of: Web/JavaScript/Guide/Control_flow_and_error_handling +--- +
{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Grammar_and_types", "Web/JavaScript/Guide/Loops_and_iteration")}}
+ +

JavaScript 擁有許多陳述式,特別是流程控制的陳述式,你可以用這些陳述式來增加程式的互動性。這個章節將會概要介紹陳述式。

+ +

JavaScript 參考中有比本章更多關於陳述式的細節。 在 Javascript 程式碼中,分號(;)被用來隔開陳述式。

+ +

任何 JavaScript 運算式也是一個陳述式。 有關運算式的完整資訊,請參閱運算式與運算子

+ +

區塊陳述式

+ +

最基本的陳述式是「用於將陳述式分塊」的「區塊陳述式」。程式區塊被以一對大括號隔離開來:

+ +
{
+  陳述式 1;
+  陳述式 2;
+  .
+  .
+  .
+  陳述式 n;
+}
+
+ +

範例

+ +

區塊陳述式經常與流程控制陳述式(例如:ifforwhile)搭配使用。

+ +
while (x < 10) {
+  x++;
+}
+
+ +

在這裏,{ x++; } 是區塊陳述式。

+ +

重要:JavaScript 在 ECMAScript2015 (第六版)以前的版本並沒有區塊範圍的概念。 在區塊中的變數有效範圍是包含該區塊的函式或者是執行檔,並且在區塊外也可使用它們。換句話說,區塊並不定義了範圍。JavaScript 中的"獨立"區塊將會產生與 C 和 Java 中不同的效果。舉例來說:

+ +
var x = 1;
+{
+  var x = 2;
+}
+console.log(x); // 輸出 2
+
+ +

這裏輸出了 2 是因為區塊中變數 var x 陳述式擁有與區塊外的 var x 相同的有效範圍。在 C 或 Java,相同的程式碼將會輸出 1。

+ +

從 ECMAScript2015 版本起, 使用 let 定義的變數範圍將被限制於區塊內。

+ +

條件陳述式

+ +

條件陳述式是一些在指定條件爲真下將被執行的指令。 JavaScript 支援兩種條件陳述式: if...elseswitch

+ +

if...else 陳述式

+ +

if 陳述式將在「邏輯判斷爲真」下執行接下來的一個陳述式。選擇性的 else 陳述式將會在「邏輯判斷爲否」時被執行。 if 陳述式的用法看起來如下:

+ +
if (指定條件) {
+  陳述式 1;
+} else {
+  陳述式 2;
+}
+ +

指定條件可以是任何會回傳true或false的運算式。參見 Boolean 來進一步瞭解哪些運算式會回傳 truefalse。假如指定條件爲 true,陳述式 1 會被執行;否則,陳述式 2 會被執行。陳述式 1 及陳述式 2 可以是任何陳述式,包含巢狀 if 陳述式。

+ +

你也可以藉由 else if 來使用複合的陳述式來測試多種不同的條件,如下:

+ +
if (指定條件1) {
+  陳述式 1;
+} else if (指定條件 2) {
+  陳述式 2;
+} else if (指定條件 n) {
+  陳述式 n;
+} else {
+  最後陳述式;
+}
+
+ +

在多個條件中,只有第一個條件爲 true 的陳述式會被執行。若要執行多個陳述式,可以將陳述式包在同一個程式區塊中({ ... })。 通常來說,使用區塊陳述式是很好的習慣,尤其在使用巢狀 if 的時候:

+ +
if (指定條件) {
+  陳述式_1_執行_當_指定條件_爲_真;
+  陳述式_2_執行_當_指定條件_爲_真;
+} else {
+  陳述式_3_執行_當_指定條件_爲_否;
+  陳述式_4_執行_當_指定條件_爲_否;
+}
+
+ +
建議不要在以賦值作爲條件運算式,因為"賦值"常常被和"等於"搞混。 例如, 不要寫出如下面的程式碼:
+ +
if (x = y) {
+  /* 陳述式 */
+}
+
+ +

如果你真的需要以賦值作爲條件運算式,一種常見的方式是額外在賦值式外面加上一組括號。例如:

+ +
if ((x = y)) {
+  /* 陳述式 */
+}
+
+ +

爲"否"的值

+ +

下列的值會被看作 false(也稱為 {{Glossary("Falsy")}} 值)

+ + + +

其他所有的值,包含所有物件,當被作為條件陳述式都會被視為 true

+ +

不要把"布林真假值"和"布林物件的真假值"弄混了。 例如:

+ +
var b = new Boolean(false);
+if (b) // 這會是 True
+if (b == true) // 這會是 false
+
+ +

範例

+ +

在下面的範例中,函式 checkData 回傳 trueText 物件的長度爲三;否則, 顯示出 alert 並回傳 false

+ +
function checkData() {
+  if (document.form1.threeChar.value.length == 3) {
+    return true;
+  } else {
+    alert("請輸入恰好三個字元. " +
+    document.form1.threeChar.value + " is not valid.");
+    return false;
+  }
+}
+
+ +

switch 陳述式

+ +

switch 陳述式允許程式依運算式的不同值來選擇不同標籤。 假如運算式和標籤匹配,程式會執行標籤對應的陳述式。範例如下:

+ +
switch (運算式) {
+  case 標籤 1:
+    陳述式 1
+    [break;]
+  case 標籤 2:
+    陳述式 2
+    [break;]
+    ...
+  default:
+    陳述式
+    [break;]
+}
+
+ +

程序首先尋找一個標籤與運算式的值匹配的 case 子句,然後將控制權轉移給該子句,執行與其相關的陳述式。 如果沒有找到匹配的標籤,程序將查找 default 子句(選擇性),如果找到,則將控制權轉移到該子句,執行關聯的陳述式。 如果沒有找到 default 子句,程序繼續在 switch 結束後的陳述式執行。 按照慣例,默認子句是最後一個子句,但並不硬性規定。

+ +

與每個 case 子句相關聯的 break 陳述式(選擇性)確保程序在發現匹配的陳述式之後退出 switch,並在 switch 後的陳述式中繼續執行。 如果省略 break,程序將繼續在 switch 陳述式中的下一個陳述式執行。

+ +

範例

+ +

在下面範例中,如果變數 fruittype 為「Bananas」,程序將與「Bananas」匹配並執行相關陳述式。 當遇到 break 時,程序離開 switch 並執行 switch 後的陳述式。 如果省略 break,也將執行 case 「Cherries」的陳述式。

+ +
switch (fruittype) {
+  case "Oranges":
+    console.log("Oranges are $0.59 a pound.");
+    break;
+  case "Apples":
+    console.log("Apples are $0.32 a pound.");
+    break;
+  case "Bananas":
+    console.log("Bananas are $0.48 a pound.");
+    break;
+  case "Cherries":
+    console.log("Cherries are $3.00 a pound.");
+    break;
+  case "Mangoes":
+    console.log("Mangoes are $0.56 a pound.");
+    break;
+  case "Papayas":
+    console.log("Mangoes and papayas are $2.79 a pound.");
+    break;
+  default:
+   console.log("Sorry, we are out of " + fruittype + ".");
+}
+console.log("Is there anything else you'd like?");
+ +

例外處理陳述式

+ +

你可以用以 throw 陳述式丟出例外,並以 try...catch 陳述式處理之。

+ + + +

例外的形態

+ +

任何物件(object)都可以在 JavaScript 中被拋出。 然而,並非所有拋出的物件都相同。 雖然將數字或字串作為錯誤物件使用是相當常見的,但使用為此目的專門創造的一種例外物件類型通常更有效:

+ + + +

throw 陳述式

+ +

使用 throw 陳述式拋出例外。當拋出例外時,你要指定包含在要拋出物件中的值:

+ +
throw expression;
+
+ +

您可以拋出任何運算式,而不僅僅是特定類型的運算式。以下的程式碼會拋出一些不同類型的例外:

+ +
throw "Error2";   // 字串形態
+throw 42;         // 數字形態
+throw true;       // True/False
+throw {toString: function() { return "我是物件!"; } };
+
+ +
備註:您可以在拋出例外時指定物件。 然後,可以在 catch 區塊中引用對象的屬性。
+ +
// 創建類型爲 UserException 的物件
+function UserException(message) {
+  this.message = message;
+  this.name = "UserException";
+}
+
+// 讓例外轉換成整齊的字串當它被當作字串使用時
+// (舉例來說:於 error console)
+UserException.prototype.toString = function() {
+  return this.name + ': "' + this.message + '"';
+}
+
+// 創建一個物件的實例並丟出它
+throw new UserException("Value too high");
+ +

try...catch 陳述式

+ +

try...catch 陳述式標記了一組要嘗試的陳述式,並在拋出例外時指定一個或多個響應。 如果例外被拋出,try...catch 陳述式捕獲它。

+ +

try...catch 陳述式包括一個 try 區塊,它包含一個或多個陳述式,零個或多個 catch 區塊,包含在 try 區塊中拋出例外時該做什麼的陳述式。 也就是說,你希望 try 區塊成功,如果它不成功,你希望控制權傳遞給 catch 區塊。 如果 try 區塊內的任何陳述式(或在 try 區塊內調用的函數中)拋出例外,則控制立即切換到 catch 區塊。 如果在 try 區塊中沒有拋出例外,則跳過 catch 區塊。 finally 區塊在 trycatch 區塊執行後執行,但在 try...catch 陳述式之後的陳述式之前執行。

+ +

以下的範例使用 try...catch 陳述式。該範例調用基於傳遞給函數的值並從陣列中檢索月份名稱的函數。如果值不對應於月份數(1-12),則會拋出一個例外,其值為 "InvalidMonthNo",並且 catch 區塊中的陳述式將 monthName 變數設置為 unknown

+ +
function getMonthName(mo) {
+  mo = mo - 1; // Adjust month number for array index (1 = Jan, 12 = Dec)
+  var months = ["Jan","Feb","Mar","Apr","May","Jun","Jul",
+                "Aug","Sep","Oct","Nov","Dec"];
+  if (months[mo]) {
+    return months[mo];
+  } else {
+    throw "InvalidMonthNo"; //throw 關鍵字在這裏被使用
+  }
+}
+
+try { // statements to try
+  monthName = getMonthName(myMonth); // 函式可以丟出例外
+}
+catch (e) {
+  monthName = "unknown";
+  logMyErrors(e); // 將例外傳至例外處理機制
+}
+
+ +

catch 區塊

+ +

你可以使用 catch 區塊來處理 try 區塊可能丟出的例外。

+ +
catch (catchID) {
+  陳述式
+}
+
+ +

catch 區塊指定用來保存 throw 陳述式所丟出的值的標識符(前面語法中的 catchID) 您可以使用此標識符獲取有關被拋出的例外的信息。 JavaScript 在進入catch 區塊時創建此標識符; 標識符僅持續 catch 區塊的持續時間;在 catch 區塊完成執行後,標識符不再可用。

+ +

例如,下列的程式碼中丟出了一個例外,當例外發生後,控制權被轉交給 catch 區塊。

+ +
try {
+  throw "myException"; // 產生例外
+}
+catch (e) {
+  // 用於處理例外的陳述式
+  logMyErrors(e); // 將例外物件傳給 error handler
+}
+
+ +

finally 區塊

+ +

finally 區塊中包含在 trycatch 區塊執行之後但在 try...catch 陳述式之後的陳述式之前 執行的陳述式。 無論是否拋出例外,finally 區塊都會執行。 如果拋出例外,則即使沒有 catch 區塊處理例外,finally 區塊中的陳述式也會執行。

+ +

您可以使用 finally 區塊來使腳本在發生例外時正常地結束。例如,您可能需要釋放腳本中綁定的資源。 在以下示例中,打開一個文件,然後執行使用該文件的陳述式(伺服器端 JavaScript 允許您訪問文件)。 如果在打開文件時拋出例外,finally 區塊會在腳本結束之前關閉文件。

+ +
openMyFile();
+try {
+  writeMyFile(theData); // 可能產生例外
+} catch(e) {
+  handleError(e); // 處理可能發生的例外
+} finally {
+  closeMyFile(); // 總是在 try 結束後關閉檔案
+}
+
+ +

如果 finally 區塊有返回值,那麼該值將成為整個 try-catch-finally 過程的返回值,而捨棄 trycatch 區塊中的任何返回陳述式:

+ +
function f() {
+  try {
+    console.log(0);
+    throw "bogus";
+  } catch(e) {
+    console.log(1);
+    return true; // 這個回傳會被擱置
+                 // 直到 finally 區塊結束
+    console.log(2); // 不會到達這裏
+  } finally {
+    console.log(3);
+    return false; // 覆寫先前的 "return"
+    console.log(4); // 不會到達這裏
+  }
+  // "return false" 在這裏被執行
+  console.log(5); // 不會到達這裏
+}
+f(); // console 0, 1, 3; 會回傳false
+
+ +

finally 區塊覆寫返回值也適用於在 catch 區塊中拋出或重新拋出的例外(即便在catch 中再次丟出例外,catch 所屬的 finally 區塊還是會被執行):

+ +
function f() {
+  try {
+    throw "bogus";
+  } catch(e) {
+    console.log('caught inner "bogus"');
+    throw e; // 此處的 throw 陳述式將被擱置到
+             // finally區塊結束
+  } finally {
+    return false; // 覆寫先前的"throw"
+  }
+  // "return false" 在此被執行
+}
+
+try {
+  f();
+} catch(e) {
+  // 這裏永遠不可能到達因為在f函式中catch的throw
+  // 被finally中的return覆寫了
+  console.log('caught outer "bogus"');
+}
+
+// 輸出 -> caught inner "bogus"
+ +

巢狀 try...catch 陳述式

+ +

你可以使用一個或多個的 try...catch 陳述式。 假如一個內層的try...catch 陳述式不具有 catch 區塊, 它將必須要有 finally 區塊與及封閉的 try...catch 陳述式來檢測是否有符合的例外。

+ +

使用 Error 物件

+ +

根據錯誤的類型,您可以使用 "name" 和 "message" 屬性來獲取更精確的資訊。"name" 提供了錯誤所屬的類別(class)(例如,"DOMException" 或 "Error"),而 "message" 通常提供藉由將錯誤物件轉換為字串所獲得的更簡潔的資訊。參見巢狀 try 區塊位於 try...catch 參考資料頁面。

+ +

假如您要丟出自定義的例外, 為了方便使用這些屬性(例如,如果你的 catch 區塊並不要區分你自己的例外和系統的),你可以使用 Error 構造子。舉例來說:

+ +
function doSomethingErrorProne () {
+  if (ourCodeMakesAMistake()) {
+    throw (new Error('The message'));
+  } else {
+    doSomethingToGetAJavascriptError();
+  }
+}
+....
+try {
+  doSomethingErrorProne();
+}
+catch (e) {
+  console.log(e.name); // 紀錄 'Error'
+  console.log(e.message); // 紀錄 'The message' 或者其他 JavaScript 例外的資訊)
+}
+ +

Promises 容器

+ +

從 ECMAScript2015 起,JavaScript 支援 {{jsxref("Promise")}} 物件,允許您控制延遲和異步操作的流程。

+ +

Promise 有以下幾種狀態:

+ + + +

+ +

使用 XHR 載入圖檔

+ +

這裏有個簡單的範例,使用了 Promise 物件與及 XMLHttpRequest 來載入 MDN GitHub promise-test repository 中的一張圖檔。你也可以觀看結果。 每一步都有註解來讓您慢慢理解 Promise 物件與及 XHR 架構。 下面的版本沒有註解,但藉由觀察 Promise 物件的變動您或許可以對 promise 物件有所了解:

+ +
function imgLoad(url) {
+  return new Promise(function(resolve, reject) {
+    var request = new XMLHttpRequest();
+    request.open('GET', url);
+    request.responseType = 'blob';
+    request.onload = function() {
+      if (request.status === 200) {
+        resolve(request.response);
+      } else {
+        reject(Error('Image didn\'t load successfully; error code:'
+                     + request.statusText));
+      }
+    };
+    request.onerror = function() {
+      reject(Error('There was a network error.'));
+    };
+    request.send();
+  });
+}
+ +

以獲得更多資訊,查看 {{jsxref("Promise")}} 參照頁面,以及使用 Promises 教學。

+ +
{{PreviousNext("Web/JavaScript/Guide/Grammar_and_types", "Web/JavaScript/Guide/Loops_and_iteration")}}
diff --git a/files/zh-tw/web/javascript/guide/details_of_the_object_model/index.html b/files/zh-tw/web/javascript/guide/details_of_the_object_model/index.html new file mode 100644 index 0000000000..5b7872afce --- /dev/null +++ b/files/zh-tw/web/javascript/guide/details_of_the_object_model/index.html @@ -0,0 +1,720 @@ +--- +title: 深入了解物件模型 +slug: Web/JavaScript/Guide/Details_of_the_Object_Model +translation_of: Web/JavaScript/Guide/Details_of_the_Object_Model +--- +

{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Working_with_Objects", "Web/JavaScript/Guide/Iterators_and_Generators")}}

+ +

JavaScript 是一種基於原型,而不是基於類的物件導向語言。由於這個根本的區別,使它在如何創建物件的層級結構,以及如何繼承屬性和它的值上,顯得有點模糊。本文將闡明這個問題。

+ +

本文假設您已經有點 JavaScript 的基礎,並且用 JavaScript 的函數創建過簡單的物件。

+ +

基於類 (Class-Based) 與 基於原型 (Prototype-Based) 語言的比較

+ +

基於類的物件導向語言,比如 Java 和 C++,是建基於兩種概念之上:類和實例。

+ + + +

基於原型的語言(例如 JavaScript)並不存在這種區別:它只有物件。基於原型的語言具有所謂原型物件(Prototypical Object)的概念。新物件在初始化時以原型物件為範本獲得屬性。任何物件都可以指定其自身的屬性,在創建時或運行時都可以。而且,任何物件都可以關聯為另一個物件的原型(Prototype),從而允許後者共用前者的屬性。

+ +

定義類

+ +

在基於類的語言中,類被定義在分開的類定義(Class Definition)。在類定義中,允許定義特殊的方法,稱為建構函數(Constructor),用以創建該類的實例。建構函數可以指定實例屬性的初始值,以及它的初始化處理。使用 new 操作符和建構函數來創建類的實例。

+ +

JavaScript 也遵循類似的模型,但卻沒有類定義。JavaScript 使用建構函數來定義物件的屬性及初始值,所有的 JavaScript 函數都可以作為建構函數。使用 new 操作符來建立實例。

+ +

子類 (Subclass) 和繼承 (Inheritance)

+ +

基於類的語言是通過類定義來構建類的層級結構。在類定義中,可以指定新的類為一個現存的類的子類。子類將繼承超類的全部屬性,並可以添加新的屬性或者修改繼承的屬性。例如,假設 Employee 類只有 namedept 屬性,而 ManagerEmployee 的子類並添加了 reports 屬性。這時,Manager 類的實例將具有三個屬性:name,dept 和 reports

+ +

JavaScript 的繼承通過關聯另一個有構建函數的原型物件來實現,這樣,您可以創建完全一樣的 EmployeeManager 範例,不過使用的方法略有不同。首先,定義 Employee 構建函數,指定 namedept 屬性。然後,定義 Manager 構建函數,指定 reports 屬性。最後,將一個新的 Employee 物件賦值給 Manager 構建函數的 prototype 屬性。這樣,當創建一個新的 Manager 物件時,它將從 Employee 物件中繼承 namedept 屬性。

+ +

添加和移除屬性

+ +

在基於類的語言中,通常要在編譯時建立類,然後在編譯時或者運行時產生實例。一旦定義了類,便無法改變類的屬性數目或者類型。但在 JavaScript 中,允許運行時增加或者移除任何物件的屬性。如果在物件的原型物件中增加屬性,則以該物件作為原型的所有物件也將獲得該屬性。

+ +

區別摘要

+ +

下面的表格列出了上述區別。本節的後續部分將描述有關使用 JavaScript 構建函數和原型創建物件層級結構的詳細資訊,並與在 Java 中的做法做對比。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
表 8.1 基於類(Java)和基於原型(JavaScript)的物件系統的比較
基於類的(Java)基於原型的(JavaScript)
類和實例是不同的事物。所有物件均為實例。
通過類定義來定義類;通過建構函數來產生實體。通過建構函數來定義和創建一組物件。
通過 new 操作符來創建物件。相同。
通過類定義來定義現存類的子類,從而建構物件的層級結構。通過將一個物件作為原型指定關聯於建構函數來建構物件的層級結構。
遵循類鏈繼承屬性。遵循原型鏈繼承屬性。
類定義指定類的所有實例的所有屬性。無法在運行時添加屬性。建構函數或原型指定初始的屬性集。允許動態地向單個的物件或者整個物件集中添加屬性,或者從中移除屬性。
+ +

僱員示例

+ +

本節的餘下部分將使用如下圖所示的僱員層級結構。

+ +

+ +

圖例 8.1:一個簡單的物件層級

+ +

該示例中使用以下物件:

+ + + +

創建層級結構

+ +

有幾種不同的方式,可以用於定義適當的建構函數,藉以實現僱員的層級結構。如何選擇很大程度上取決於您希望在您的應用程式中能做到什麼。

+ +

本節展現了如何使用非常簡單的(同時也是相當不靈活的)定義,使得繼承得以實現。在這些定義中,無法在創建物件時指定屬性的值。新創建的物件僅僅獲得了預設值,當然允許隨後加以修改。圖例 8.2 展現了這些簡單的定義形成的層級結構。

+ +

在 真實的應用程式中,您很可能想定義允許在創建物件時給出屬性值的建構函數。(參見 更靈活的建構函數 獲得進一步的資訊)。對於現在而言,這些簡單的定義示範了繼承是如何發生的。

+ +

figure8.2.png
+ 圖例 8.2:Employee 物件定義

+ +

以下 Employee 的 Java 和 JavaScript 的定義相類似。唯一的不同是在 Java 中需要指定每個屬性的類型,而在 JavaScript 中則不必指定,同時 Java 的類必須創建一個顯式的建構函數方法。

+ + + + + + + + + + + + + + +
JavaScriptJava
+
+function Employee () {
+  this.name = "";
+  this.dept = "general";
+}
+
+
+
+public class Employee {
+   public String name;
+   public String dept;
+   public Employee () {
+      this.name = "";
+      this.dept = "general";
+   }
+}
+
+
+ +

ManagerWorkerBee 的定義顯示了在如何指定繼承鏈中上一層物件方面的不同點。在 JavaScript 中,需要為建構函數的 prototype 屬性添加一個原型實例作為它的屬性值。您可以在定義了建構函數之後的任何時間添加這一屬性。而在 Java 中,則需要在類定義中指定超類,且不能在類定義之外改變超類。

+ + + + + + + + + + + + + + +
JavaScriptJava
+
+function Manager () {
+  this.reports = [];
+}
+Manager.prototype = new Employee;
+
+function WorkerBee () {
+  this.projects = [];
+}
+WorkerBee.prototype = new Employee;
+
+
+
+public class Manager extends Employee {
+   public Employee[] reports;
+   public Manager () {
+      this.reports = new Employee[0];
+   }
+}
+
+public class WorkerBee extends Employee {
+   public String[] projects;
+   public WorkerBee () {
+      this.projects = new String[0];
+   }
+}
+
+
+ +

EngineerSalesPerson 的定義創建了派生自 WorkerBee 進而派生自 Employee 的物件。這些類型的物件將具有在這個鏈之上的所有物件的屬性。同時,這些定義重載了繼承的 dept 屬性值,賦予這些屬性特定於這些物件的新的屬性值。

+ + + + + + + + + + + + + + +
JavaScriptJava
+
+function SalesPerson () {
+   this.dept = "sales";
+   this.quota = 100;
+}
+SalesPerson.prototype = new WorkerBee;
+
+function Engineer () {
+   this.dept = "engineering";
+   this.machine = "";
+}
+Engineer.prototype = new WorkerBee;
+
+
+
+public class SalesPerson extends WorkerBee {
+   public double quota;
+   public SalesPerson () {
+      this.dept = "sales";
+      this.quota = 100.0;
+   }
+}
+
+public class Engineer extends WorkerBee {
+   public String machine;
+   public Engineer () {
+      this.dept = "engineering";
+      this.machine = "";
+   }
+}
+
+
+ +

使用這些定義,可以創建這些物件的實例。這些實例將獲得其屬性的預設值。圖例 8.3 展現了使用這些 JavaScript 定義創建新定義,並顯示了新物件中的屬性值。

+ +

{{ note('術語 實例(instance)在 基於類的語言中具有特定的技術含義。在這些語言中,實例是指類的個體成員,與類有著根本性的不同。在 JavaScript 中,“實例”並不具有這種技術含義,因為 JavaScript 中不存在類和實例之間的這種差異。然而,在談論 JavaScript 時,“實例”可以非正式地用於表示用特定的建構函數創建的物件。所以,在這個例子中,你可以非正式地 janeEngineer 的一個實例。與之類似,儘管術語父(parent)子(child)祖先(ancestor),和後代(descendant)在 JavaScript 中並沒有正式的含義,您可以非正式地使用這些術語用於指代原型鏈中處於更高層次或者更低層次的物件。') }}

+ +

figure8.3.png
+ 圖例 8.3:通過簡單的定義創建物件

+ +

物件的屬性

+ +

本節將討論物件如何從原型鏈中的其它物件中繼承屬性,以及在運行時添加屬性的相關細節。

+ +

繼承屬性

+ +

假設通過如下語句創建一個 mark 物件作為 WorkerBee(如 圖例 8.3 所示):

+ +
var mark = new WorkerBee;
+
+ +

當 JavaScript 發現 new 操作符,它將創建一個普通的物件,並將其作為關鍵字 this 的值傳遞給 WorkerBee 的建構函數。該建構函數顯式地設置 projects 屬性的值,然後隱式地將其內部的 __proto__ 屬性設置為 WorkerBee.prototype 的值(屬性的名稱前後均有兩個底線)。__proto__ 屬性決定了用於返回屬性值的原型鏈。一旦這些屬性得以設置,JavaScript 返回新創建的物件,然會設定陳述式設置變數 mark 的值為該物件。

+ +

這個過程不會顯式地為 mark 物件從原型鏈中所繼承的屬性設置值(本地值)。當請求屬性的值時,JavaScript 將首先檢查物件自身中是否設置了該屬性的值,如果有,則返回該值。如果本地值不存在,則 JavaScript 將檢查原型鏈(通過 __proto__ 屬性)。如果原型鏈中的某個物件具有該屬性的值,則返回這個值。如果沒有找到該屬性,JavaScript 則認為物件中不存在該屬性。這樣,mark 物件中將具有如下的屬性和對應的值:

+ +
mark.name = "";
+mark.dept = "general";
+mark.projects = [];
+
+ +

mark 對象從 mark.__proto__ 中保存的原型物件中繼承了 namedept 屬性的值。並由 WorkerBee 建構函數為 projects 屬性設置了本地值。 這就是 JavaScript 中的屬性和屬性值的繼承。這個過程的一些微妙之處將在 再談屬性繼承 中進一步討論。

+ +

由於這些建構函數不支援設置實例特定的值,所以,這些屬性值僅僅是泛泛地由創建自 WorkerBee 的所有物件所共用的預設值。當然,允許修改這些屬性的值。所以,您也可以為這些屬性指定特定的值,如下所示:

+ +
mark.name = "Doe, Mark";
+mark.dept = "admin";
+mark.projects = ["navigator"];
+ +

添加屬性

+ +

在 JavaScript 中,可以在運行時為任何物件添加屬性,而不必受限於建構函數提供的屬性。添加特定於某個物件的屬性,只需要為該物件指定一個屬性值,如下所示:

+ +
mark.bonus = 3000;
+
+ +

這樣 mark 物件就有了 bonus 屬性,而其它 WorkerBee 則沒有該屬性。

+ +

如果向某個建構函數的原型物件中添加新的屬性,則該屬性將添加到從這個原型中繼承屬性的所有物件的中。例如,可以通過如下的語句向所有僱員中添加 specialty 屬性:

+ +
Employee.prototype.specialty = "none";
+
+ +

一旦 JavaScript 執行該語句,則 mark 物件也將具有 specialty 屬性,其值為 "none"。下圖展現了在 Employee 原型中添加該屬性,然後在 Engineer 的原型中重載該屬性的效果。

+ +


+ Figure 8.4: Adding properties

+ +

更靈活的建構函數

+ +

到目前為止所展現的建構函數不允許在創建新的實例時指定屬性值。正如 Java 一樣,可以為建構函數提供參數以便初始化實例的屬性值。下圖展現其中一種做法。

+ +


+ Figure 8.5: Specifying properties in a constructor, take 1

+ +

下面的表格中羅列了這些物件在 Java 和 JavaScript 中的定義。

+ + + + + + + + + + + + + + + + + + + + + + +
JavaScriptJava
+
+function Employee (name, dept) {
+  this.name = name || "";
+  this.dept = dept || "general";
+}
+
+
+
+public class Employee {
+   public String name;
+   public String dept;
+   public Employee () {
+      this("", "general");
+   }
+   public Employee (String name) {
+      this(name, "general");
+   }
+   public Employee (String name, String dept) {
+      this.name = name;
+      this.dept = dept;
+   }
+}
+
+
+
+function WorkerBee (projs) {
+  this.projects = projs || [];
+}
+WorkerBee.prototype = new Employee;
+
+
+
+public class WorkerBee extends Employee {
+   public String[] projects;
+   public WorkerBee () {
+      this(new String[0]);
+   }
+   public WorkerBee (String[] projs) {
+      projects = projs;
+   }
+}
+
+
+
+
+
+function Engineer (mach) {
+   this.dept = "engineering";
+   this.machine = mach || "";
+}
+Engineer.prototype = new WorkerBee;
+
+
+
+public class Engineer extends WorkerBee {
+   public String machine;
+   public Engineer () {
+      dept = "engineering";
+      machine = "";
+   }
+   public Engineer (String mach) {
+      dept = "engineering";
+      machine = mach;
+   }
+}
+
+
+ +

這些 JavaScript 定義使用了設置預設值的一種特殊慣用法:

+ +
this.name = name || "";
+
+ +

JavaScript 的邏輯 OR 操作符(||)將求解它的第一個參數。如果該參數的值可以轉換為真,則操作符返回該值。否則,操作符返回第二個參數的值。因此,這行代碼首先檢查 name 是否具有一個對 name 屬性有用的值。如果有,則設置其為 this.name 的值。否則,設置 this.name 的值為空的字串。為求簡潔,本章將使用這一慣用法,儘管咋一看它有些費解。

+ +

{{ note('如果調用建構函數時,指定了可以轉換為 false 的參數(比如 0 (零)和空字串("")),結果可能出乎調用者意料。此時,將使用預設值(譯者注:而不是指定的參數值 0 和 "")。') }}

+ +

基於這些定義,當創建物件的實例時,可以為本地定義的屬性指定值。正如 圖例 8.5 所示一樣,您可以通過如下語句創建新的 Engineer

+ +
var jane = new Engineer("belau");
+
+ +

此時,Jane 的屬性如下所示:

+ +
jane.name == "";
+jane.dept == "engineering";
+jane.projects == [];
+jane.machine == "belau"
+
+ +

基於上述定義,無法為諸如 name 這樣的繼承屬性指定初始值。在 JavaScript 中,如果想為繼承的屬性指定初始值,建構函數中需要更多的代碼。

+ +

到目前為止,建構函數已經能夠創建一個普通物件,然後為新物件指定本地的屬性和屬性值。您還可以通過直接調用原型鏈上的更高層次物件的建構函數,讓建構函數添加更多的屬性。下面的圖例展現這種新定義。

+ +


+ Figure 8.6 Specifying properties in a constructor, take 2

+ +

讓我們仔細看看這些定義的其中之一。以下是 Engineer 建構函數的定義:

+ +
function Engineer (name, projs, mach) {
+  this.base = WorkerBee;
+  this.base(name, "engineering", projs);
+  this.machine = mach || "";
+}
+
+ +

假設您創建了一個新的 Engineer 物件,如下所示:

+ +
var jane = new Engineer("Doe, Jane", ["navigator", "javascript"], "belau");
+
+ +

JavaScript 遵循以下步驟:

+ +
    +
  1. new 操作符創建了一個新的普通物件,並將其 __proto__ 屬性設置為 Engineer.prototype
  2. +
  3. new 操作符將該新對象作為 this 關鍵字的值傳遞給 Engineer 建構函數。
  4. +
  5. 建構函數為該新物件創建了一個名為 base 的新屬性,並將 WorkerBee 的建構函數指定為 base 屬性的值。這使得 WorkerBee 建構函數成為 Engineer 物件的一個方法。base 屬性的名字沒有特殊性。可以使用任何合法的屬性名稱;base 僅僅是為了貼近它的用意。
  6. +
  7. +

    建構函數調用 base 方法,將傳遞給該建構函數的參數中的兩個,作為參數傳遞給 base 方法,同時還傳遞一個字串參數  "engineering"。顯式地在建構函數中使用 "engineering" 表明所有 Engineer 物件繼承的 dept 屬性具有相同的值,且該值重載了繼承自 Employee 的值。

    +
  8. +
  9. 因為 baseEngineer 的一個方法,在調用 base 時,JavaScript 將在步驟 1 中創建的對象綁定給 this 關鍵字。這樣,WorkerBee 函數接著將 "Doe, Jane""engineering" 參數傳遞給 Employee 建構函數。當從 Employee 建構函數返回時,WorkerBee 函數用剩下的參數設置 projects 屬性。
  10. +
  11. 當從 base 方法返回時,Engineer 建構函數將物件的 machine 屬性初始化為 "belau"
  12. +
  13. 當從建構函數返回時,JavaScript 將新物件賦值給 jane 變數。
  14. +
+ +

您可以認為,在 Engineer 的建構函數中調用 WorkerBee 的建構函數,也就為 Engineer 物件設置好了適當繼承。事實並非如此。調用 WorkerBee 建構函數確保了Engineer 物件以所有被調用的建構函數中所指定的屬性作為起步。但是,如果之後在 Employee 或者 WorkerBee 原型中添加了屬性,那些屬性不會被 Engineer 物件繼承。例如,假設如下語句:

+ +
function Engineer (name, projs, mach) {
+  this.base = WorkerBee;
+  this.base(name, "engineering", projs);
+  this.machine = mach || "";
+}
+var jane = new Engineer("Doe, Jane", ["navigator", "javascript"], "belau");
+Employee.prototype.specialty = "none";
+
+ +

物件 jane 不會繼承 specialty 屬性。必需顯式地設置原型才能確保動態的技能。假設修改為如下的語句:

+ +
function Engineer (name, projs, mach) {
+  this.base = WorkerBee;
+  this.base(name, "engineering", projs);
+  this.machine = mach || "";
+}
+Engineer.prototype = new WorkerBee;
+var jane = new Engineer("Doe, Jane", ["navigator", "javascript"], "belau");
+Employee.prototype.specialty = "none";
+
+ +

現在 jane 物件的 specialty 屬性為 "none" 了。

+ +

繼承的另一種途徑是使用call() / apply() 方法。下面的方式都是等價的:

+ + + + + + + + +
+
+function Engineer (name, projs, mach) {
+  this.base = WorkerBee;
+  this.base(name, "engineering", projs);
+  this.machine = mach || "";
+}
+
+
+
+function Engineer (name, projs, mach) {
+  WorkerBee.call(this, name, "engineering", projs);
+  this.machine = mach || "";
+}
+
+
+ +

使用 javascript 的 call() 方法相對明瞭一些,因為無需 base 方法了。

+ +

再談屬性的繼承

+ +

前面的小節中描述了 JavaScript 建構函數和原型如何提供層級結構和繼承的實現。本節中將討論之前未曾明確的一些細微之處。

+ +

本地的值和繼承的值

+ +

正如本章前面所述,在訪問一個物件的屬性時,JavaScript 將按照如下的步驟處理:

+ +
    +
  1. 檢查是否存在本地的值。如果存在,返回該值。
  2. +
  3. 如果本地值不存在,檢查原型鏈(通過 __proto__ 屬性)。
  4. +
  5. 如果原型鏈中的某個物件具有指定屬性的值,則返回該值。
  6. +
  7. 如果這樣的屬性不存在,則物件沒有該屬性。
  8. +
+ +

以上步驟的結果依賴於您是如何定義的。最早的例子中具有如下定義:

+ +
function Employee () {
+  this.name = "";
+  this.dept = "general";
+}
+
+function WorkerBee () {
+  this.projects = [];
+}
+WorkerBee.prototype = new Employee;
+
+ +

基於這些定義,假定通過如下的語句創建 WorkerBee 的實例 amy:

+ +
var amy = new WorkerBee;
+
+ +

amy 物件將具有一個本地屬性,projects。namedept 屬性則不是 amy 物件本地的,而是從 amy 物件的 __proto__ 屬性獲得的。因此,amy 將具有如下的屬性值:

+ +
amy.name == "";
+amy.dept == "general";
+amy.projects == [];
+
+ +

現在,假定修改了關聯於 Employee 的原型中的 name 屬性的值:

+ +
Employee.prototype.name = "Unknown"
+
+ +

乍一看,您可能期望新的值會傳播給所有 Employee 的實例。然而,並非如此。

+ +

在創建 Employee 對象的 任何 實例時,該實例的 name 屬性將獲得一個本地值(空的字串)。這意味著在創建一個新的 Employee 物件作為 WorkerBee 的原型時,WorkerBee.prototypename 屬性將具有一個本地值。這樣,當 JavaScript 查找 amy 物件(WorkerBee 的實例)的 name 屬性時,JavaScript 將找到 WorkerBee.prototype 中的本地值。因此,也就不會繼續在原型鏈中向上找到 Employee.prototype 了。

+ +

如果想在運行時修改物件的屬性值並且希望該值被所有該物件的後代所繼承,不能在該物件的建構函數中定義該屬性。而是應該將該屬性添加到該物件所關聯的原型中。例如,假設將前面的代碼作如下修改:

+ +
function Employee () {
+  this.dept = "general";
+}
+Employee.prototype.name = "";
+
+function WorkerBee () {
+  this.projects = [];
+}
+WorkerBee.prototype = new Employee;
+
+var amy = new WorkerBee;
+
+Employee.prototype.name = "Unknown";
+
+ +

這時,amyname 屬性將為 "Unknown"。

+ +

正如這些例子所示,如果希望物件的屬性具有預設值,且希望在運行時修改這些預設值,應該在物件的原型中設置這些屬性,而不是在建構函數中。

+ +

判斷實例的關係

+ +

JavaScript 的屬性查找機制首先在物件自身的屬性中查找,如果指定的屬性名稱沒有找到,將在物件的特殊屬性 __proto__ 中查找。這個過程是遞迴的;被稱為“在原型鏈中查找”。

+ +

特殊的 __proto__ 屬性是在構建物件時設置的;設置為建構函數的 prototype 屬性的值。所以運算式 new Foo() 將創建一個物件,其 __proto__ == Foo.prototype。因而,修改 Foo.prototype 的屬性,將改變所有通過 new Foo() 創建的物件的屬性的查找。

+ +

每個物件都有一個 __proto__ 物件屬性(除了 Object);每個函數都有一個 prototype 物件屬性。因此,通過“原型繼承(prototype inheritance)”,物件與其它物件之間形成關係。通過比較物件的 __proto__ 屬性和函數的 prototype 屬性可以檢測物件的繼承關係。JavaScript 提供了便捷方法:instanceof 操作符可以用來將一個物件和一個函數做檢測,如果物件繼承自函數的原型,則該操作符返回真。例如:

+ +
var f = new Foo();
+var isTrue = (f instanceof Foo);
+ +

作為詳細一點的例子,假定我們使用和在 繼承屬性 中相同的一組定義。創建 Engineer 物件如下:

+ +
var chris = new Engineer("Pigman, Chris", ["jsd"], "fiji");
+
+ +

對於該物件,以下所有語句均為真:

+ +
chris.__proto__ == Engineer.prototype;
+chris.__proto__.__proto__ == WorkerBee.prototype;
+chris.__proto__.__proto__.__proto__ == Employee.prototype;
+chris.__proto__.__proto__.__proto__.__proto__ == Object.prototype;
+chris.__proto__.__proto__.__proto__.__proto__.__proto__ == null;
+
+ +

基於此,可以寫出一個如下所示的 instanceOf 函數:

+ +
function instanceOf(object, constructor) {
+   while (object != null) {
+      if (object == constructor.prototype)
+         return true;
+      if (typeof object == 'xml') {
+        return constructor.prototype == XML.prototype;
+      }
+      object = object.__proto__;
+   }
+   return false;
+}
+
+ +
Note: 在上面的實現中,檢查物件的類型是否為 "xml" 的目的在於解決新近版本的 JavaScript 中表達 XML 物件的特異之處。如果您想瞭解其中瑣碎細節,可以參考 {{ bug(634150) }}。
+ +
instanceOf (chris, Engineer)
+instanceOf (chris, WorkerBee)
+instanceOf (chris, Employee)
+instanceOf (chris, Object)
+
+ +

但如下運算式為假:

+ +
instanceOf (chris, SalesPerson)
+ +

建構函數中的全域資訊

+ +

在創建建構函數時,在建構函數中設置全域資訊要小心。例如,假設希望為每一個僱員分配一個唯一標識。可能會為 Employee 使用如下定義:

+ +
var idCounter = 1;
+
+function Employee (name, dept) {
+   this.name = name || "";
+   this.dept = dept || "general";
+   this.id = idCounter++;
+}
+
+ +

基於該定義,在創建新的 Employee 時,建構函數為其分配了序列中的下一個識別字。然後遞增全域的識別字計數器。因此,如果,如果隨後的語句如下,則 victoria.id 為 1 而 harry.id 為 2:

+ +
var victoria = new Employee("Pigbert, Victoria", "pubs")
+var harry = new Employee("Tschopik, Harry", "sales")
+
+ +

乍一看似乎沒問題。但是,無論什麼目的,在每一次創建 Employee 對象時,idCounter 都將被遞增一次。如果創建本章中所描述的整個 Employee 層級結構,每次設置原型的時候,Employee 建構函數都將被調用一次。假設有如下代碼:

+ +
var idCounter = 1;
+
+function Employee (name, dept) {
+   this.name = name || "";
+   this.dept = dept || "general";
+   this.id = idCounter++;
+}
+
+function Manager (name, dept, reports) {...}
+Manager.prototype = new Employee;
+
+function WorkerBee (name, dept, projs) {...}
+WorkerBee.prototype = new Employee;
+
+function Engineer (name, projs, mach) {...}
+Engineer.prototype = new WorkerBee;
+
+function SalesPerson (name, projs, quota) {...}
+SalesPerson.prototype = new WorkerBee;
+
+var mac = new Engineer("Wood, Mac");
+
+ +

還可以進一步假設上面省略掉的定義中包含 base 屬性而且調用了原型鏈中高於它們的建構函數。即便在現在這個情況下,在 mac 物件創建時,mac.id 為 5。

+ +

依賴于應用程式,計數器額外的遞增可能有問題,也可能沒問題。如果確實需要準確的計數器,則以下建構函數可以作為一個可行的方案:

+ +
function Employee (name, dept) {
+   this.name = name || "";
+   this.dept = dept || "general";
+   if (name)
+      this.id = idCounter++;
+}
+
+ +

在用作原型而創建新的 Employee 實例時,不會指定參數。使用這個建構函數定義,如果不指定參數,建構函數不會指定識別字,也不會遞增計數器。而如果想讓 Employee 分配到識別字,則必需為僱員指定姓名。在這個例子中,mac.id 將為 1。

+ +

沒有多繼承

+ +

某些物件導向語言支援多重繼承。也就是說,物件可以從無關的多個父物件中繼承屬性和屬性值。JavaScript 不支持多重繼承。

+ +

JavaScript 屬性值的繼承是在運行時通過檢索物件的原型鏈來實現的。因為物件只有一個原型與之關聯,所以 JavaScript 無法動態地從多個原型鏈中繼承。

+ +

在 JavaScript 中,可以在建構函數中調用多個其它的建構函數。這一點造成了多重繼承的假像。例如,考慮如下語句:

+ +
function Hobbyist (hobby) {
+   this.hobby = hobby || "scuba";
+}
+
+function Engineer (name, projs, mach, hobby) {
+   this.base1 = WorkerBee;
+   this.base1(name, "engineering", projs);
+   this.base2 = Hobbyist;
+   this.base2(hobby);
+   this.machine = mach || "";
+}
+Engineer.prototype = new WorkerBee;
+
+var dennis = new Engineer("Doe, Dennis", ["collabra"], "hugo")
+
+ +

進一步假設使用本章前面所屬的 WorkerBee 的定義。此時 dennis 物件具有如下屬性:

+ +
dennis.name == "Doe, Dennis"
+dennis.dept == "engineering"
+dennis.projects == ["collabra"]
+dennis.machine == "hugo"
+dennis.hobby == "scuba"
+
+ +

dennis 確實從 Hobbyist 建構函數中獲得了 hobby 屬性。但是,假設添加了一個屬性到 Hobbyist 建構函數的原型:

+ +
Hobbyist.prototype.equipment = ["mask", "fins", "regulator", "bcd"]
+
+ +

dennis 物件不會繼承這個新屬性。

+ +
{{ PreviousNext("JavaScript/Guide/Predefined_Core_Objects", "JavaScript/Guide/Inheritance_Revisited") }}
diff --git a/files/zh-tw/web/javascript/guide/expressions_and_operators/index.html b/files/zh-tw/web/javascript/guide/expressions_and_operators/index.html new file mode 100644 index 0000000000..2792b5682a --- /dev/null +++ b/files/zh-tw/web/javascript/guide/expressions_and_operators/index.html @@ -0,0 +1,934 @@ +--- +title: 運算式與運算子 +slug: Web/JavaScript/Guide/Expressions_and_Operators +translation_of: Web/JavaScript/Guide/Expressions_and_Operators +--- +
{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Functions", "Web/JavaScript/Guide/Numbers_and_dates")}}
+ +

這個章節將講述 JavaScript 的運算式與運算子,包括賦值運算子,比較運算子,算術運算子,位元運算子, 邏輯運算子, 字串運算子, 條件(三元)運算子 以及更多運算子.

+ +

更多關於運算子以及運算式的資料可以在 reference 中找到。

+ +

運算子

+ +

JavaScript 有以下幾種運算子。 此處將描述運算子以及一些運算子的優先順序。

+ + + +

JavaScript 同時具有二元運算子及一元運算子, 以及一種特殊的 三元運算子,也就是 條件運算子。 一個二元運算子需要具備兩個運算元, 一個在運算元之前,一個在運算元之後:

+ +
運算元1 運算子 運算元2
+
+ +

例如, 3+4x*y.

+ +

一個 一元運算子 需要一個運算元, 位於運算子之前或之後:

+ +
運算子 運算元
+
+ +

+ +
運算元 運算子
+
+ +

例如, x++++x.

+ +

賦值運算子

+ +

一個 賦值運算子 將 基於其 右方的運算元 的值賦予其 左方的運算元。 最簡單的 賦值運算子 是 等於 (=), 它將賦予 左方運算元 與 右方運算元相同之值。 也就是, x = y 會把y的值賦予給x。

+ +

也有一些復合的 賦值運算子 是為了縮短下面表中的運算:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
復合運算子
名稱簡化的運算子意義
賦值x = yx = y
加法賦值x += yx = x + y
減法賦值x -= yx = x - y
乘法賦值x *= yx = x * y
除法賦值x /= yx = x / y
餘數賦值x %= yx = x % y
指數賦值x **= yx = x ** y
左移賦值x <<= yx = x << y
右移賦值x >>= yx = x >> y
無號右移賦值x >>>= yx = x >>> y
位元 AND 賦值x &= yx = x & y
位元 XOR 賦值x ^= yx = x ^ y
位元 OR 賦值x |= yx = x | y
+ +

解構

+ +

為了進行更複雜的賦值,解構賦值是 JavaScript 用來從陣列或物件中提取資料的語法。

+ +
var foo = ['one', 'two', 'three'];
+
+// 不使用解構
+var one   = foo[0];
+var two   = foo[1];
+var three = foo[2];
+
+// 使用解構
+var [one, two, three] = foo;
+ +

比較運算子

+ +

比較運算子 會比較 運算元 並基於比較的結果回傳邏輯值。 運算元可以是數字,字串,邏輯,或物件的值。 字串的比較是基於字典序的, 使用 Unicode 的值。 在多數情況下,假如兩個運算元不具有相同型態, JavaScript 會嘗試將它們轉換成相同型態。這個行為通常是將運算元以數學形式對待。 在某些的轉換型態的例外中會使用到 ===!== 運算子, 它們會嚴格地進行相等或不相等的比較。 這些運算子不會在確認相等與否前嘗試轉換運算元的型態。 下面的表解釋了比較運算子:

+ +
var var1 = 3;
+var var2 = 4;
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
比較運算子
運算子描述會回傳True的例子
等於 (==)假如運算元等價就回傳True。3 == var1 +

"3" == var1

+ 3 == '3'
不等於 (!=)假如運算元等價就回傳True。var1 != 4
+ var2 != "3"
嚴格等於 (===)假如運算元具有相同型態且等價則回傳True。參考 {{jsxref("Object.is")}} 及 JS中的等價性3 === var1
嚴格不等於 (!==)假如運算元具有相同型態但不等價,或是具有不同型態,回傳True。var1 !== "3"
+ 3 !== '3'
大於 (>)假如左方運算元大於右方運算元,回傳True。var2 > var1
+ "12" > 2
大於或等於 (>=)假如左方運算元大於或等於右方運算元,回傳True。var2 >= var1
+ var1 >= 3
小於 (<)假如左方運算元小於右方運算元,回傳True。var1 < var2
+ "2" < 12
小於或等於 (<=)假如左方運算元小於或等於右方運算元,回傳True。var1 <= var2
+ var2 <= 5
+ +
+

筆記: (=>)不是運算子,是 箭頭函式。

+
+ +

算術運算子

+ +

算術運算子 以 數值 (文字或變數也可以)作為其運算元,並回傳單一數值。最常見的算術運算元是 加法 (+),減法 (-), 乘法 (*),及除法 (/)。 這些運算子在大多數程式語言中功能相同 (比較特別的是,在除數為0時 {{jsxref("Infinity")}})。例如:

+ +
1 / 2; // 0.5
+1 / 2 == 1.0 / 2.0; // 會是 true
+
+ +

除了標準的算術運算子外 (+, -, * /), JavaScript 提供以下表中的算術運算子:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
算術運算子
運算子描述範例
取餘數 (%)二元運算子。回傳兩個運算元相除後的餘數。12 % 5 回傳 2.
增加 (++)一元運算子。 將運算元增加1。假如使用在運算元之前 (++x),會運算元回傳增加1後的值;假如使用在運算元之後。 (x++) 會回傳運算元加1前的值。假如 x是 3,那 ++x 將把 x 設定為 4 並回傳 4,而 x++ 會回傳 3 , 接著才把 x 設定為 4。
減少 (--)一元運算子。 將運算元減少1。回傳值的情況與 增加運算元 相同。假如 x是 3,那 --x 將把 x 設定為 2 並回傳 2,而 x-- 會回傳 3 , 接著才把 x 設定為 2。
(一元運算子)減號 (-)一元運算子。回傳運算元的負數。假如x是3,-x 回傳 -3。
(一元運算子)加號 (+)一元運算子。嘗試將運算元轉換成數字,假如它還不是數字的話。+"3" 回傳 3
+ +true 回傳 1.
指數運算子 (**) {{experimental_inline}}計算以 a 為底的 b 次方, 也就是, ab2 ** 3 回傳 8.
+ 10 ** -1 回傳 0.1.
+ +

位元運算子

+ +

位元運算子 把運算元當作 32 位元的集合來看待 (0和1), 而不是十進位,十六進位,或八進位。例如,十進位數字 9 以二進位表示就是  1001。 位元運算子將運算元以上述二進位的形式處理,但是回傳 Javascript 中的數字類型值。

+ +

下列表總結了 JavaScript' 中的位元運算子。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
位元運算子
運算子用法描述
位元 ANDa & b回傳兩個運算元對於每個bit做AND的結果。
位元 ORa | b回傳兩個運算元對於每個bit做OR的結果。
位元 XORa ^ b回傳兩個運算元對於每個bit做XOR的結果。
位元 NOT~ a將運算元中的每個bit反轉(1->0,0->1)。
左移a << ba 的每個bit向左移動 b 個bits,空餘的位數以0填滿。
有號右移a >> ba 的每個bit向右移動 b 個bits,空餘位數以最高位補滿。
以0填充的右移a >>> ba 的每個bit向右移動 b 個bits,空餘的位數以0填滿。
+ +

位元邏輯運算子

+ +

概念上,位元邏輯運算子運作過程如下:

+ + + +

例如, 9 的二元表示法是 1001, 15 的二元表示法是 1111。因此,在使用位元運算子的時候,結果如下:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
位元運算子範例
運算式結果二元描述式
15 & 991111 & 1001 = 1001
15 | 9151111 | 1001 = 1111
15 ^ 961111 ^ 1001 = 0110
~15-16~00000000...00001111 = 11111111...11110000
~9-10~00000000...00001001 = 11111111...11110110
+ +

注意,在使用 位元NOT 運算子時, 所有的32個bit都被進行NOT了,包含最左邊用來描述正負數的位元(two's-complement representation)。

+ +

位元移動運算子

+ +

位元移動運算子需要兩個運算元: 第一個是運算的目標,第二個是要移動的位元數。移動的方向取決於使用的運算子。

+ +

移動運算子會將運算元轉換成32 bits的整數,並且會回傳與左方運算元相同的型態。

+ +

移動運算子在下表被列出.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
位元移動運算子
運算子描述範例
左移
+ (<<)
這個運算子會將第 一個運算元的每個bit向左移動 第二個運算元所指定的bit數量。左邊超出的位數會被捨棄,右邊空出的位數以0補齊。9<<2 得到 36,因為1001 向左移動 2 bits 會得到 100100, 也就是二進位的 36。
有號右移 (>>)這個運算子會將第 一個運算元的每個bit向右移動 第二個運算元所指定的bit數量。右邊超出的位數會被捨棄,左邊空出的位數以最高位補齊。9>>2 得到 2,因為 1001 向右移動 2 bits 會得到 10,也就是二進位的 2。 相同的, -9>>2 會得到 -3,因為最高位用來表示正負號的bit被保留了。
以0填充的右移 (>>>)這個運算子會將第 一個運算元的每個bit向右移動 第二個運算元所指定的bit數量。右邊超出的位數會被捨棄,左邊空出的位數以0補齊。19>>>2 得到 4, 因為 10011 向右移動 2 bits 會得到 100,是二進位的 4。對於非負的數字而言, 以0填充的右移 會得到和 有號右移相同的結果。
+ +

邏輯運算子

+ +

邏輯運算子 通常被用於布林(邏輯)值; 使用於 布林(邏輯)值時, 它們會回傳布林型態的值。 然而,&&|| 運算子實際上是回傳兩指定運算元之一,因此用於非布林型態值時,它可能會回傳一個非布林型態的值。 邏輯運算子將在下表中被詳細解釋。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Logical operators
OperatorUsageDescription
邏輯 AND (&&)運算式1 && 運算式2假如 運算式1 可以被轉換成 false的話,回傳 運算式1; 否則,回傳 運算式2。 因此,&&只有在 兩個運算元都是True 時才會回傳 True,否則回傳 false
邏輯 OR (||)運算式1 || 運算式2假如 運算式1 可以被轉換成 true的話,回傳 運算式1; 否則,回傳 運算式2。 因此,||在 兩個運算元有任一個是True 時就會回傳 True,否則回傳 false
邏輯 NOT (!)!運算式假如單一個運算元能被轉換成True時,回傳false , 不然回傳 true
+ +

可以被轉換為 false 的運算式是 null, 0, NaN, 空字串 (""),或 未定義。

+ +

下面是 && (邏輯 AND) 運算子 的範例。

+ +
var a1 =  true && true;     // t && t 回傳 true
+var a2 =  true && false;    // t && f 回傳 false
+var a3 = false && true;     // f && t 回傳 false
+var a4 = false && (3 == 4); // f && f 回傳 false
+var a5 = "Cat" && "Dog";    // t && t 回傳 Dog
+var a6 = false && "Cat";    // f && t 回傳 false
+var a7 = "Cat" && false;    // t && f 回傳 false
+
+ +

下列是 || (邏輯 OR) 運算子的範例。

+ +
var o1 =  true || true;     // t || t 回傳 true
+var o2 = false || true;     // f || t 回傳 true
+var o3 =  true || false;    // t || f 回傳 true
+var o4 = false || (3 == 4); // f || f 回傳 false
+var o5 = 'Cat' || 'Dog';    // t || t 回傳 Cat
+var o6 = false || 'Cat';    // f || t 回傳 Cat
+var o7 = 'Cat' || false;    // t || f 回傳 Cat
+
+ +

下列是 ! (邏輯 NOT) 運算子的範例。

+ +
var n1 = !true;  // !t 回傳 false
+var n2 = !false; // !f 回傳 true
+var n3 = !'Cat'; // !t 回傳 false
+
+ +

短路解析

+ +

邏輯運算式是由左向右解析的, 他們會以下列規則嘗試進行 短路解析:

+ + + +

這些規則保證 解析總是正確的。 值得注意的地方是,剩餘部分的運算式並沒有被解析,所以不會占用任何效能。

+ +

字串運算子

+ +

除了作為比較運算子之外, 運算子 (+) 也能用於字串,將兩字串接在一起,並回傳接在一起後的結果。

+ +

例如,

+ +
console.log('我的 ' + '字串'); // 會印出 字串 "我的字串"。
+ +

簡化的設定運算子 += 也能用於串接字串。

+ +

例如,

+ +
var mystring = '字';
+mystring += '母'; // 得到 "字母" 並賦與給變數 mystring.
+ +

條件(三元)運算子

+ +

條件運算子 是 JavaScript 中唯一需要三個運算元的運算子。 這個運算子接受兩個運算元作為值且一個運算元作為條件。 語法是:

+ +
條件 ? 值1 : 值2
+
+ +

如果 條件 為 true,運算子回傳 值1, 否則回傳 值2。 你可以在任何使用標準運算子的地方改用 條件運算子。

+ +

例如,

+ +
var status = (age >= 18) ? '成人' : '小孩';
+
+ +

這個陳述句會將 "成人" 賦與給變數 status 假如 age 大於等於18。 否則,會將 "小孩" 賦與給變數 status

+ +

逗號運算子

+ +

逗點運算子 (,) 作用是解析兩個運算元並回傳後面那個運算元的值。 這個運算子通常用於for迴圈內部,讓多個變數能在每次迴圈中被更新。

+ +

例如,假如 a 是一個有十個物件在裡面的二維陣列, 下面的程式中就使用了逗點運算子來同時更新兩個變數。 這段程式碼會印出陣列中所有對角線上的物件:

+ +
for (var i = 0, j = 9; i <= j; i++, j--)
+  console.log('a[' + i + '][' + j + ']= ' + a[i][j]);
+
+ +

一元運算子

+ +

一元運算 是只需要一個運算元的運算。

+ +

delete

+ +

delete 運算子會刪除物件,物件的性質,或是陣列中指定 index 的物件。 語法是:

+ +
delete 物件名稱;
+delete 物件名稱.性質;
+delete 物件名稱[索引];
+delete 性質; // 只有在 with 陳述句中可以使用
+
+ +

物件名稱 是物件的名稱, 性質 是物件中的一個特性, 索引 是用來表示物件在陣列中位置的一個整數。

+ +

第四種形式只有在 with 陳述句中可用, 用來刪除物件中的一個特性。

+ +

你可以用 delete 運算子來刪除隱式宣告的變數, 但不適用於使用 var 宣告的變數。

+ +

假如 delete 運算子使用成功, 它會將物件 或是 物件的特性設定為 未定義。 delete 運算子會在運算成功時回傳 true ,失敗時回傳 false

+ +
x = 42;
+var y = 43;
+myobj = new Number();
+myobj.h = 4;    // 建立特性 h
+delete x;       // 回傳 true (只有在隱式宣告時能被刪除)
+delete y;       // 回傳 false (在使用 var 宣告時無法刪除)
+delete Math.PI; // 回傳 false (不能刪除內建定義的特性)
+delete myobj.h; // 回傳 true (可以刪除使用者自定義的特性)
+delete myobj;   // 回傳 true (在隱式宣告時可被刪除)
+
+ +
刪除陣列元素
+ +

在你刪除了陣列中的一個元素後, 陣列的長度並不會改變。 例如, 假如你刪除 a[3]a[4] 依然是 a[4]a[3] 為 未定義。

+ +

當使用 delete 運算子刪除陣列中的一個元素後, 那個元素便不再存在於陣列中了。 在下面的程式中, trees[3] 被用 delete 移除了。然而, trees[3] 的記憶體位址仍可用並且會回傳 未定義。

+ +
var trees = ['redwood', 'bay', 'cedar', 'oak', 'maple'];
+delete trees[3];
+if (3 in trees) {
+  // 不會執行到這裡
+}
+
+ +

假如你希望給予陣列元素 未定義 的值, 你可以直接使用 undefined 關鍵字而不是使用 delete 運算子。 下列範例中, trees[3] 被指定了 undefined, 然而陣列元素依然存在:

+ +
var trees = ['redwood', 'bay', 'cedar', 'oak', 'maple'];
+trees[3] = undefined;
+if (3 in trees) {
+  // 會執行這裡
+}
+
+ +

typeof

+ +

typeof 運算子 能以下列任一方式使用:

+ +
typeof 運算元
+typeof (運算元)
+
+ +

typeof 運算子會回傳代表運算元類型的 字串。 運算元能是字串,變數,關鍵字,或是會回傳型態的物件。 括號是可有可無的。

+ +

假設你定義了以下這些變數:

+ +
var myFun = new Function('5 + 2');
+var shape = 'round';
+var size = 1;
+var today = new Date();
+
+ +

typeof 運算子會回傳下列結果:

+ +
typeof myFun;       // 回傳 "function"
+typeof shape;       // 回傳 "string"
+typeof size;        // 回傳 "number"
+typeof today;       // 回傳 "object"
+typeof doesntExist; // 回傳 "undefined"
+
+ +

對於 truenull關鍵字, typeof 運算子會回傳下列結果:

+ +
typeof true; // 回傳 "boolean"
+typeof null; // 回傳 "object"
+
+ +

對於字串或數字, typeof 運算子會回傳下列結果:

+ +
typeof 62;            // 回傳 "number"
+typeof 'Hello world'; // 回傳 "string"
+
+ +

對於特性,typeof 運算子會回傳 特性的值的類型:

+ +
typeof document.lastModified; // 回傳 "string"
+typeof window.length;         // 回傳 "number"
+typeof Math.LN2;              // 回傳 "number"
+
+ +

對於 方法 及 函式, typeof 運算子會回傳下列結果:

+ +
typeof blur;        // 回傳 "function"
+typeof eval;        // 回傳 "function"
+typeof parseInt;    // 回傳 "function"
+typeof shape.split; // 回傳 "function"
+
+ +

對於內建定義的物件, typeof 運算子會回傳下列結果:

+ +
typeof Date;     // 回傳 "function"
+typeof Function; // 回傳 "function"
+typeof Math;     // 回傳 "object"
+typeof Option;   // 回傳 "function"
+typeof String;   // 回傳 "function"
+
+ +

void

+ +

void 運算子 能以下列任一方式使用:

+ +
void (運算式)
+void 運算式
+
+ +

void 運算子會解析運算式而不回傳任何值。 運算式 是 JavaScript 中要解析的對象。 括號是可有可無的,但是建議使用。

+ +

你可以使用 void 運算子來解析超連結中的運算式。 運算式會被解析而不會在當前頁面被印出。

+ +

下列範例是一個在點擊時甚麼都不做的超連結。 當使用者點擊連結時, void(0) 被解析為 未定義, 而甚麼都不會發生。

+ +
<a href="javascript:void(0)">點擊這裡,甚麼都不會發生</a>
+
+ +

下列範例是一個在使用者點擊時傳送表單的超連結。

+ +
<a href="javascript:void(document.form.submit())">
+點擊以送出</a>
+ +

關係運算子

+ +

關係運算子比較兩運算元並基於比較結果回傳布林值。

+ +

in

+ +

in 運算子 在指定性質存在於物件中時回傳 true 。 語法是:

+ +
性質名稱 in 物件名稱
+
+ +

性質名稱 可以是 字串或數字,或是陣列的索引, 且 物件名稱 是物件的名稱。

+ +

下列範例示範了 in 運算子的一些用法。

+ +
// 陣列
+var trees = ['redwood', 'bay', 'cedar', 'oak', 'maple'];
+0 in trees;        // 回傳 true
+3 in trees;        // 回傳 true
+6 in trees;        // 回傳 false
+'bay' in trees;    // 回傳 false (你必須指定 索引,
+                   // 而不是 索引所對應的元素)
+'length' in trees; // 回傳 true (length 是陣列的性質之一)
+
+// 內建物件
+'PI' in Math;          // 回傳 true
+var myString = new String("coral");
+'length' in myString;  // 回傳 true
+
+// 自訂義物件
+var mycar = { make: 'Honda', model: 'Accord', year: 1998 };
+'make' in mycar;  // 回傳 true
+'model' in mycar; // 回傳 true
+
+ +

instanceof

+ +

instanceof 運算子 在 指定物件 具有 指定的物件型態 時回傳 true。 語法是:

+ +
物件名稱 instanceof 物件類型
+
+ +

物件名稱 是用來與 物件類型 比較的物件的名字, 物件類型 是物件的類型, 例如 {{jsxref("Date")}} 或 {{jsxref("Array")}}。

+ +

當你需要在程式執行中確認物件的形態時,你可以使用 instanceof 運算子。 例如,當捕捉到例外時, 你可以依照例外的類型來決定用來處理意外的程式碼。

+ +

例如,下列程式碼使用 instanceof 來判斷變數 theDay 是不是 Date 類型的物件。 因為 theDayDate 類型的物件, 所以if 陳述中的陳述句會被執行。

+ +
var theDay = new Date(1995, 12, 17);
+if (theDay instanceof Date) {
+  // 會被執行的陳述
+}
+
+ +

運算子優先級

+ +

運算子優先級決定運算子被使用於運算元的先後順序。 你也可以使用括號來強制指定優先級。

+ +

下列表格列出了運算子的優先級, 從高到低。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
運算子優先級
運算子類型屬於該類別的運算子
成員. []
呼叫/建立 實例() new
反向/增加! ~ - + ++ -- typeof void delete
乘法/除法* / %
加法/減法+ -
位元移動<< >> >>>
關係運算子< <= > >= in instanceof
相等性== != === !==
位元 and&
位元 xor^
位元 or|
邏輯 and&&
邏輯 or||
條件運算子?:
指定運算子= += -= *= /= %= <<= >>= >>>= &= ^= |=
逗點運算子,
+ +

這個表格更詳細的版本,解釋了運算子的更多細節和關聯性, 可以在 JavaScript 參考 中被找到。

+ +

運算式

+ +

運算式是任何一段可以取得一個值的程式碼。

+ +

任何合乎語法的運算式都能取得一個值,概念上, 有兩種不同型態的運算式: 有副作用的 (例如: 將一個值指定給一個變數) 以及只為了取得值而解析的運算式。

+ +

運算式 x = 7 是上述的第一種類型。 這個使用 = 運算子的運算式會將數值 7 賦與給 x。 運算式本身也會被解析為 7。

+ +

運算式 3 + 4 是上述的第二種類型。 這個運算式使用 + 運算子把 3 和 4 加起來,而不指定給任何變數。
+
+ JavaScript 運算式有下列幾種種類:

+ + + +

主流運算式

+ +

JavaScript 基本的關鍵字及運算式。

+ +

this

+ +

this 關鍵字 能取得當前所在物件。 一般而言, this 能取得呼叫處所在的物件。 你可以使用 點 或是 中括號 來取用該物件中的特性:

+ +
this['特性名稱']
+this.特性名稱
+
+ +

以下定義一個叫做 validate 的函式,比較物件中特性 value 與傳入的兩變數:

+ +
function validate(obj, lowval, hival){
+  if ((obj.value < lowval) || (obj.value > hival))
+    console.log('不可用的值!');
+}
+
+ +

你可以在表單的 onChange event handler 中呼叫 validate 函式, 並以 this 來傳入表單的元素, 範例如下:

+ +
<p>請輸入一介於18 與 99 的數字:</p>
+<input type="text" name="age" size=3 onChange="validate(this, 18, 99);">
+
+ +

分組運算子

+ +

分組運算子 ( ) 控制了運算子的優先順序。 例如,你可以覆寫先乘除,後加減的優先順序,使其變成先加減,後乘除。

+ +
var a = 1;
+var b = 2;
+var c = 3;
+
+// 預設運算級
+a + b * c     // 7
+// 預設的結果
+a + (b * c)   // 7
+
+// 現在複寫運算級
+// 變成先進行加法,後乘法了
+(a + b) * c   // 9
+
+// 結果
+a * c + b * c // 9
+
+ +

解析

+ +

解析是 JavaScript 中的一個實驗性功能, 在未來版本的 ECMAScript 計畫被導入。有兩種不同類型的解析:

+ +
+
{{experimental_inline}} {{jsxref("Operators/Array_comprehensions", "[for (x of y) x]")}}
+
陣列解析。
+
{{experimental_inline}} {{jsxref("Operators/Generator_comprehensions", "(for (x of y) y)")}}
+
產生器解析。
+
+ +

解析在許多程式語言中都存在,允許你快速地基於現存陣列產生新的陣列,例如:

+ +
[for (i of [ 1, 2, 3 ]) i*i ];
+// [ 1, 4, 9 ]
+
+var abc = [ 'A', 'B', 'C' ];
+[for (letters of abc) letters.toLowerCase()];
+// [ 'a', 'b', 'c' ]
+ +

左側運算式

+ +

左側是指定值的對象。

+ +

new

+ +

你可以使用 new 運算子 來建立一個使用者自定義物件或內建物件的實例。 用法如下:

+ +
var 物件名稱 = new 物件型態([參數1, 參數2, ..., 參數N]);
+
+ +

super

+ +

super 關鍵字 用於呼叫物件的父物件中的函式。 在使用 類別 來呼叫父類別的建構子時很實用,例如:

+ +
super([參數]); // 呼叫父物件的建構子.
+super.父物件的函式([參數]);
+
+ +

展開運算子

+ +

展開運算子能將運算式展開於需要多個參數的地方 (如函式呼叫) 或是需要多個元素 (如陣列字串常數) 的地方。

+ +

範例: 現在你想要用已存在的一個陣列做為新的一個陣列的一部份,當字串常數不再可用而你必須使用指令式編程,也就是使用,一連串的 push, splice, concat,等等。 展開運算子能讓過程變得更加簡潔:

+ +
var parts = ['肩膀', '膝蓋'];
+var lyrics = ['頭', ...parts, '和', '腳趾'];
+ +

相同的,展開運算子也適用於函式呼叫:

+ +
function f(x, y, z) { }
+var args = [0, 1, 2];
+f(...參數);
+ +
{{PreviousNext("Web/JavaScript/Guide/Functions", "Web/JavaScript/Guide/Numbers_and_dates")}}
diff --git a/files/zh-tw/web/javascript/guide/functions/index.html b/files/zh-tw/web/javascript/guide/functions/index.html new file mode 100644 index 0000000000..03866506d1 --- /dev/null +++ b/files/zh-tw/web/javascript/guide/functions/index.html @@ -0,0 +1,442 @@ +--- +title: 函式 +slug: Web/JavaScript/Guide/Functions +translation_of: Web/JavaScript/Guide/Functions +--- +

{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Loops_and_iteration", "Web/JavaScript/Guide/Expressions_and_Operators")}}

+ +

函式是構成javascript的基本要素之一。一個函式本身就是一段JavaScript程序—含用於執行某一個任務或計算的語法。要呼叫某一個函式之前,你必需先在這個函式欲執行的scope中定義它。

+ +

定義函式

+ +

一個函式的定義由一系列的函式關鍵詞組成, 依次為:

+ + + +

 

+ +

例如,以下的程式碼定義了一個名為square的簡單函式:

+ +
+
function square(number) {
+  return number * number;
+}
+
+
+ +

函式square有一個參數,叫作number。這個函式只有一行程式碼,它會回傳number自乘的結果。函式return 語法描述函式的返回值。

+ +
return number * number;
+
+ +

原始參數(例如一個數字)被作為值傳遞給函式,如果呼叫的函式改變了這個參數的值,不會影響到函式外部的原始變數

+ +

如果傳遞一個物件(例如 Array 或自定義的其它物件)作為參數,而函式改變了這個物件的屬性,這樣的改變對函式外部是有作用的(因為是傳遞物件的位址),如下面的例子所示

+ +
function myFunc(theObject) {
+  theObject.make = "Toyota";
+}
+
+var mycar = {make: "Honda", model: "Accord", year: 1998},
+    x,
+    y;
+
+x = mycar.make;     // x 的值為 "Honda"
+
+myFunc(mycar);
+y = mycar.make;     // y 的值為 "Toyota"
+                    // (屬性 make 被 function 改變)
+
+ +

請注意,重新給參數指定一個對象(物件),並不會對函式的外部有任何影響,因為這樣只是改變了參數的值,而不是改變了對象的一個屬性值:

+ +
function myFunc(theObject) {
+  theObject = {make: "Ford", model: "Focus", year: 2006};
+}
+
+var mycar = {make: "Honda", model: "Accord", year: 1998},
+    x,
+    y;
+
+x = mycar.make;     // x 的值為 "Honda"
+
+myFunc(mycar);
+y = mycar.make;     // y 的值還是 "Honda" 
+ +

儘管上述函式定義都是用的是陳述式,函式也同樣可以由函式表達式來定義。這樣的函式可以是匿名的;它不必有名稱。例如,上面提到的函式square也可這樣來定義:

+ +
var square = function(number) {return number * number};
+var x = square(4) //x 的值為 16
+ +
+
必要時,函式名稱可與函式表達式同時存在,並且可以用於在函式內部代指其本身(遞迴):
+ +
 
+
+ +
var factorial = function fac(n) {return n<2 ? 1 : n*fac(n-1)};
+
+console.log(factorial(3));
+
+ +

函式表達式在將函式作為一個參數傳遞給其它函式時十分方便。下面的例子展示了一個叫map的函式如何​​被定義,而後呼叫一個匿名函式作為其第一個參數:

+ +
function map(f,a) {
+  var result = [], // Create a new Array
+      i;
+  for (i = 0; i != a.length; i++)
+    result[i] = f(a[i]);
+  return result;
+}
+
+ +

下面的程式碼呼叫map函式並將一個匿名函式傳入作為第一個參數:

+ +
map(function(x) {return x * x * x}, [0, 1, 2, 5, 10]);
+// 結果會回傳 [0, 1, 8, 125, 1000]
+
+ +

除了上述的定義方式以外,我們也可以透過 Function constructor 來定義, 類似 eval().

+ +

呼叫函式

+ +

定義一個函式並不會自動的執行它。定義了函式僅僅是賦予函式以名稱並明確函式被呼叫時該做些什麼。呼叫函式才會以給定的參數真正執行這些動作。例如,一旦你定義了函式square,你可以如下這樣呼叫它:

+ +
square(5);
+
+ +

上述程式碼把5傳遞給square函式函式執行完會回傳25。

+ +

函式必須在呼叫區塊的可視範圍內,但函數也可以宣告在使用處的下面,如下列範例:

+ +
console.log(square(5));
+/* ... */
+function square(n){return n*n}
+
+ +

The scope of a function is the function in which it is declared, or the entire program if it is declared at the top level. Note that this works only when defining the function using the above syntax (i.e. function funcName(){}). The code below will not work.

+ +
console.log(square(5));
+square = function (n) {
+  return n * n;
+}
+
+ +

The arguments of a function are not limited to strings and numbers. You can pass whole objects to a function, too. The show_props function (defined in Working with Objects) is an example of a function that takes an object as an argument.

+ +

A function can be recursive; that is, it can call itself. For example, here is a function that computes factorials recursively:

+ +
function factorial(n){
+  if ((n == 0) || (n == 1))
+    return 1;
+  else
+    return (n * factorial(n - 1));
+}
+
+ +

You could then compute the factorials of one through five as follows:

+ +
var a, b, c, d, e;
+a = factorial(1); // a gets the value 1
+b = factorial(2); // b gets the value 2
+c = factorial(3); // c gets the value 6
+d = factorial(4); // d gets the value 24
+e = factorial(5); // e gets the value 120
+
+ +

There are other ways to call functions. There are often cases where a function needs to be called dynamically, or the number of arguments to a function vary, or in which the context of the function call needs to be set to a specific object determined at runtime. It turns out that functions are, themselves, objects, and these objects in turn have methods (see the Function object). One of these, the apply() method, can be used to achieve this goal.

+ +

Function scope

+ +

Variables defined inside a function cannot be accessed from anywhere outside the function, because the variable is defined only in the scope of the function. However, a function can access all variables and functions defined inside the scope in which it is defined. In other words, a function defined in the global scope can access all variables defined in the global scope. A function defined inside another function can also access all variables defined in it's parent function and any other variable to which the parent function has access.

+ +
// The following variables are defined in the global scope
+var num1 = 20,
+    num2 = 3,
+    name = "Chamahk";
+
+// This function is defined in the global scope
+function multiply() {
+  return num1 * num2;
+}
+
+multiply(); // Returns 60
+
+// A nested function example
+function getScore () {
+  var num1 = 2,
+      num2 = 3;
+
+  function add() {
+    return name + " scored " + (num1 + num2);
+  }
+
+  return add();
+}
+
+getScore(); // Returns "Chamahk scored 5"
+
+ +

閉包

+ +

閉包是 JavaScript 最強大的特性之一。JavaScript 允許巢狀函式(nesting of functions)並給予內部函式完全訪問(full access)所有變數、與外部函式定義的函式(還有所有外部函式內的變數與函式)不過,外部函式並不能訪問內部函式的變數與函式。這保障了內部函式的變數安全。另外,由於內部函式能訪問外部函式定義的變數與函式,將存活得比外部函式還久。A closure is created when the inner function is somehow made available to any scope outside the outer function.

+ +
var pet = function(name) {          // The outer function defines a variable called "name"
+      var getName = function() {
+        return name;                // The inner function has access to the "name" variable of the outer function
+      }
+
+      return getName;               // Return the inner function, thereby exposing it to outer scopes
+    },
+    myPet = pet("Vivie");
+
+myPet();                            // Returns "Vivie"
+
+ +

It can be much more complex than the code above. An object containing methods for manipulating the inner variables of the outer function can be returned.

+ +
var createPet = function(name) {
+  var sex;
+
+  return {
+    setName: function(newName) {
+      name = newName;
+    },
+
+    getName: function() {
+      return name;
+    },
+
+    getSex: function() {
+      return sex;
+    },
+
+    setSex: function(newSex) {
+      if(typeof newSex == "string" && (newSex.toLowerCase() == "male" || newSex.toLowerCase() == "female")) {
+        sex = newSex;
+      }
+    }
+  }
+}
+
+var pet = createPet("Vivie");
+pet.getName();                  // Vivie
+
+pet.setName("Oliver");
+pet.setSex("male");
+pet.getSex();                   // male
+pet.getName();                  // Oliver
+
+ +

In the codes above, the name variable of the outer function is accessible to the inner functions, and there is no other way to access the inner variables except through the inner functions. The inner variables of the inner function act as safe stores for the inner functions. They hold "persistent", yet secure, data for the inner functions to work with. The functions do not even have to be assigned to a variable, or have a name.

+ +
var getCode = (function(){
+  var secureCode = "0]Eal(eh&2";    // A code we do not want outsiders to be able to modify...
+
+  return function () {
+    return secureCode;
+  };
+})();
+
+getCode();    // Returns the secret code
+
+ +

There are, however, a number of pitfalls to watch out for when using closures. If an enclosed function defines a variable with the same name as the name of a variable in the outer scope, there is no way to refer to the variable in the outer scope again.

+ +
var createPet = function(name) {  // Outer function defines a variable called "name"
+  return {
+    setName: function(name) {    // Enclosed function also defines a variable called "name"
+      name = name;               // ??? How do we access the "name" defined by the outer function ???
+    }
+  }
+}
+
+ +

The magical this variable is very tricky in closures. They have to be used carefully, as what this refers to depends completely on where the function was called, rather than where it was defined. An excellent and elaborate article on closures can be found here.

+ +

Using the arguments object

+ +

The arguments of a function are maintained in an array-like object. Within a function, you can address the arguments passed to it as follows:

+ +
arguments[i]
+
+ +

where i is the ordinal number of the argument, starting at zero. So, the first argument passed to a function would be arguments[0]. The total number of arguments is indicated by arguments.length.

+ +

Using the arguments object, you can call a function with more arguments than it is formally declared to accept. This is often useful if you don't know in advance how many arguments will be passed to the function. You can use arguments.length to determine the number of arguments actually passed to the function, and then access each argument using the arguments object.

+ +

For example, consider a function that concatenates several strings. The only formal argument for the function is a string that specifies the characters that separate the items to concatenate. The function is defined as follows:

+ +
function myConcat(separator) {
+   var result = "", // initialize list
+       i;
+   // iterate through arguments
+   for (i = 1; i < arguments.length; i++) {
+      result += arguments[i] + separator;
+   }
+   return result;
+}
+
+ +

You can pass any number of arguments to this function, and it concatenates each argument into a string "list":

+ +
// returns "red, orange, blue, "
+myConcat(", ", "red", "orange", "blue");
+
+// returns "elephant; giraffe; lion; cheetah; "
+myConcat("; ", "elephant", "giraffe", "lion", "cheetah");
+
+// returns "sage. basil. oregano. pepper. parsley. "
+myConcat(". ", "sage", "basil", "oregano", "pepper", "parsley");
+
+ +

Please note that the arguments variable is "array-like", but not an array. It is array-like in that is has a numbered index and a length property. However, it does not possess all of the array-manipulation methods.

+ +

See the Function object in the JavaScript Reference for more information.

+ +

Predefined functions

+ +

JavaScript has several top-level predefined functions:

+ + + +

The following sections introduce these functions. See the JavaScript Reference for detailed information on all of these functions.

+ +

eval Function

+ +

The eval function evaluates a string of JavaScript code without reference to a particular object. The syntax of eval is:

+ +
eval(expr);
+
+ +

where expr is a string to be evaluated.

+ +

If the string represents an expression, eval evaluates the expression. If the argument represents one or more JavaScript statements, eval performs the statements. The scope of eval code is identical to the scope of the calling code. Do not call eval to evaluate an arithmetic expression; JavaScript evaluates arithmetic expressions automatically.

+ +

isFinite function

+ +

The isFinite function evaluates an argument to determine whether it is a finite number. The syntax of isFinite is:

+ +
isFinite(number);
+
+ +

where number is the number to evaluate.

+ +

If the argument is NaN, positive infinity or negative infinity, this method returns false, otherwise it returns true.

+ +

The following code checks client input to determine whether it is a finite number.

+ +
if(isFinite(ClientInput)){
+   /* take specific steps */
+}
+
+ +

isNaN function

+ +

The isNaN function evaluates an argument to determine if it is "NaN" (not a number). The syntax of isNaN is:

+ +
isNaN(testValue);
+
+ +

where testValue is the value you want to evaluate.

+ +

The parseFloat and parseInt functions return "NaN" when they evaluate a value that is not a number. isNaN returns true if passed "NaN," and false otherwise.

+ +

The following code evaluates floatValue to determine if it is a number and then calls a procedure accordingly:

+ +
var floatValue = parseFloat(toFloat);
+
+if (isNaN(floatValue)) {
+   notFloat();
+} else {
+   isFloat();
+}
+
+ +

parseInt and parseFloat functions

+ +

The two "parse" functions, parseInt and parseFloat, return a numeric value when given a string as an argument.

+ +

The syntax of parseFloat is:

+ +
parseFloat(str);
+
+ +

where parseFloat parses its argument, the string str, and attempts to return a floating-point number. If it encounters a character other than a sign (+ or -), a numeral (0-9), a decimal point, or an exponent, then it returns the value up to that point and ignores that character and all succeeding characters. If the first character cannot be converted to a number, it returns "NaN" (not a number).

+ +

The syntax of parseInt is:

+ +
parseInt(str [, radix]);
+
+ +

parseInt parses its first argument, the string str, and attempts to return an integer of the specified radix (base), indicated by the second, optional argument, radix. For example, a radix of ten indicates to convert to a decimal number, eight octal, sixteen hexadecimal, and so on. For radixes above ten, the letters of the alphabet indicate numerals greater than nine. For example, for hexadecimal numbers (base 16), A through F are used.

+ +

If parseInt encounters a character that is not a numeral in the specified radix, it ignores it and all succeeding characters and returns the integer value parsed up to that point. If the first character cannot be converted to a number in the specified radix, it returns "NaN." The parseInt function truncates the string to integer values.

+ +

Number and String functions

+ +

The Number and String functions let you convert an object to a number or a string. The syntax of these functions is:

+ +
var objRef;
+objRef = Number(objRef);
+objRef = String(objRef);
+
+ +

objRef 是物件的參照。 Number uses the valueOf() method of the object; String uses the toString() method of the object.

+ +

下列範例將 日期 物件轉換為可讀字串。

+ +
var D = new Date(430054663215),
+    x;
+x = String(D); // x 等於 "星期二 八月 18 04:37:43 GMT-0700  1983"
+
+ +

下列範例將 字串 物件轉換為 數字 物件。

+ +
var str = "12",
+    num;
+num = Number(字串);
+
+ +

使用 DOM 方法 write() 與 JavaScript typeof 運算子.

+ +
var str = "12",
+    num;
+document.write(typeof str);
+document.write("<br/>");
+num = Number(str);
+document.write(typeof num);
+
+ +

escape 與 unescape 函式(JavaScript 1.5後去除)

+ +

escapeunescape 對於非ASCII 字元無法處理。 在 JavaScript 1.5 之後改用 encodeURI, decodeURI, encodeURIComponent, 與 decodeURIComponent.

+ +

escapeunescape 用於編碼與解碼字串。 escape 函式回傳十六進位編碼。 unescape 函式會將十六進位的編碼轉換回 ASCII 字串。

+ +

這些函式的語法是:

+ +
escape(字串);
+unescape(字串);
+
+ +

這些函式常被用於伺服器後端中處理姓名等資料。

diff --git a/files/zh-tw/web/javascript/guide/grammar_and_types/index.html b/files/zh-tw/web/javascript/guide/grammar_and_types/index.html new file mode 100644 index 0000000000..ac059a7f24 --- /dev/null +++ b/files/zh-tw/web/javascript/guide/grammar_and_types/index.html @@ -0,0 +1,697 @@ +--- +title: 語法與型別 +slug: Web/JavaScript/Guide/Grammar_and_types +tags: + - Guide + - JavaScript +translation_of: Web/JavaScript/Guide/Grammar_and_types +--- +
{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Introduction", "Web/JavaScript/Guide/Control_flow_and_error_handling")}}
+ +

本章討論 JavaScript 的基本語法與基礎資料類型、包括變數、常數、字元常數

+ +

基礎知識

+ +

JavaScript 許多基本語法借鑒自 Java,C或是C++,但亦受 Awk、Perl 和 Python 的影響。

+ +

JavaScript 是 Case-sensitive(區分大小寫)並使用 Unicode 編碼。舉例來說,Früh (德文的"early") 可以當作變數的名稱。

+ +
var Früh = "foobar";
+ +

但變數 früh 並不等於 Früh,因為大小寫對 JavaScript 是有區別的。

+ +

在 JavaScript 中,每行指令被稱為 {{Glossary("Statement", "Statements")}},並用分號(;)分隔。空格、Tab 與換行符號皆被視為空白。JavaScript 的文件會從左到右進行掃描,並轉換成一系列的元素,像是令牌(Token)、控制字符(Control characters)、換行器(line terminators)、註解(Comments)或是空白(Withespace),ECMAScript 也定義了特定的保留字和字面值,並在每個沒有加分號的 Statement 自動加上分號。然而,推薦的作法還是在每個 Statement 的結尾自行加上分號,以防止一些潛在的副作用,如果需要更多資訊,可以參考這篇

+ +

註解(Comments)

+ +

註解語法跟 C++ 和其他語言相同:

+ +
// a one line comment
+
+/* this is a longer,
+   multi-line comment
+ */
+
+/* You can't, however, /* nest comments */ SyntaxError */
+ +

宣告(Declarations)

+ +

JavaScript有三種宣告方式

+ +
+
{{jsxref("Statements/var", "var")}}
+
宣告一個可隨意更改其內容的變數
+
{{jsxref("Statements/let", "let")}}
+
宣告一個可隨意更改其內容的區塊區域變數
+
{{jsxref("Statements/const", "const")}}
+
宣告一個只可讀取的不可變常數
+
+ +

變數(Variables)

+ +

變數(variable)是對值(value)的引用,變數的名稱被稱為 {{Glossary("Identifier", "identifiers")}} 需要遵從一定的規則。

+ +

在 JavaScript 中,變數必須使用字母(letter)、下底線( _)、錢號($)作為開頭;後面的字員組成可以包含數字(0-9)。JavaScript 是區分大小寫(case secsitive)的,大寫字母('A' ~ 'Z')和小寫字母('a' ~ 'z')皆可使用且不相等。

+ +

You can use most of ISO 8859-1 or Unicode letters such as å and ü in identifiers (for more details see this blog post). You can also use the Unicode escape sequences as characters in identifiers.

+ +

Some examples of legal names are Number_hits, temp99, $credit, and _name.

+ +

定義變數

+ +

你可以透過三種方式來定義變數:

+ + + +

變數取值

+ +

變數可以透過 var 或是 let 來定義,如果尚未指定數值給該變數,那麼該變數的值會是 {{jsxref("undefined")}}。如果嘗試去存取未定義的變數,會跳出 {{jsxref("ReferenceError")}} 的例外。

+ +
var a;
+console.log('The value of a is ' + a); // The value of a is undefined
+
+console.log('The value of b is ' + b); // The value of b is undefined
+var b;
+
+console.log('The value of c is ' + c); // Uncaught ReferenceError: c is not defined
+
+let x;
+console.log('The value of x is ' + x); // The value of x is undefined
+
+console.log('The value of y is ' + y); // Uncaught ReferenceError: y is not defined
+let y; 
+ +

你可以利用 undefined 來判斷該變數是否有值,在下面的程式碼的例子中,input 這個變數沒有賦值,if 判斷式會得到 true 的結果。

+ +
var input;
+if (input === undefined) {
+  doThis();
+} else {
+  doThat();
+}
+
+ +

被賦予 undefined 的變數,在被當做布林值的情境下都會被視為 false,以下面的例子來說,程式碼會執行 myFunction,因為 myArrayundefined

+ +
var myArray = [];
+if (!myArray[0]) myFunction();
+
+ +

被賦予 undefined 的變數,在和數值進行運算之後,會被轉成非數值(NaN):

+ +
var a;
+a + 2;  // Evaluates to NaN
+ +

當你對 {{jsxref("null")}} 進行運算,{{jsxref("null")}} 會自動轉換成數值 0,如果當做布林值運算,會被當成 false,舉例來說:

+ +
var n = null;
+console.log(n * 32); // Will log 0 to the console
+
+ +

變數範圍

+ +

當我們在函式外宣告一個變數時,這個變數會是一個全域變數  (global variable), 因為在這份程式文件裡面的所有程式碼都可以使用到這個變數。但當我們只在函式內宣告變數時,這變數是區域變數 (local variable),因為變數只會在函式內被使用到。

+ +

請注意!! 在 ECMAScript 2015 以前的 JavaScript 版本裡,並沒有定義區塊描述 (block statement) 的變數有效範圍。更精確的說,之前版本所定義的變數,其特性相當於全域變數;不只在宣告的區塊或函數裡面有效 ,其變數值還會超出宣告區塊而影響到全部的描述碼。

+ +

從下面例子來看,其輸出結果會是 5。雖然 x 是在 if { } 區塊裡面被宣告的,但卻因為有全域變數的特性,因此溢出大括號而成為後續描述碼的變數值。

+ +
if (true) {
+  var x = 5;
+}
+console.log(x);  // x is 5
+
+ +

接著舉一個 ECMAScript 2015 之後的宣告範例。當使用了 let  這個區域變數宣告方式,變數 y 的有效範圍只有在 if { } 的範圍內,因此輸出結果是 ReferenceError。

+ +
if (true) {
+  let y = 5;
+}
+console.log(y);  // ReferenceError: y is not defined (y沒有被定義)
+
+ +

變數提升

+ +

在JavaScript 中另一件有關變數不常見的事, 是你可引用一個較晚宣告的變數並且不會有異常。這個概念被稱為「提升hoisting)」;從意義上來說明,變數在 JavaScript 中是「被提升(hoisted)」或「被抬至(lifted)」到函式(function)或陳述式(statement)的頂部。
+ 然而,被提升(hoisted)的變數將返回一個未定義的值(undefined)。所以即使你在使用或者引用這個變數之後才宣告和初始化它,它仍然會返回它是一個未定義的值(undefined)。

+ +
/**
+ * Example 1
+ */
+console.log(x === undefined); // true
+var x = 3;
+
+/**
+ * Example 2
+ */
+// will return a value of undefined
+var myvar = 'my value';
+
+(function() {
+  console.log(myvar); // undefined
+  var myvar = 'local value';
+})();
+
+ +

上面的例子可以轉譯成如下相同的程式:

+ +
/**
+ * Example 1
+ */
+var x;
+console.log(x === undefined); // true
+x = 3;
+
+/**
+ * Example 2
+ */
+var myvar = 'my value';
+
+(function() {
+  var myvar;
+  console.log(myvar); // undefined
+  myvar = 'local value';
+})();
+
+ +


+ 由於提升(hoisting),全部在函數(function) 中的 var 陳述式應該盡可能地置放在接近函數(function)的頂部。這個最佳實踐增加了程式碼的清晰度。
+
+ 在ECMAScript 2015 中,let(const)不會將變數提升到區塊(block)的頂部。但是,在變數宣告之前就引用塊中的變數,會導致 {{jsxref("ReferenceError")}}。變數從區塊(block)的開始到宣告被處理之前,就處於「暫時無效(temporal dead zone)」。

+ +
console.log(x); // ReferenceError
+let x = 3;
+ +

函式提升

+ +

針對函式來說,只有函式宣告式(function declaration)提昇到頂部,但函式表示式(function exprssion) 不被提昇至頂部。

+ +
/* Function declaration */
+
+foo(); // "bar"
+
+function foo() {
+  console.log('bar');
+}
+
+
+/* Function expression */
+
+baz(); // TypeError: baz is not a function
+
+var baz = function() {
+  console.log('bar2');
+};
+
+ +

全域變數 (Global variables)

+ +

全域變數事實上是全域物件的屬性值。在網頁中的全域物件是 {{domxref("window")}},因此你可使用 window.variable 的語法來設定及存取全域變數。

+ +

Consequently, 你可以指定 window 或 frame 物件的名稱來存取在另一個在 window 物件或 frame 物件所宣告的全域變數。例如,如果在一個文檔中已宣告一個稱為 phoneNumber 的變數,你可以在 iframe 中使用 parent.phoneNumber 來存取該變數

+ +

常數 (Constants)

+ +

你可用 {{jsxref("Statements/const", "const")}} 關鍵字來建立一個唯讀、有名稱的常數。 常數識別子的命名語法與變數識別子的命名語法是一樣的: 必須由一個英文字母,底線或錢符號($)開始,之後可包含英文字母,數字及底線字元。

+ +
const PI = 3.14;
+
+ +

當程式執行時,無法再透過賦值或重新宣告來改變常數已設定的值。常數必須被初始化。

+ +

The scope rules for constants are the same as those for let block-scope variables. If the const keyword is omitted, the identifier is assumed to represent a variable.

+ +

你不能在同一個局部範圍內使用與其它函式或變數相同的名稱來宣告變數。例如:

+ +
// THIS WILL CAUSE AN ERROR
+function f() {};
+const f = 5;
+
+// THIS WILL CAUSE AN ERROR ALSO
+function f() {
+  const g = 5;
+  var g;
+
+  //statements
+}
+
+ +

但是常數物件內的物件屬性並不受到保護,因此以下陳述式可以正常執行。

+ +
const MY_OBJECT = {'key': 'value'};
+MY_OBJECT.key = 'otherValue';
+ +

資料結構及型別

+ +

資料型別 (Data types)

+ +

最新 ECMAScript 標準定義以下七種資料型別:

+ + + +

儘管這些變數關聯性很小, 他們可以讓你在你的應用程式中, 產生出有意義的函數. 

+ +

物件與 函數 在語言中是其它的基本元素. 你可以把物件想成是一個被命名過且用來裝數值的容器,以及函數則為你的應用程式所執行的步驟.  

+ +

資料型別轉換

+ +

JavaScript 是一個動態型別的語言,這意味著你不需要在宣告變數時定義它的資料型別,程式執行時會自動轉換,你可以用下面方式宣告變數:

+ +
+
var answer = 42;
+
+
+ +

你可以指派字串在同個變數中,例如:

+ +
+
answer = "Thanks for all the fish...";
+
+
+ +

由於 Javascript 是一個動態型別的語言,因此這樣的宣告方式不會導致錯誤。

+ +

在該陳述式中,它調用了字串和數字,並使用 + 進行運算,JavaScript 會自動把數字轉換成字串,例如:

+ +
x = "The answer is " + 42 // "The answer is 42"
+y = 42 + " is the answer" // "42 is the answer"
+
+ +

在該陳述式中,它調用了其它運算子,JavaScript 就不會將數字轉換成字串,例如:

+ +
"37" - 7 // 30
+"37" + 7 // "377"
+
+ +

字串轉數值

+ +

當代表數字的值以字串形式存在記憶體中,有些方法可用來將這種字串轉換成整數或浮點數。 

+ + + +

parseInt 只會返回整數,因此減少了對小數的使用。此外,parseInt 的最佳實務是始終包含基數參數。基數參數用於指定使用的數值系統。

+ +

另一個將字串轉成數字是使用單元 + (unary plus) 運算子:

+ +
'1.1' + '1.1' = '1.11.1'
+(+'1.1') + (+'1.1') = 2.2
+// 注意: 括號是為了易於閱讀,並不是必須的.
+ +

字面值(Literals)

+ +

您能使用字面值來表示JavaScript中的值。這些是您在腳本中實際提供的固定值,而不是變量。本節描述以下類型的字面值:

+ + + +

陣列字面值 (Array literals)

+ +

陣列字面值是零或多個表達式的列表,每個表達式代表一個數組元素,並用方括號([])括起來。使用陣列字面值創建陣列時,將使用指定的值作為其元素對其進行初始化,並將其長度設置為指定的參數值。

+ +

以下範例創建了陣列 coffees ,長度為 3 並包含三個元素:

+ +
var coffees = ['French Roast', 'Colombian', 'Kona'];
+
+ +
+

Note : An array literal is a type of object initializer. See Using Object Initializers.

+
+ +

If an array is created using a literal in a top-level script, JavaScript interprets the array each time it evaluates the expression containing the array literal. In addition, a literal used in a function is created each time the function is called.

+ +

Array literals are also Array objects. See {{jsxref("Array")}} and Indexed collections for details on Array objects.

+ +

Extra commas in array literals

+ +

You do not have to specify all elements in an array literal. If you put two commas in a row, the array is created with undefined for the unspecified elements. The following example creates the fish array:

+ +
var fish = ['Lion', , 'Angel'];
+
+ +

This array has two elements with values and one empty element (fish[0] is "Lion", fish[1] is undefined, and fish[2] is "Angel").

+ +

If you include a trailing comma at the end of the list of elements, the comma is ignored. In the following example, the length of the array is three. There is no myList[3]. All other commas in the list indicate a new element.

+ +
+

Note : Trailing commas can create errors in older browser versions and it is a best practice to remove them.

+
+ +
var myList = ['home', , 'school', ];
+
+ +

In the following example, the length of the array is four, and myList[0] and myList[2] are missing.

+ +
var myList = [ ,'home', , 'school'];
+
+ +

In the following example, the length of the array is four, and myList[1] and myList[3] are missing. Only the last comma is ignored.

+ +
var myList = ['home', , 'school', , ];
+
+ +

Understanding the behavior of extra commas is important to understanding JavaScript as a language, however when writing your own code: explicitly declaring the missing elements as undefined will increase your code's clarity and maintainability.

+ +

布林字面值 (Boolean literals)

+ +

布林型別有兩種字面值: true 跟 false.

+ +

Do not confuse the primitive Boolean values true and false with the true and false values of the Boolean object. The Boolean object is a wrapper around the primitive Boolean data type. See {{jsxref("Boolean")}} for more information.

+ +

整數字面值 (Numerical literals)

+ +

整數能表示為「十進制」、「十六進制」、「八進制」、「二進制」

+ + + +

整數字面值範例如下:

+ +
0, 117 and -345 (decimal, base 10)
+015, 0001 and -0o77 (octal, base 8)
+0x1123, 0x00111 and -0xF1A7 (hexadecimal, "hex" or base 16)
+0b11, 0b0011 and -0b11 (binary, base 2)
+
+ +

更多資訊請參閱 Numeric literals in the Lexical grammar reference.

+ +

浮點數字面值 (Floating-point literals)

+ +

浮點數字面值能包含以下部分:

+ + + +

指數部分由「"e" 或 "E" 後面跟整數」所組成,可帶符號 "+" 或 "-" 於整數前。浮點數字面值至少由「一位數字」與「一個小數點 "e" (或 "E")」組成。

+ +

簡言之,於法如下:

+ +
[(+|-)][digits][.digits][(E|e)[(+|-)]digits]
+
+ +

舉個例子:

+ +
3.1415926
+-.123456789
+-3.1E+12
+.1e-23
+
+ +

物件字面值 (Object literals)

+ +

物件字面值是用大括號({})括起來的零或多對鍵值對的列表。因為 "{" 將被解譯為區塊(block)的開頭,因此你不應在陳述句開頭使用物件字面值,這將導致錯誤或不預期的行為。

+ +

以下是物件字面值的範例。car 物件包含三個屬性:

+ + + +
var sales = 'Toyota';
+
+function carTypes(name) {
+  if (name === 'Honda') {
+    return name;
+  } else {
+    return "Sorry, we don't sell " + name + ".";
+  }
+}
+
+var car = { myCar: 'Saturn', getCar: carTypes('Honda'), special: sales };
+
+console.log(car.myCar);   // Saturn
+console.log(car.getCar);  // Honda
+console.log(car.special); // Toyota
+
+ +

此外,您可以使用數字或字串字面值作為屬性名,也可將物件嵌套在另一個物件中。如下範例:

+ +
var car = { manyCars: {a: 'Saab', 'b': 'Jeep'}, 7: 'Mazda' };
+
+console.log(car.manyCars.b); // Jeep
+console.log(car[7]); // Mazda
+
+ +

物件屬性名可以是任何字串,包括空字串。如果屬性名不是有效的 JavaScript {{Glossary("Identifier","識別字")}} 或數字,則必須將其用引號引起來。無效的屬性名稱也不能作為點 (.) 屬性訪問,但是可以使用類似數組的符號("[]")進行訪問和設置。

+ +
var unusualPropertyNames = {
+  '': 'An empty string',
+  '!': 'Bang!'
+}
+console.log(unusualPropertyNames.'');   // SyntaxError: Unexpected string
+console.log(unusualPropertyNames['']);  // An empty string
+console.log(unusualPropertyNames.!);    // SyntaxError: Unexpected token !
+console.log(unusualPropertyNames['!']); // Bang!
+ +

Enhanced Object literals

+ +

In ES2015, object literals are extended to support setting the prototype at construction, shorthand for foo: foo assignments, defining methods, making super calls, and computing property names with expressions. Together, these also bring object literals and class declarations closer together, and let object-based design benefit from some of the same conveniences.

+ +
var obj = {
+    // __proto__
+    __proto__: theProtoObj,
+    // Shorthand for ‘handler: handler’
+    handler,
+    // Methods
+    toString() {
+     // Super calls
+     return 'd ' + super.toString();
+    },
+    // Computed (dynamic) property names
+    [ 'prop_' + (() => 42)() ]: 42
+};
+ +

Please note:

+ +
var foo = {a: 'alpha', 2: 'two'};
+console.log(foo.a);    // alpha
+console.log(foo[2]);   // two
+//console.log(foo.2);  // Error: missing ) after argument list
+//console.log(foo[a]); // Error: a is not defined
+console.log(foo['a']); // alpha
+console.log(foo['2']); // two
+
+ +

正規表達式字面值 (RegExp literals)

+ +

正則表達式字面值是包含在斜杠間的樣式。以下是正則表達式文字的範例。

+ +
var re = /ab+c/;
+ +

字串字面值 (String literals)

+ +

字串字面值是用雙引號(“)或單引號(')包住的零或多個字元。字串必須用同類的引號定界;也就是「兩個單引號」或「兩個雙引號」。以下是字串字面值的範例:

+ +
'foo'
+"bar"
+'1234'
+'one line \n another line'
+"John's cat"
+
+ +

你可以在字串字面值上調用 String 物件的任何方法 - JavaScript 將自動轉換字串字面值為臨時 String 物件並調用該方法,然後丟棄該臨時 String 物件。您還可以將 String.length 屬性與字串字面值一起使用:

+ +
console.log("John's cat".length)
+// Will print the number of symbols in the string including whitespace.
+// In this case, 10.
+
+ +

In ES2015, template literals are also available. Template literals are enclosed by the back-tick (` `)  (grave accent) character instead of double or single quotes. Template strings provide syntactic sugar for constructing strings. This is similar to string interpolation features in Perl, Python and more. Optionally, a tag can be added to allow the string construction to be customized, avoiding injection attacks or constructing higher level data structures from string contents.

+ +
// Basic literal string creation
+`In JavaScript '\n' is a line-feed.`
+
+// Multiline strings
+`In JavaScript template strings can run
+ over multiple lines, but double and single
+ quoted strings cannot.`
+
+// String interpolation
+var name = 'Bob', time = 'today';
+`Hello ${name}, how are you ${time}?`
+
+// Construct an HTTP request prefix is used to interpret the replacements and construction
+POST`http://foo.org/bar?a=${a}&b=${b}
+     Content-Type: application/json
+     X-Credentials: ${credentials}
+     { "foo": ${foo},
+       "bar": ${bar}}`(myOnReadyStateChangeHandler);
+ +

You should use string literals unless you specifically need to use a String object. See {{jsxref("String")}} for details on String objects.

+ +

字串裡的特殊字元

+ +

除了普通字元,字串也能包含特殊字元,範例如下:

+ +
'one line \n another line'
+
+ +

下表列出了可以在 JavaScript 字串中使用的特殊字元。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
表格: JavaScript 特殊字元
字元意涵
\0Null Byte
\b退格 (Backspace)
\fForm feed
\n換行 (New line)
\r回車 (Carriage return)
\t跳格 (Tab)
\vVertical tab
\'Apostrophe or single quote
\"Double quote
\\Backslash character
\XXXThe character with the Latin-1 encoding specified by up to three octal digits XXX between 0 and 377. For example, \251 is the octal sequence for the copyright symbol.
\xXXThe character with the Latin-1 encoding specified by the two hexadecimal digits XX between 00 and FF. For example, \xA9 is the hexadecimal sequence for the copyright symbol.
\uXXXXThe Unicode character specified by the four hexadecimal digits XXXX. For example, \u00A9 is the Unicode sequence for the copyright symbol. See Unicode escape sequences.
\u{XXXXX}Unicode code point escapes. For example, \u{2F804} is the same as the simple Unicode escapes \uD87E\uDC04.
+ +

Escaping characters

+ +

For characters not listed in the table, a preceding backslash is ignored, but this usage is deprecated and should be avoided.

+ +

You can insert a quotation mark inside a string by preceding it with a backslash. This is known as escaping the quotation mark. For example:

+ +
var quote = "He read \"The Cremation of Sam McGee\" by R.W. Service.";
+console.log(quote);
+
+ +

The result of this would be:

+ +
He read "The Cremation of Sam McGee" by R.W. Service.
+
+ +

To include a literal backslash inside a string, you must escape the backslash character. For example, to assign the file path c:\temp to a string, use the following:

+ +
var home = 'c:\\temp';
+
+ +

You can also escape line breaks by preceding them with backslash. The backslash and line break are both removed from the value of the string.

+ +
var str = 'this string \
+is broken \
+across multiple \
+lines.'
+console.log(str);   // this string is broken across multiplelines.
+
+ +

Although JavaScript does not have "heredoc" syntax, you can get close by adding a line break escape and an escaped line break at the end of each line:

+ +
var poem =
+'Roses are red,\n\
+Violets are blue.\n\
+Sugar is sweet,\n\
+and so is foo.'
+
+ +

ECMAScript 2015 introduces a new type of literal, namely template literals. This allows for many new features including multiline strings!

+ +
var poem =
+`Roses are red,
+Violets are blue.
+Sugar is sweet,
+and so is foo.` 
+ +

More information

+ +

This chapter focuses on basic syntax for declarations and types. To learn more about JavaScript's language constructs, see also the following chapters in this guide:

+ + + +

In the next chapter, we will have a look at control flow constructs and error handling.

+ +

{{PreviousNext("Web/JavaScript/Guide/Introduction", "Web/JavaScript/Guide/Control_flow_and_error_handling")}}

diff --git a/files/zh-tw/web/javascript/guide/index.html b/files/zh-tw/web/javascript/guide/index.html new file mode 100644 index 0000000000..769f57c55e --- /dev/null +++ b/files/zh-tw/web/javascript/guide/index.html @@ -0,0 +1,116 @@ +--- +title: JavaScript 指南 +slug: Web/JavaScript/Guide +translation_of: Web/JavaScript/Guide +--- +
{{jsSidebar("JavaScript Guide")}}
+ +

JavaScript 指南會讓您了解如何使用 JavaScript 並給您這個語言的概觀。若您需要語言功能詳細資訊請參考 JavaScript 參考文件

+ +

章節

+ +

本指南區分成以下數個章節:

+ + + + + + + + + +

{{Next("Web/JavaScript/Guide/Introduction")}}

diff --git a/files/zh-tw/web/javascript/guide/indexed_collections/index.html b/files/zh-tw/web/javascript/guide/indexed_collections/index.html new file mode 100644 index 0000000000..b34c419252 --- /dev/null +++ b/files/zh-tw/web/javascript/guide/indexed_collections/index.html @@ -0,0 +1,450 @@ +--- +title: 索引集合 +slug: Web/JavaScript/Guide/Indexed_collections +translation_of: Web/JavaScript/Guide/Indexed_collections +--- +
{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Regular_Expressions", "Web/JavaScript/Guide/Keyed_Collections")}}
+ +

This chapter introduces collections of data which are ordered by an index value. This includes arrays and array-like constructs such as {{jsxref("Array")}} objects and {{jsxref("TypedArray")}} objects.

+ +

Array object

+ +

An array is an ordered set of values that you refer to with a name and an index. For example, you could have an array called emp that contains employees' names indexed by their numerical employee number. So emp[1] would be employee number one, emp[2] employee number two, and so on.

+ +

JavaScript does not have an explicit array data type. However, you can use the predefined Array object and its methods to work with arrays in your applications. The Array object has methods for manipulating arrays in various ways, such as joining, reversing, and sorting them. It has a property for determining the array length and other properties for use with regular expressions.

+ +

Creating an array

+ +

The following statements create equivalent arrays:

+ +
var arr = new Array(element0, element1, ..., elementN);
+var arr = Array(element0, element1, ..., elementN);
+var arr = [element0, element1, ..., elementN];
+
+ +

element0, element1, ..., elementN is a list of values for the array's elements. When these values are specified, the array is initialized with them as the array's elements. The array's length property is set to the number of arguments.

+ +

The bracket syntax is called an "array literal" or "array initializer." It's shorter than other forms of array creation, and so is generally preferred. See Array literals for details.

+ +

To create an array with non-zero length, but without any items, either of the following can be used:

+ +
var arr = new Array(arrayLength);
+var arr = Array(arrayLength);
+
+// This has exactly the same effect
+var arr = [];
+arr.length = arrayLength;
+
+ +
+

Note : in the above code, arrayLength must be a Number. Otherwise, an array with a single element (the provided value) will be created. Calling arr.length will return arrayLength, but the array actually contains empty (undefined) elements. Running a {{jsxref("Statements/for...in","for...in")}} loop on the array will return none of the array's elements.

+
+ +

In addition to a newly defined variable as shown above, arrays can also be assigned as a property of a new or an existing object:

+ +
var obj = {};
+// ...
+obj.prop = [element0, element1, ..., elementN];
+
+// OR
+var obj = {prop: [element0, element1, ...., elementN]};
+
+ +

If you wish to initialize an array with a single element, and the element happens to be a Number, you must use the bracket syntax. When a single Number value is passed to the Array() constructor or function, it is interpreted as an arrayLength, not as a single element.

+ +
var arr = [42];      // Creates an array with only one element:
+                     // the number 42.
+
+var arr = Array(42); // Creates an array with no elements
+                     // and arr.length set to 42; this is
+                     // equivalent to:
+var arr = [];
+arr.length = 42;
+
+ +

Calling Array(N) results in a RangeError, if N is a non-whole number whose fractional portion is non-zero. The following example illustrates this behavior.

+ +
var arr = Array(9.3);  // RangeError: Invalid array length
+
+ +

If your code needs to create arrays with single elements of an arbitrary data type, it is safer to use array literals. Or, create an empty array first before adding the single element to it.

+ +

Populating an array

+ +

You can populate an array by assigning values to its elements. For example,

+ +
var emp = [];
+emp[0] = 'Casey Jones';
+emp[1] = 'Phil Lesh';
+emp[2] = 'August West';
+
+ +
+

Note : if you supply a non-integer value to the array operator in the code above, a property will be created in the object representing the array, instead of an array element.

+
+ +
var arr = [];
+arr[3.4] = 'Oranges';
+console.log(arr.length);                // 0
+console.log(arr.hasOwnProperty(3.4));   // true
+
+ +

You can also populate an array when you create it:

+ +
var myArray = new Array('Hello', myVar, 3.14159);
+var myArray = ['Mango', 'Apple', 'Orange'];
+
+ +

Referring to array elements

+ +

You refer to an array's elements by using the element's ordinal number. For example, suppose you define the following array:

+ +
var myArray = ['Wind', 'Rain', 'Fire'];
+
+ +

You then refer to the first element of the array as myArray[0] and the second element of the array as myArray[1]. The index of the elements begins with zero.

+ +
+

Note : the array operator (square brackets) is also used for accessing the array's properties (arrays are also objects in JavaScript). For example,

+
+ +
var arr = ['one', 'two', 'three'];
+arr[2];  // three
+arr['length'];  // 3
+
+ +

Understanding length

+ +

At the implementation level, JavaScript's arrays actually store their elements as standard object properties, using the array index as the property name. The length property is special; it always returns the index of the last element plus one (in the following example, Dusty is indexed at 30, so cats.length returns 30 + 1). Remember, JavaScript Array indexes are 0-based: they start at 0, not 1. This means that the length property will be one more than the highest index stored in the array:

+ +
var cats = [];
+cats[30] = ['Dusty'];
+console.log(cats.length); // 31
+
+ +

You can also assign to the length property. Writing a value that is shorter than the number of stored items truncates the array; writing 0 empties it entirely:

+ +
var cats = ['Dusty', 'Misty', 'Twiggy'];
+console.log(cats.length); // 3
+
+cats.length = 2;
+console.log(cats); // logs "Dusty, Misty" - Twiggy has been removed
+
+cats.length = 0;
+console.log(cats); // logs nothing; the cats array is empty
+
+cats.length = 3;
+console.log(cats); // [undefined, undefined, undefined]
+
+ +

Iterating over arrays

+ +

A common operation is to iterate over the values of an array, processing each one in some way. The simplest way to do this is as follows:

+ +
var colors = ['red', 'green', 'blue'];
+for (var i = 0; i < colors.length; i++) {
+  console.log(colors[i]);
+}
+
+ +

If you know that none of the elements in your array evaluate to false in a boolean context — if your array consists only of DOM nodes, for example, you can use a more efficient idiom:

+ +
var divs = document.getElementsByTagName('div');
+for (var i = 0, div; div = divs[i]; i++) {
+  /* Process div in some way */
+}
+
+ +

This avoids the overhead of checking the length of the array, and ensures that the div variable is reassigned to the current item each time around the loop for added convenience.

+ +

The {{jsxref("Array.forEach", "forEach()")}} method provides another way of iterating over an array:

+ +
var colors = ['red', 'green', 'blue'];
+colors.forEach(function(color) {
+  console.log(color);
+});
+// red
+// green
+// blue
+
+ +

Alternatively, You can shorten the code for the forEach parameter with ES6 Arrow Functions:

+ +
var colors = ['red', 'green', 'blue'];
+colors.forEach(color => console.log(color));
+// red
+// green
+// blue
+
+ +

The function passed to forEach is executed once for every item in the array, with the array item passed as the argument to the function. Unassigned values are not iterated in a forEach loop.

+ +

Note that the elements of array that are omitted when the array is defined are not listed when iterating by forEach, but are listed when undefined has been manually assigned to the element:

+ +
var array = ['first', 'second', , 'fourth'];
+
+array.forEach(function(element) {
+  console.log(element);
+});
+// first
+// second
+// fourth
+
+if (array[2] === undefined) {
+  console.log('array[2] is undefined'); // true
+}
+
+array = ['first', 'second', undefined, 'fourth'];
+
+array.forEach(function(element) {
+  console.log(element);
+});
+// first
+// second
+// undefined
+// fourth
+
+ +

Since JavaScript elements are saved as standard object properties, it is not advisable to iterate through JavaScript arrays using {{jsxref("Statements/for...in","for...in")}} loops because normal elements and all enumerable properties will be listed.

+ +

Array methods

+ +

The {{jsxref("Array")}} object has the following methods:

+ +

{{jsxref("Array.concat", "concat()")}} joins two arrays and returns a new array.

+ +
var myArray = new Array('1', '2', '3');
+myArray = myArray.concat('a', 'b', 'c');
+// myArray is now ["1", "2", "3", "a", "b", "c"]
+
+ +

{{jsxref("Array.join", "join(deliminator = ',')")}} joins all elements of an array into a string.

+ +
var myArray = new Array('Wind', 'Rain', 'Fire');
+var list = myArray.join(' - '); // list is "Wind - Rain - Fire"
+
+ +

{{jsxref("Array.push", "push()")}} adds one or more elements to the end of an array and returns the resulting length of the array.

+ +
var myArray = new Array('1', '2');
+myArray.push('3'); // myArray is now ["1", "2", "3"]
+
+ +

{{jsxref("Array.pop", "pop()")}} removes the last element from an array and returns that element.

+ +
var myArray = new Array('1', '2', '3');
+var last = myArray.pop();
+// myArray is now ["1", "2"], last = "3"
+
+ +

{{jsxref("Array.shift", "shift()")}} removes the first element from an array and returns that element.

+ +
var myArray = new Array('1', '2', '3');
+var first = myArray.shift();
+// myArray is now ["2", "3"], first is "1"
+
+ +

{{jsxref("Array.unshift", "unshift()")}} adds one or more elements to the front of an array and returns the new length of the array.

+ +
var myArray = new Array('1', '2', '3');
+myArray.unshift('4', '5');
+// myArray becomes ["4", "5", "1", "2", "3"]
+ +

{{jsxref("Array.slice", "slice(start_index, upto_index)")}} extracts a section of an array and returns a new array.

+ +
var myArray = new Array('a', 'b', 'c', 'd', 'e');
+myArray = myArray.slice(1, 4); // starts at index 1 and extracts all elements
+                               // until index 3, returning [ "b", "c", "d"]
+
+ +

{{jsxref("Array.splice", "splice(index, count_to_remove, addElement1, addElement2, ...)")}} removes elements from an array and (optionally) replaces them. It returns the items which were removed from the array.

+ +
var myArray = new Array('1', '2', '3', '4', '5');
+myArray.splice(1, 3, 'a', 'b', 'c', 'd');
+// myArray is now ["1", "a", "b", "c", "d", "5"]
+// This code started at index one (or where the "2" was),
+// removed 3 elements there, and then inserted all consecutive
+// elements in its place.
+
+ +

{{jsxref("Array.reverse", "reverse()")}} transposes the elements of an array, in place: the first array element becomes the last and the last becomes the first. It returns a reference to the array.

+ +
var myArray = new Array('1', '2', '3');
+myArray.reverse();
+// transposes the array so that myArray = ["3", "2", "1"]
+
+ +

{{jsxref("Array.sort", "sort()")}} sorts the elements of an array in place, and returns a reference to the array.

+ +
var myArray = new Array('Wind', 'Rain', 'Fire');
+myArray.sort();
+// sorts the array so that myArray = ["Fire", "Rain", "Wind"]
+
+ +

sort() can also take a callback function to determine how array elements are compared.

+ +

The sort method and other methods below that take a callback are known as iterative methods, because they iterate over the entire array in some fashion. Each one takes an optional second argument called thisObject. If provided, thisObject becomes the value of the this keyword inside the body of the callback function. If not provided, as with other cases where a function is invoked outside of an explicit object context, this will refer to the global object ({{domxref("window")}}).

+ +

The callback function is called with two arguments, that are array's elements.

+ +

The function below compares two values and returns one of three values:

+ +

For instance, the following will sort by the last letter of a string:

+ +
var sortFn = function(a, b) {
+  if (a[a.length - 1] < b[b.length - 1]) return -1;
+  if (a[a.length - 1] > b[b.length - 1]) return 1;
+  if (a[a.length - 1] == b[b.length - 1]) return 0;
+}
+myArray.sort(sortFn);
+// sorts the array so that myArray = ["Wind","Fire","Rain"]
+ + + +

{{jsxref("Array.indexOf", "indexOf(searchElement[, fromIndex])")}} searches the array for searchElement and returns the index of the first match.

+ +
var a = ['a', 'b', 'a', 'b', 'a'];
+console.log(a.indexOf('b')); // logs 1
+// Now try again, starting from after the last match
+console.log(a.indexOf('b', 2)); // logs 3
+console.log(a.indexOf('z')); // logs -1, because 'z' was not found
+
+ +

{{jsxref("Array.lastIndexOf", "lastIndexOf(searchElement[, fromIndex])")}} works like indexOf, but starts at the end and searches backwards.

+ +
var a = ['a', 'b', 'c', 'd', 'a', 'b'];
+console.log(a.lastIndexOf('b')); // logs 5
+// Now try again, starting from before the last match
+console.log(a.lastIndexOf('b', 4)); // logs 1
+console.log(a.lastIndexOf('z')); // logs -1
+
+ +

{{jsxref("Array.forEach", "forEach(callback[, thisObject])")}} executes callback on every array item and returns undefined.

+ +
var a = ['a', 'b', 'c'];
+a.forEach(function(element) { console.log(element); });
+// logs each item in turn
+
+ +

{{jsxref("Array.map", "map(callback[, thisObject])")}} returns a new array of the return value from executing callback on every array item.

+ +
var a1 = ['a', 'b', 'c'];
+var a2 = a1.map(function(item) { return item.toUpperCase(); });
+console.log(a2); // logs ['A', 'B', 'C']
+
+ +

{{jsxref("Array.filter", "filter(callback[, thisObject])")}} returns a new array containing the items for which callback returned true.

+ +
var a1 = ['a', 10, 'b', 20, 'c', 30];
+var a2 = a1.filter(function(item) { return typeof item === 'number'; });
+console.log(a2); // logs [10, 20, 30]
+
+ +

{{jsxref("Array.every", "every(callback[, thisObject])")}} returns true if callback returns true for every item in the array.

+ +
function isNumber(value) {
+  return typeof value === 'number';
+}
+var a1 = [1, 2, 3];
+console.log(a1.every(isNumber)); // logs true
+var a2 = [1, '2', 3];
+console.log(a2.every(isNumber)); // logs false
+
+ +

{{jsxref("Array.some", "some(callback[, thisObject])")}} returns true if callback returns true for at least one item in the array.

+ +
function isNumber(value) {
+  return typeof value === 'number';
+}
+var a1 = [1, 2, 3];
+console.log(a1.some(isNumber)); // logs true
+var a2 = [1, '2', 3];
+console.log(a2.some(isNumber)); // logs true
+var a3 = ['1', '2', '3'];
+console.log(a3.some(isNumber)); // logs false
+
+ +

{{jsxref("Array.reduce", "reduce(callback[, initialValue])")}} applies callback(firstValue, secondValue) to reduce the list of items down to a single value and returns that value.

+ +
var a = [10, 20, 30];
+var total = a.reduce(function(first, second) { return first + second; }, 0);
+console.log(total) // Prints 60
+
+ +

{{jsxref("Array.reduceRight", "reduceRight(callback[, initialValue])")}} works like reduce(), but starts with the last element.

+ +

reduce and reduceRight are the least obvious of the iterative array methods. They should be used for algorithms that combine two values recursively in order to reduce a sequence down to a single value.

+ +

Multi-dimensional arrays

+ +

Arrays can be nested, meaning that an array can contain another array as an element. Using this characteristic of JavaScript arrays, multi-dimensional arrays can be created.

+ +

The following code creates a two-dimensional array.

+ +
var a = new Array(4);
+for (i = 0; i < 4; i++) {
+  a[i] = new Array(4);
+  for (j = 0; j < 4; j++) {
+    a[i][j] = '[' + i + ', ' + j + ']';
+  }
+}
+
+ +

This example creates an array with the following rows:

+ +
Row 0: [0, 0] [0, 1] [0, 2] [0, 3]
+Row 1: [1, 0] [1, 1] [1, 2] [1, 3]
+Row 2: [2, 0] [2, 1] [2, 2] [2, 3]
+Row 3: [3, 0] [3, 1] [3, 2] [3, 3]
+
+ +

Arrays and regular expressions

+ +

When an array is the result of a match between a regular expression and a string, the array returns properties and elements that provide information about the match. An array is the return value of {{jsxref("Global_Objects/RegExp/exec","RegExp.exec()")}}, {{jsxref("Global_Objects/String/match","String.match()")}}, and {{jsxref("Global_Objects/String/split","String.split()")}}. For information on using arrays with regular expressions, see Regular Expressions.

+ +

Working with array-like objects

+ +

Some JavaScript objects, such as the {{domxref("NodeList")}} returned by {{domxref("document.getElementsByTagName()")}} or the {{jsxref("Functions/arguments","arguments")}} object made available within the body of a function, look and behave like arrays on the surface but do not share all of their methods. The arguments object provides a {{jsxref("Global_Objects/Function/length","length")}} attribute but does not implement the {{jsxref("Array.forEach", "forEach()")}} method, for example.

+ +

Array prototype methods can be called against other array-like objects. for example:

+ +
function printArguments() {
+  Array.prototype.forEach.call(arguments, function(item) {
+    console.log(item);
+  });
+}
+
+ +

Array prototype methods can be used on strings as well, since they provide sequential access to their characters in a similar way to arrays:

+ +
Array.prototype.forEach.call('a string', function(chr) {
+  console.log(chr);
+});
+ +

Typed Arrays

+ +

JavaScript typed arrays are array-like objects and provide a mechanism for accessing raw binary data. As you already know, {{jsxref("Array")}} objects grow and shrink dynamically and can have any JavaScript value. JavaScript engines perform optimizations so that these arrays are fast. However, as web applications become more and more powerful, adding features such as audio and video manipulation, access to raw data using WebSockets, and so forth, it has become clear that there are times when it would be helpful for JavaScript code to be able to quickly and easily manipulate raw binary data in typed arrays.

+ +

Buffers and views: typed array architecture

+ +

To achieve maximum flexibility and efficiency, JavaScript typed arrays split the implementation into buffers and views. A buffer (implemented by the {{jsxref("ArrayBuffer")}} object) is an object representing a chunk of data; it has no format to speak of, and offers no mechanism for accessing its contents. In order to access the memory contained in a buffer, you need to use a view. A view provides a context — that is, a data type, starting offset, and number of elements — that turns the data into an actual typed array.

+ +

Typed arrays in an ArrayBuffer

+ +

ArrayBuffer

+ +

The {{jsxref("ArrayBuffer")}} is a data type that is used to represent a generic, fixed-length binary data buffer. You can't directly manipulate the contents of an ArrayBuffer; instead, you create a typed array view or a {{jsxref("DataView")}} which represents the buffer in a specific format, and use that to read and write the contents of the buffer.

+ +

Typed array views

+ +

Typed array views have self descriptive names and provide views for all the usual numeric types like Int8, Uint32, Float64 and so forth. There is one special typed array view, the Uint8ClampedArray. It clamps the values between 0 and 255. This is useful for Canvas data processing, for example.

+ +

{{page("/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray", "TypedArray_objects")}}

+ +

For more information, see JavaScript typed arrays and the reference documentation for the different {{jsxref("TypedArray")}} objects.

+ +

{{PreviousNext("Web/JavaScript/Guide/Regular_Expressions", "Web/JavaScript/Guide/Keyed_Collections")}}

diff --git a/files/zh-tw/web/javascript/guide/introduction/index.html b/files/zh-tw/web/javascript/guide/introduction/index.html new file mode 100644 index 0000000000..ab855f45fc --- /dev/null +++ b/files/zh-tw/web/javascript/guide/introduction/index.html @@ -0,0 +1,180 @@ +--- +title: JavaScript 概觀 +slug: Web/JavaScript/Guide/Introduction +translation_of: Web/JavaScript/Guide/Introduction +--- +

{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide", "Web/JavaScript/Guide/Grammar_and_types")}}

+ +

這個章節的內容主要是介紹 JavaScript 和討論一些 JavaScript 的基本概念。

+ +

在開始前需具備之能力

+ +

本手冊假設您具有以下基本背景:

+ + + +

 

+ +

什麼是 JavaScript?

+ +

JavaScript 是個跨平台、物件導向、輕小型的腳本語言。作為獨立語言並不實用,而是為了能簡單嵌入其他產品和應用程式(例如:網頁瀏覽器)而設計。JavaScript 若寄宿在主體環境(Host environment)時,可以與環境中的物件 (Object)相連,並以程式控制這些物件。

+ +

Core JavaScript 包含了物件的核心集合(例如: Array、 Date、 Math)及語言成份的核心集合(例如:運算子、控制結構、敘述)。在 Core JavaScript 增加額外的物件即可擴充各式各樣的功能,例如:

+ + + +

舉例來說:用戶端的擴充套件允許某個應用程式將元素放置在 HTML 的表單上及對使用者的操作(例如:滑鼠點選、表單輸入、頁面導覽等)做出回應。

+ + + +

舉例來說:伺服器端的擴充套件允許某個應用程式和相關的資料庫交換訊息、對一個其他應用程式的呼叫提供連續性的資訊、在伺服器上執行檔案操作。

+ +

透過 JavaScript 的 LiveConnect 功能,你可以使 Java 和 JavaScript 的程式碼彼此相連。在 JavaScript 的程式碼中,你可以實例化(instantiate)Java 的物件並存取那些物件的公有方法(public methods)及欄位(fields)。在 Java 的程式碼中,你可以存取 JavaScript 的物件、屬性(properties)及方法(methods)。

+ +

Netscape 公司發明了 JavaScript ,而 JavaScript 的第一次使用正是在 Netscape 自家的瀏覽器上。

+ +

JavaScript 與 Java

+ +

JavaScript 與 Java 在某些方面非常相似但本質上卻是不同的。 JavaScript 雖然和 Java 類似,卻沒有 Java 的靜態定型(static typing)及強型態確認(strong type checking)特性。 JavaScript 遵從大部份的 Java 表達式語法、命名傳統和基本的流程控制概念,這特性同時也是為何要將 LiveScript 重新命名為 JavaScript 的原因。

+ +

相較於 Java 由許多類別中的宣告建立的 compile-time 系統,JavaScript 支援一個由少數代表數值(numeric)、布林值(Boolean)、字串(string)的資料類型所構成的執行期函式庫(runtime system)。JavaScript 擁有一個基於原型的物件模型(prototype-based object model)而不是普遍使用的基於類別的物件模型(class-based object model)。基於原型的物件模型提供動態繼承(dynamic inheritance)的功能,意即被繼承的物件可以根據個別的物件而改變。JavaScript 也支援不需任何特殊宣告的函式,函式可以是物件的屬性,如同鬆散型態方法(loosely typed method)那樣執行。

+ +

JavaScript 和 Java 相比起來,算是一個格式非常自由的語言。你不需要宣告所有的變數、類別(class)、方法,你不需要注意哪些方法是公有(public)或私有(private)或受保護的(protected),你不需要實作介面(interface)。變數、參數及函式回傳的型態並不是顯性型態。

+ +

Java 是一個為了快速執行與安全型態而設計的基於類別的程式語言(class-based programming language)。安全型態意即你在 Java 中無法將整數丟給一個物件參考,也無法藉由中斷 Java bytecodes 來存取私有的記憶體。 Java 的基於類別模型(class-based model)意思是程式由專門的類別及其方法所組成。Java 的類別繼承(class inheritance)和強型別(strong typing)通常需要嚴謹的耦合對象階級(coupled object hierarchies)。這些需求使得 Java 在程式的撰寫上比 JavaScript 來的複雜。

+ +

相反的,JavaScript 承襲了如同 HyperTalk 和 dBASE 相同的精神:較小、動態類型。 這些腳本語言因為較簡單的語法、特殊化的功能、較寬鬆的要求等特性,進而提供了許多軟體開發工具(programming tool)給更多更廣大的愛好者。

+ +

表1.1 - JavaScript 和 Java 比較

+ + + + + + + + + + + + + + + + + + + + + + + +
 
JavaScriptJava
+

物件導向。

+ +

物件的型態之間無區別。

+ +

藉由原型機制(prototype mechanism)和屬性(properties)實行繼承。

+ +

屬性和方法可被動態新增至任何物件。

+
+

類別導向。

+ +

物件藉由類別階級(class hierarchy)而被分離至類別和所有繼承的實體(instance)中。

+ +

類別和實體無法動態新增屬性和方法。

+
+

變數資料型態沒有宣告就可使用(動態定型,dynamic typing)。

+
+

變數資料型態必須宣告才可使用(靜態定型,static typing)。

+
無法自動覆寫到硬碟。無法自動覆寫到硬碟。
+ +

更多關於 JavaScript 和 Java 的差異比較,請參見 Details of the Object Model

+ +

JavaScript 與 ECMAScript 規格

+ +

Netscape 公司發明了 JavaScript ,而 JavaScript 的第一次應用正是在 Netscape 瀏覽器。然而,Netscape 後來和 Ecma International(一個致力於將資訊及通訊系統標準化的歐洲組織,前身為 ECMA - 歐洲計算機製造商協會)合作,開發一個基於 JavaScript 核心並同時兼具標準化與國際化的程式語言,這個經過標準化的 JavaScript 便稱作 ECMAScript ,和 JavaScript 有著相同的應用方式並支援相關標準。各個公司都可以使用這個開放的標準語言去開發 JavaScript 的專案。ECMAScript 標準記載於 ECMA-262 這個規格中。

+ +

ECMA-262 標準同時也經過 ISO(國際標準化組織)認証,成為 ISO-16262 標準。你可以在 Mozilla 的網站上找到 PDF版本的ECMA-262,但這板本已過期;你也可以在 Ecma International 的網站 找到這個規格。 ECMAScript 規格中並沒有描述已經被 W3C(全球資訊網協會)標準化的文件物件模型(DOM)。文件物件模型定義了 HTML 文件物件(document objects)和腳本之間運作的方式。

+ +

JavaScript 版本與 ECMAScript 版本之間的關係

+ +

ECMAScript 規格(ECMA-262)在 Netscape 和 Ecma International 的密切合作下產生。下表描述了 JavaScript 的版本和 ECMAScript 的版本之間的關係。

+ +

表1.2 - JavaScript 版本及 ECMAScript 版本

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
JavaScript 的版本和 ECMAScript 版本的關係
JavaScript 1.1ECMA-262 第1版是基於 JavaScript 1.1 建立的。
JavaScript 1.2 +

ECMA-262 在 JavaScript 1.2 發行之際尚未完成。以下是 JavaScript 1.2 並未完全相容於 ECMA-262 第1版的原因:

+ +
    +
  • Netscape 在 JavaScript 1.2 中新增了一些特性,而這些特性在 ECMA-262 並未被考慮到。
  • +
  • ECMA-262 新增了兩個新的特性:使用國際化的 Unicode 編碼及統一了不同平台之間的行為。JavaScript 1.2 中的一些特性,例如:日期物件(Date object)是具有平台依賴性(platform-dependent)並且使用平台特製化行為(platform-specific behavior)的。
  • +
+
JavaScript 1.3 +

JavaScript 1.3 完全相容於 ECMA-262 第1版。

+ JavaScript 1.3 藉由保留所有在 JavaScript 1.2 新增的特性(除了 == 和 != 以外,因為要和 ECMA-262 一致),解決了 JavaScript 1.2 和 ECMA-262 之間的衝突。
JavaScript 1.4 +

JavaScript 1.4 完全相容於 ECMA-262 第1版。

+ ECMAScript 第3版規格在 JavaScript 1.4 發行之際尚未完成。
JavaScript 1.5JavaScript 1.5 完全相容於 ECMA-262 第3版。
+ +
注意:ECMA-262 第2版是由已修正錯誤的第1版並加上些微的更動構成。現今由 Ecma International 的 TC39 工作組(TC39 Working Group)所發行的版本是 ECMAScript 5.1版
+ +

JavaScript Reference 指出了哪些 JavaScript 的特性是相容於 ECMAScript 的。

+ +

JavaScript 永遠包含許多非 ECMAScript 規格中的特性;

+ +

JavaScript 不僅相容於 ECMAScript 更提供了額外的特性。

+ +

JavaScript 使用說明 v.s ECMAScript 規格

+ +

ECMAScript 規格是執行 ECMAScript 所必須的條件,當你想判斷某個 JavaScript 的特性是否在其他 ECMAScript 實作中有被支援時,ECMAScript 規格是非常有用的。如果你打算撰寫 JavaScript 程式碼並在程式碼中使用僅有 ECMAScript 所支援的特性,那你可能需要查閱一下 ECMAScript 規格。

+ +

ECMAScript 文件並不是為了幫助腳本程式設計師而撰寫,如果想知道撰寫腳本的相關資訊,請參考 JavaScript 使用說明。

+ +

JavaScript 和 ECMAScript 的專門術語

+ +

ECMAScript 規格使用的術語和語法對於 JavaScript 的程式設計師來說可能不是那麼的親切。雖然語言的描述在 ECMAScript 中可能會有所不同,但語言本身的性質仍然是不變的。JavaScript 支援所有在 ECMAScript 規格中被描述到的功能。

+ +

JavaScript 使用說明對於語言的觀點的描述較適合 JavaScript 的程式設計師。例如:

+ + diff --git a/files/zh-tw/web/javascript/guide/iterators_and_generators/index.html b/files/zh-tw/web/javascript/guide/iterators_and_generators/index.html new file mode 100644 index 0000000000..6ad6128a6f --- /dev/null +++ b/files/zh-tw/web/javascript/guide/iterators_and_generators/index.html @@ -0,0 +1,193 @@ +--- +title: Iterators and generators +slug: Web/JavaScript/Guide/Iterators_and_Generators +translation_of: Web/JavaScript/Guide/Iterators_and_Generators +--- +
{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Using_promises", "Web/JavaScript/Guide/Meta_programming")}}
+ +

處理集合中的每個項目是很常見的操作,JavaScript提供了許多迭代集合的方法,從簡單的 {{jsxref("Statements/for","for")}} 循環到 {{jsxref("Global_Objects/Array/map","map()")}} 和  {{jsxref("Global_Objects/Array/filter","filter()")}}。

+ +

Iterators 和 Generators 將迭代的概念直接帶進核心語言,並提供一個機制來客製化  {{jsxref("Statements/for...of","for...of")}}  的循環行為。

+ +

更多詳情請參考:

+ + + +

Iterators (疊代器)

+ +

在 JavaScript 中,iterator 是一個物件(object),他定義一個序列,並在終止時回傳一個值。

+ +

更精確地說,iterator 是任何一個透過 next() 方法實現 Iterator protocol 的物件,該方法回傳具有以下兩個屬性 (property) 的物件:

+ +
+
value
+
在 iteration 序列中的下一個值。
+
done
+
如果序列中的最後一個值已經被消耗(使用)了,則此值為 true 。如果 value 和 done 一起存在, 則他是這個 iterator 的回傳值。
+
+ +

一旦建立 iterator 物件後,可以透過反覆呼叫 next() 來進行迭代。 iterator 經過迭代後,即被認為已經消耗iterator ,因為通常只可能執行一次。在產生終止值之後,對  next() 的其他調用應僅繼續返回{done:true}。
+
+ The most common iterator in Javascript is the Array iterator, which simply returns each value in the associated array in sequence. While it is easy to imagine that all iterators could be expressed as arrays, this is not true. Arrays must be allocated in their entirety, but iterators are consumed only as necessary and thus can express sequences of unlimited size, such as the range of integers between 0 and Infinity.
+
+ Here is an example which can do just that. It allows creation of a simple range iterator which defines a sequence of integers from start (inclusive) to end (exclusive) spaced step apart. Its final return value is the size of the sequence it created, tracked by the variable iterationCount.

+ +
function makeRangeIterator(start = 0, end = Infinity, step = 1) {
+    let nextIndex = start;
+    let iterationCount = 0;
+
+    const rangeIterator = {
+       next: function() {
+           let result;
+           if (nextIndex <= end) {
+               result = { value: nextIndex, done: false }
+               nextIndex += step;
+               iterationCount++;
+               return result;
+           }
+           return { value: iterationCount, done: true }
+       }
+    };
+    return rangeIterator;
+}
+ +

Using the iterator then looks like this:

+ +
let it = makeRangeIterator(1, 10, 2);
+
+let result = it.next();
+while (!result.done) {
+ console.log(result.value); // 1 3 5 7 9
+ result = it.next();
+}
+
+console.log("Iterated over sequence of size: ", result.value); // 5
+
+
+ +
+

It is not possible to know reflectively whether a particular object is an iterator. If you need to do this, use Iterables.

+
+ +

Generator functions

+ +

While custom iterators are a useful tool, their creation requires careful programming due to the need to explicitly maintain their internal state. Generator functions provide a powerful alternative: they allow you to define an iterative algorithm by writing a single function whose execution is not continuous. Generator functions are written using the {{jsxref("Statements/function*","function*")}} syntax. When called initially, generator functions do not execute any of their code, instead returning a type of iterator called a Generator. When a value is consumed by calling the generator's next method, the Generator function executes until it encounters the yield keyword.

+ +

The function can be called as many times as desired and returns a new Generator each time, however each Generator may only be iterated once.
+
+ We can now adapt the example from above. The behavior of this code is identical, but the implementation is much easier to write and read.

+ +
function* makeRangeIterator(start = 0, end = 100, step = 1) {
+    for (let i = start; i < end; i += step) {
+        yield i;
+    }
+}
+ +

Iterables

+ +

An object is iterable if it defines its iteration behavior, such as what values are looped over in a {{jsxref("Statements/for...of", "for...of")}} construct. Some built-in types, such as {{jsxref("Array")}} or {{jsxref("Map")}}, have a default iteration behavior, while other types (such as {{jsxref("Object")}}) do not.

+ +

In order to be iterable, an object must implement the @@iterator method, meaning that the object (or one of the objects up its prototype chain) must have a property with a {{jsxref("Symbol.iterator")}} key.
+
+ It may be possible to iterate over an iterable more than once, or only once. It is up to the programmer to know which is the case. Iterables which can iterate only once (e.g. Generators) customarily return this from their @@iterator method, where those which can be iterated many times must return a new iterator on each invocation of @@iterator.

+ +

User-defined iterables

+ +

We can make our own iterables like this:

+ +
var myIterable = {
+    *[Symbol.iterator]() {
+        yield 1;
+        yield 2;
+        yield 3;
+    }
+}
+
+for (let value of myIterable) {
+    console.log(value);
+}
+// 1
+// 2
+// 3
+
+or
+
+[...myIterable]; // [1, 2, 3]
+
+ +

Built-in iterables

+ +

{{jsxref("String")}}, {{jsxref("Array")}}, {{jsxref("TypedArray")}}, {{jsxref("Map")}} and {{jsxref("Set")}} are all built-in iterables, because their prototype objects all have a {{jsxref("Symbol.iterator")}} method.

+ +

Syntaxes expecting iterables

+ +

Some statements and expressions are expecting iterables, for example the {{jsxref("Statements/for...of","for-of")}} loops, {{jsxref("Operators/yield*","yield*")}}.

+ +
for (let value of ['a', 'b', 'c']) {
+    console.log(value);
+}
+// "a"
+// "b"
+// "c"
+
+[...'abc']; // ["a", "b", "c"]
+
+function* gen() {
+  yield* ['a', 'b', 'c'];
+}
+
+gen().next(); // { value: "a", done: false }
+
+[a, b, c] = new Set(['a', 'b', 'c']);
+a; // "a"
+
+
+ +

Advanced generators

+ +

Generators compute their yielded values on demand, which allows them to efficiently represent sequences that are expensive to compute, or even infinite sequences as demonstrated above.

+ +

The {{jsxref("Global_Objects/Generator/next","next()")}} method also accepts a value which can be used to modify the internal state of the generator. A value passed to next() will be treated as the result of the last yield expression that paused the generator.

+ +

Here is the fibonacci generator using next(x) to restart the sequence:

+ +
function* fibonacci() {
+  var fn1 = 0;
+  var fn2 = 1;
+  while (true) {
+    var current = fn1;
+    fn1 = fn2;
+    fn2 = current + fn1;
+    var reset = yield current;
+    if (reset) {
+        fn1 = 0;
+        fn2 = 1;
+    }
+  }
+}
+
+var sequence = fibonacci();
+console.log(sequence.next().value);     // 0
+console.log(sequence.next().value);     // 1
+console.log(sequence.next().value);     // 1
+console.log(sequence.next().value);     // 2
+console.log(sequence.next().value);     // 3
+console.log(sequence.next().value);     // 5
+console.log(sequence.next().value);     // 8
+console.log(sequence.next(true).value); // 0
+console.log(sequence.next().value);     // 1
+console.log(sequence.next().value);     // 1
+console.log(sequence.next().value);     // 2
+ +

You can force a generator to throw an exception by calling its {{jsxref("Global_Objects/Generator/throw","throw()")}} method and passing the exception value it should throw. This exception will be thrown from the current suspended context of the generator, as if the yield that is currently suspended were instead a throw value statement.

+ +

If the exception is not caught from within the generator,  it will propagate up through the call to throw(), and subsequent calls to next() will result in the done property being true.

+ +

Generators have a {{jsxref("Global_Objects/Generator/return","return(value)")}} method that returns the given value and finishes the generator itself.

+ +

{{PreviousNext("Web/JavaScript/Guide/Using_promises", "Web/JavaScript/Guide/Meta_programming")}}

diff --git a/files/zh-tw/web/javascript/guide/keyed_collections/index.html b/files/zh-tw/web/javascript/guide/keyed_collections/index.html new file mode 100644 index 0000000000..ef0fa369dc --- /dev/null +++ b/files/zh-tw/web/javascript/guide/keyed_collections/index.html @@ -0,0 +1,156 @@ +--- +title: 鍵值集合 +slug: Web/JavaScript/Guide/Keyed_collections +tags: + - Collections + - Guide + - JavaScript + - Map + - set +translation_of: Web/JavaScript/Guide/Keyed_collections +--- +
{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Indexed_Collections", "Web/JavaScript/Guide/Working_with_Objects")}}
+ +

本章介紹用 “key” 來整理的資料集合 ; Map 和 Set objects 相當於多個可重複的元素依照插入順序進行排序。

+ +

Maps

+ +

Map object

+ +

ECMAScript 2015 引進了新的資料結構用以映射變量至變量。A {{jsxref("Map")}} object is a simple key/value map and can iterate its elements in insertion order

+ +

The following code shows some basic operations with a Map. See also the {{jsxref("Map")}} reference page for more examples and the complete API. You can use a {{jsxref("Statements/for...of","for...of")}} loop to return an array of [key, value] for each iteration.

+ +
var sayings = new Map();
+sayings.set('dog', 'woof');
+sayings.set('cat', 'meow');
+sayings.set('elephant', 'toot');
+sayings.size; // 3
+sayings.get('fox'); // undefined
+sayings.has('bird'); // false
+sayings.delete('dog');
+sayings.has('dog'); // false
+
+for (var [key, value] of sayings) {
+  console.log(key + ' goes ' + value);
+}
+// "cat goes meow"
+// "elephant goes toot"
+
+sayings.clear();
+sayings.size; // 0
+
+ +

Object and Map compared

+ +

Traditionally, {{jsxref("Object", "objects", "", 1)}} have been used to map strings to values. Objects allow you to set keys to values, retrieve those values, delete keys, and detect whether something is stored at a key. Map objects, however, have a few more advantages that make them better maps.

+ + + +

These three tips can help you to decide whether to use a Map or an Object:

+ + + +

WeakMap object

+ +

The {{jsxref("WeakMap")}} object is a collection of key/value pairs in which the keys are objects only and the values can be arbitrary values. The object references in the keys are held weakly, meaning that they are a target of garbage collection (GC) if there is no other reference to the object anymore. The WeakMap API is the same as the Map API.

+ +

One difference to Map objects is that WeakMap keys are not enumerable (i.e., there is no method giving you a list of the keys). If they were, the list would depend on the state of garbage collection, introducing non-determinism.

+ +

For more information and example code, see also "Why WeakMap?" on the {{jsxref("WeakMap")}} reference page.

+ +

One use case of WeakMap objects is to store private data for an object or to hide implementation details. The following example is from Nick Fitzgerald's blog post "Hiding Implementation Details with ECMAScript 6 WeakMaps". The private data and methods belong inside the object and are stored in the privates WeakMap object. Everything exposed on the instance and prototype is public; everything else is inaccessible from the outside world because privates is not exported from the module

+ +
const privates = new WeakMap();
+
+function Public() {
+  const me = {
+    // Private data goes here
+  };
+  privates.set(this, me);
+}
+
+Public.prototype.method = function() {
+  const me = privates.get(this);
+  // Do stuff with private data in `me`...
+};
+
+module.exports = Public;
+
+ +

Sets

+ +

Set object

+ +

{{jsxref("Set")}} objects 是變數的集合。 You can iterate its elements in insertion order. A value in a Set may only occur once; it is unique in the Set's collection.

+ +

The following code shows some basic operations with a Set. See also the {{jsxref("Set")}} reference page for more examples and the complete API.

+ +
var mySet = new Set();
+mySet.add(1);
+mySet.add('some text');
+mySet.add('foo');
+
+mySet.has(1); // true
+mySet.delete('foo');
+mySet.size; // 2
+
+for (let item of mySet) console.log(item);
+// 1
+// "some text"
+
+ +

Array 和 Set 之間的相互轉換

+ +

You can create an {{jsxref("Array")}} from a Set using {{jsxref("Array.from")}} or the spread operator. Also, the Set constructor accepts an Array to convert in the other direction. Note again that Set objects store unique values, so any duplicate elements from an Array are deleted when converting.

+ +
Array.from(mySet);
+[...mySet2];
+
+mySet2 = new Set([1, 2, 3, 4]);
+
+ +

比較 Array 和 Set 

+ +

Traditionally, a set of elements has been stored in arrays in JavaScript in a lot of situations. The new Set object, however, has some advantages:

+ + + +

WeakSet object

+ +

{{jsxref("WeakSet")}} objects are collections of objects. An object in the WeakSet may only occur once; it is unique in the WeakSet's collection and objects are not enumerable.

+ +

The main differences to the {{jsxref("Set")}} object are:

+ + + +

The use cases of WeakSet objects are limited. They will not leak memory so it can be safe to use DOM elements as a key and mark them for tracking purposes, for example.

+ +

Key and value equality of Map and Set

+ +

Both the key equality of Map objects and the value equality of Set objects, are based on the "same-value-zero algorithm":

+ + + +

{{PreviousNext("Web/JavaScript/Guide/Indexed_Collections", "Web/JavaScript/Guide/Working_with_Objects")}}

diff --git a/files/zh-tw/web/javascript/guide/loops_and_iteration/index.html b/files/zh-tw/web/javascript/guide/loops_and_iteration/index.html new file mode 100644 index 0000000000..ca913c3d2e --- /dev/null +++ b/files/zh-tw/web/javascript/guide/loops_and_iteration/index.html @@ -0,0 +1,337 @@ +--- +title: Loops and iteration +slug: Web/JavaScript/Guide/Loops_and_iteration +tags: + - JavaScript + - Loop + - 教學 + - 迴圈 +translation_of: Web/JavaScript/Guide/Loops_and_iteration +--- +
{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Control_flow_and_error_handling", "Web/JavaScript/Guide/Functions")}}
+ +

迴圈提供一個快速又簡潔的方法來重複地做某件事。這個章節的JavaScript教學會介紹在JavaScript可以使用的幾種不同的迭代陳述式。 

+ +

你可以將迴圈想成一個電腦版本的"往一個方向走X步,然後往另一個方向走Y步"的遊戲;作為範例,"往東走五步"可以用這個方法用迴圈表示:

+ +
var step;
+for (step = 0; step < 5; step++) {
+  // 執行五次:從step為0到4
+  console.log('Walking east one step');
+}
+
+ +

有很多種不同種類的迴圈, 不過他們本質上都是做一樣的事:把一件動作重複地做一定的次數(而且也有可能做0次)。 各式各樣的迴圈機制提供了不同的方法來定義該迴圈的起始與結束。有些不同的情況下使用其中一種迴圈會比使用別種容易許多。

+ +

在javaScript中提供的迴圈陳述式分別為:

+ + + +

for 陳述式

+ +

一個for迴圈不斷重複直到一個指定的條件式判斷為false。JavaScript的for迴圈跟Java還有C的for迴圈很相似。一個for陳述式看起來像下面這樣:

+ +
for ([初始表達式]; [條件式]; [遞增表達式])
+  陳述式
+
+ +

當執行一個for迴圈時,會發生以下:

+ +
    +
  1. 如果有的話,初始表達式會被執行。這個表達式通常會初始化一或多個迴圈計數器,但是語法允許任何程度的複雜性。這個表達式也能用來宣告變數。
  2. +
  3. 條件式會被評估。如果評估出的值為true,迴圈的敘事式便會執行。如果評估出的值為false,這個for迴圈便會中止。如果條件式被省略了,狀態就會被假設是true。
  4. +
  5. 執行敘事式。要執行多個敘事式時,使用區塊敘事式({ ... }) 來把那些敘事式歸為一組。
  6. +
  7. 如果有更新表達式的遞增表達式便執行。然後return到第二步。
  8. +
+ +

範例

+ +

以下的函式包含一個用來數在一個滾動列表中被選過的選項(a {{HTMLElement("select")}} 允許複數選項的元素)的for陳述式 。這個for敘事式宣告了變數 i 並將其初始化為0。 他檢查 i ,如果 i 少於在<select>元素中的選項數量,進行接著的 if陳述式,並將 i 在每次通過迴圈後遞增。

+ +
<form name="selectForm">
+  <p>
+    <label for="musicTypes">Choose some music types, then click the button below:</label>
+    <select id="musicTypes" name="musicTypes" multiple="multiple">
+      <option selected="selected">R&B</option>
+      <option>Jazz</option>
+      <option>Blues</option>
+      <option>New Age</option>
+      <option>Classical</option>
+      <option>Opera</option>
+    </select>
+  </p>
+  <p><input id="btn" type="button" value="How many are selected?" /></p>
+</form>
+
+<script>
+function howMany(selectObject) {
+  var numberSelected = 0;
+  for (var i = 0; i < selectObject.options.length; i++) {
+    if (selectObject.options[i].selected) {
+      numberSelected++;
+    }
+  }
+  return numberSelected;
+}
+
+var btn = document.getElementById("btn");
+btn.addEventListener("click", function(){
+  alert('Number of options selected: ' + howMany(document.selectForm.musicTypes))
+});
+</script>
+
+
+ +

do...while 陳述式

+ +

do...while 陳述式會不斷重複直到一個特定的條件判斷為false。一個do...while 陳述式看起來像以下:

+ +
do
+  陳述式
+while (條件式);
+
+ +

陳述式會在檢查條件式以前先執行一次。要執行多個陳述式的話,使用區塊陳述式來將那些陳述式歸為一組。如果條件式為true,那陳述式便再次執行。在每次執行的最後,條件會被檢查。當條件式為false時, 停止執行並把控制傳給 do...while接著的陳述式。

+ +

範例

+ +

在下列範例中,do迴圈重複了至少一次並不斷重複直到 i 不再比 5 少。

+ +
var i = 0;
+do {
+  i += 1;
+  console.log(i);
+} while (i < 5);
+ +

while 陳述式

+ +

while 陳述式會不斷執行它的陳述式只要指定的條件式判斷為true。一個while陳述式看起來如下:

+ +
while (condition)
+  statement
+
+ +

如果條件式變成 false ,在迴圈中的陳述式會停止執行並控制交給跟在這個迴圈後面的陳述式。

+ +

條件式的測試發生於迴圈內的陳述式執行之前。如果條件式傳回 true ,陳述式便會被執行而判斷式會再被測試一次。如果條件式傳回 false ,停止執行並把控制交給跟在 while 迴圈後面的陳述式。

+ +

要執行多個陳述式的話,使用區塊陳述式來將那些陳述式歸為一組。

+ +

範例 1

+ +

以下的while迴圈在只要n比3少的情況下便會不斷重複:

+ +
var n = 0;
+var x = 0;
+while (n < 3) {
+  n++;
+  x += n;
+}
+
+ +

在每次的疊代,迴圈把 n 遞增並將其值加到 x 上。因此,x 跟 n 的值會是下列情況:

+ + + +

在完成第三次迴圈後,判斷是 n<3 不再是 true ,所以迴圈終止。

+ +

範例 2

+ +

避免無限迴圈。確定在迴圈內的判斷式終究會變成 false; 不然迴圈會永遠不終止。在迴圈內的陳述式會永遠的執行因為判斷式永遠不會變成false:

+ +
while (true) {
+  console.log("Hello, world");
+}
+ +

label 陳述式

+ +

 label 提供一個有識別字的陳述式讓你能在程式的別的地方參考。舉個例子,你能使用label 來識別一個迴圈,然後使用break或continue陳述式來指示何時程式該中斷迴圈或是繼續他的執行。

+ +

label 陳述式的語法看起來如下:

+ +
label :
+   statement
+
+ +

Label的值可以是任何不是保留字的JavaScript識別字。你用label所識別的陳述式可以是任何陳述式。

+ +

範例

+ +

在這個範例,markLoop這個label 識別一個while 迴圈。

+ +
markLoop:
+while (theMark == true) {
+   doSomething();
+}
+ +

break 陳述式

+ +

break 陳述式是用來終止一個迴圈,一個switch多重控制選項,或是和一個label陳述式聯合使用。

+ + + +

break 陳述式的語法看起來如下:

+ +
    +
  1. break;
  2. +
  3. break label;
  4. +
+ +

第一種語法會終止最內部的迴圈或switch區間;第二種語法會終止那個特定的label陳述式。

+ +

範例 1

+ +

以下的範例會不斷重複跑迴圈直到有在陣列裡的元素符合 theValue 的值:

+ +
for (var i = 0; i < a.length; i++) {
+  if (a[i] == theValue) {
+    break;
+  }
+}
+ +

範例 2: Break至一個label陳述式

+ +
var x = 0;
+var z = 0;
+labelCancelLoops: while (true) {
+  console.log("Outer loops: " + x);
+  x += 1;
+  z = 1;
+  while (true) {
+    console.log("Inner loops: " + z);
+    z += 1;
+    if (z === 10 && x === 10) {
+      break labelCancelLoops;
+    } else if (z === 10) {
+      break;
+    }
+  }
+}
+
+ +

continue 陳述式

+ +

continue 陳述式可以用於重新開始一個 while , do-while, for, 或 label 陳述式。

+ + + +

continue 陳述式的語法看起來如下:

+ +
    +
  1. continue;
  2. +
  3. continue label;
  4. +
+ +

範例 1

+ +

以下的範例有while迴圈以及一個在 i 的值為 3 的時候執行的continue陳述式。因此,n的值會連著是 1, 3, 7, 12。

+ +
var i = 0;
+var n = 0;
+while (i < 5) {
+  i++;
+  if (i == 3) {
+    continue;
+  }
+  n += i;
+}
+
+ +

範例 2

+ +

一個被label成 checkiandj 的陳述式包還著一個被label成 checkj 的陳述式。如果遇到了continue,程式會終止現在的這輪迴圈並開始下一輪。每次遇到continue,checkj就會一直重複直到它的條件式返回false。當false被傳回時,checkiandj 陳述式剩下的陳述式已被完成,而checkiandj 也會繼續重複直到它的條件式傳回 false。當false被傳回,程式會繼續進行接著checkiandj後面的陳述式。

+ +

如果continue有了checkiandj的label 程式會從checkiandj陳述式的頭開始繼續。

+ +
checkiandj:
+  while (i < 4) {
+    console.log(i);
+    i += 1;
+    checkj:
+      while (j > 4) {
+        console.log(j);
+        j -= 1;
+        if ((j % 2) == 0) {
+          continue checkj;
+        }
+        console.log(j + " is odd.");
+      }
+      console.log("i = " + i);
+      console.log("j = " + j);
+  }
+ +

for...in 陳述式

+ +

for...in 陳述式重複一個指定的變數來循環一個物件所有可枚舉的屬性。至於每個獨特的屬性,JavaScript執行特定的陳述式。一個for...in 陳述式看起來像以下:

+ +
for (variable in object) {
+  statements
+}
+
+ +

範例

+ +

以下的函式透過它的參數得到一個物件和物件的名字。接著它循環這個物件的所有屬性並傳回一個列出屬性名和值的字串。

+ +
function dump_props(obj, obj_name) {
+  var result = "";
+  for (var i in obj) {
+    result += obj_name + "." + i + " = " + obj[i] + "<br>";
+  }
+  result += "<hr>";
+  return result;
+}
+
+ +

對於一個擁有make跟model屬性的物件 car來說,執行結果是:

+ +
car.make = Ford
+car.model = Mustang
+
+ +

陣列

+ +

雖然用for...in來迭代 {{jsxref("Array")}} 元素很吸引人,但是它傳回的除了數字的索引之外還有可能是你自己定的屬性名。因此還是用帶有數字索引的傳統for迴圈來迭帶一個陣列會比較好。因為如果你想改變陣列物件,比如增加屬性或是方法,for...in 陳述式迭代的是自定的屬性而不是陣列的元素。

+ +

for...of 陳述式

+ +

 for...of 陳述式在iterable objects(可迭代的物件)上建立了一個循環 (包含 {{jsxref("Array")}}, {{jsxref("Map")}}, {{jsxref("Set")}}, arguments(參數) 物件 等等), 對每個獨特屬性的值使用一個準備被執行的有陳述式的自訂迭代掛勾。

+ +
for (variable of object) {
+  statement
+}
+ +

下列的範例可看出for...of 迴圈跟 for...in 迴圈的差別。 for...in 在屬性名字循環,而for...of 在屬性的值循環。

+ +
let arr = [3, 5, 7];
+arr.foo = "hello";
+
+for (let i in arr) {
+   console.log(i); // logs "0", "1", "2", "foo"
+}
+
+for (let i of arr) {
+   console.log(i); // logs 3, 5, 7
+}
+
+ +

{{PreviousNext("Web/JavaScript/Guide/Control_flow_and_error_handling", "Web/JavaScript/Guide/Functions")}}

diff --git a/files/zh-tw/web/javascript/guide/numbers_and_dates/index.html b/files/zh-tw/web/javascript/guide/numbers_and_dates/index.html new file mode 100644 index 0000000000..88ed75fd3e --- /dev/null +++ b/files/zh-tw/web/javascript/guide/numbers_and_dates/index.html @@ -0,0 +1,383 @@ +--- +title: 數字與日期 +slug: Web/JavaScript/Guide/Numbers_and_dates +translation_of: Web/JavaScript/Guide/Numbers_and_dates +--- +
{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Expressions_and_Operators", "Web/JavaScript/Guide/Text_formatting")}}
+ +

這個章節將介紹如何在 JavaScript 中處理數字與日期。

+ +

數字(Numbers)

+ +

在 JavaScript 中, Number所使用的標準依照 double-precision 64-bit binary format IEEE 754 (i.e. number的區間是 -(253 -1) 到 253 -1)。整數是沒有特定的類型

+ +

此外還可以顯示浮點數,三種符號數值: +{{jsxref("Infinity")}}, -{{jsxref("Infinity")}}, and {{jsxref("NaN")}} (not-a-number)。

+ +

{{jsxref("BigInt")}} 是Javascript最新的功能,它可以表示一個很大的整數。使用 BigInt需要注意一點BigInt 和{{jsxref("Number")}}不能在同一個operation混用還有當用 {{jsxref("Math")}} 物件時不能使用BigInt

+ +

請參照 JavaScript data types and structures 來取得更多詳細資料。

+ +

你可以用四種進制表示數字:十進制 (decimal),二進制 (binary),八進制 (octal) 以及十六進制 (hexadecimal)。

+ +

十進制數值

+ +
1234567890
+42
+
+// 以零為開頭時要小心:
+
+0888 // 888 解析為 十進制數值
+0777 // 在 non-strict 模式下將解析成八進制 (等同於十進制的 511)
+
+ +

請注意,十進位數字允許第一個數字設為零(0)的話,前提是後面接的數字必須要有一個數字大於8(例如輸入0888結果會是888,輸入068結果會是68),不然則會被轉成8進位(例如0777結果會是511,輸入063結果會是51)。

+ +

二進制數值

+ +

二進制數值以 0 為開頭並跟著一個大寫或小寫的英文字母 「B」 (0b 或 0B)。如果 0b 後面接著的數字不是 0 或 1,那會丟出 SyntaxError(語法錯誤): "Missing binary digits after 0b"。

+ +
var FLT_SIGNBIT  = 0b10000000000000000000000000000000; // 2147483648
+var FLT_EXPONENT = 0b01111111100000000000000000000000; // 2139095040
+var FLT_MANTISSA = 0B00000000011111111111111111111111; // 8388607
+ +

八進制數值

+ +

八進制數值以 0 為開頭。如果 0 後面的數字超出 0 到 7 這個範圍,將會被解析成十進制數值。

+ +
var n = 0755; // 493
+var m = 0644; // 420
+
+ +

Strict mode in ECMAScript 5 forbids octal syntax. Octal syntax isn't part of ECMAScript 5, but it's supported in all browsers by prefixing the octal number with a zero: 0644 === 420 and"\045" === "%". In ECMAScript 2015, octal numbers are supported if they are prefixed with 0o, e.g.: 

+ +
var a = 0o10; // ES2015: 8
+
+ +

十六進制數值

+ +

十六進制數值以 0 為開頭並跟著一個大寫或小寫的英文字母 「X」(0x 或 0X)。如果 0b 後面接著的值超出範圍 (0123456789ABCDEF),那會丟出 SyntaxError(語法錯誤):"Identifier starts immediately after numeric literal"。

+ +
0xFFFFFFFFFFFFFFFFF // 295147905179352830000
+0x123456789ABCDEF   // 81985529216486900
+0XA                 // 10
+
+ +

指數運算

+ +
1E3   // 1000
+2e6   // 2000000
+0.1e2 // 10
+ +

Number 物件

+ +

The built-in {{jsxref("Number")}} object has properties for numerical constants, such as maximum value, not-a-number, and infinity. You cannot change the values of these properties and you use them as follows:

+ +
var biggestNum = Number.MAX_VALUE;
+var smallestNum = Number.MIN_VALUE;
+var infiniteNum = Number.POSITIVE_INFINITY;
+var negInfiniteNum = Number.NEGATIVE_INFINITY;
+var notANum = Number.NaN;
+
+ +

You always refer to a property of the predefined Number object as shown above, and not as a property of a Number object you create yourself.

+ +

下面這張表格整理了 Number 物件的屬性

+ +

Number 的屬性

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
屬性描述
{{jsxref("Number.MAX_VALUE")}}可表示的最大數值
{{jsxref("Number.MIN_VALUE")}}可表示的最小數值
{{jsxref("Number.NaN")}}表示「非數值」(Not-A-Number)的數值
{{jsxref("Number.NEGATIVE_INFINITY")}}Special negative infinite value; returned on overflow
{{jsxref("Number.POSITIVE_INFINITY")}}Special positive infinite value; returned on overflow
{{jsxref("Number.EPSILON")}}Difference between one and the smallest value greater than one that can be represented as a {{jsxref("Number")}}.
{{jsxref("Number.MIN_SAFE_INTEGER")}}可以在 JavaScript 中安全表示的最小數值。
{{jsxref("Number.MAX_SAFE_INTEGER")}}可以在 JavaScript 中安全表示的最大數值。
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Number 的方法
方法描述
{{jsxref("Number.parseFloat()")}}字串轉換成浮點數。
+ 等同於全域函式 {{jsxref("parseFloat", "parseFloat()")}} 。
{{jsxref("Number.parseInt()")}}以指定的基數將字串轉換成整數。
+ 等同於全域函式 {{jsxref("parseInt", "parseInt()")}} 。
{{jsxref("Number.isFinite()")}}判定給定的值是不是一個有限數。
{{jsxref("Number.isInteger()")}}判定給定的值是不是一個整數
{{jsxref("Number.isNaN()")}}Determines whether the passed value is {{jsxref("Global_Objects/NaN", "NaN")}}. More robust version of the original global {{jsxref("Global_Objects/isNaN", "isNaN()")}}.
{{jsxref("Number.isSafeInteger()")}}Determines whether the provided value is a number that is a safe integer.
+ +

The Number prototype provides methods for retrieving information from Number objects in various formats. The following table summarizes the methods of Number.prototype.

+ + + + + + + + + + + + + + + + + + + + + + + +
Number.prototype 的方法
方法描述
{{jsxref("Number.toExponential", "toExponential()")}}Returns a string representing the number in exponential notation.
{{jsxref("Number.toFixed", "toFixed()")}}Returns a string representing the number in fixed-point notation.
{{jsxref("Number.toPrecision", "toPrecision()")}}Returns a string representing the number to a specified precision in fixed-point notation.
+ +

Math 物件

+ +

The built-in {{jsxref("Math")}} object has properties and methods for mathematical constants and functions. For example, the Math object's PI property has the value of pi (3.141...), which you would use in an application as

+ +
Math.PI
+
+ +

Similarly, standard mathematical functions are methods of Math. These include trigonometric, logarithmic, exponential, and other functions. For example, if you want to use the trigonometric function sine, you would write

+ +
Math.sin(1.56)
+
+ +

Note that all trigonometric methods of Math take arguments in radians.

+ +

The following table summarizes the Math object's methods.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Math 的方法
方法描述
{{jsxref("Math.abs", "abs()")}}絕對值
{{jsxref("Math.sin", "sin()")}}, {{jsxref("Math.cos", "cos()")}}, {{jsxref("Math.tan", "tan()")}}三角函數; 引數以弳度表示
{{jsxref("Math.asin", "asin()")}}, {{jsxref("Math.acos", "acos()")}}, {{jsxref("Math.atan", "atan()")}}, {{jsxref("Math.atan2", "atan2()")}}反三角函數; 回傳值以弳度表示
{{jsxref("Math.sinh", "sinh()")}}, {{jsxref("Math.cosh", "cosh()")}}, {{jsxref("Math.tanh", "tanh()")}}雙曲函數; 引數以 hyperbolic angle 表示
{{jsxref("Math.asinh", "asinh()")}}, {{jsxref("Math.acosh", "acosh()")}}, {{jsxref("Math.atanh", "atanh()")}}反雙曲函數; 回傳值以 hyperbolic angle 表示
+

{{jsxref("Math.pow", "pow()")}}, {{jsxref("Math.exp", "exp()")}}, {{jsxref("Math.expm1", "expm1()")}}, {{jsxref("Math.log10", "log10()")}}, {{jsxref("Math.log1p", "log1p()")}}, {{jsxref("Math.log2", "log2()")}}

+
指數及對數函式
{{jsxref("Math.floor", "floor()")}}, {{jsxref("Math.ceil", "ceil()")}}回傳小於等於/大於等於指定數字的最大/最小整數
{{jsxref("Math.min", "min()")}}, {{jsxref("Math.max", "max()")}}Returns lesser or greater (respectively) of comma separated list of numbers arguments
{{jsxref("Math.random", "random()")}}回傳一個介於 0 到 1 之間的數值
{{jsxref("Math.round", "round()")}}, {{jsxref("Math.fround", "fround()")}}, {{jsxref("Math.trunc", "trunc()")}},Rounding and truncation functions.
{{jsxref("Math.sqrt", "sqrt()")}}, {{jsxref("Math.cbrt", "cbrt()")}}, {{jsxref("Math.hypot", "hypot()")}}Square root, cube root, Square root of the sum of square arguments.
{{jsxref("Math.sign", "sign()")}}The sign of a number, indicating whether the number is positive, negative or zero.
{{jsxref("Math.clz32", "clz32()")}},
+ {{jsxref("Math.imul", "imul()")}}
Number of leading zero bits in the 32-bit binary representation.
+ The result of the C-like 32-bit multiplication of the two arguments.
+ +

Unlike many other objects, you never create a Math object of your own. You always use the built-in Math object.

+ +

Date 物件

+ +

JavaScript 沒有所謂日期(date)的數據型態(data type)。你可以使用 {{jsxref("Date")}} 物件及其方法去設定日期跟時間來滿足你的需求 。Date 物件有大量的設定取得操作日期的方法(method),但它沒有屬性。

+ +

JavaScript 處理日期的方式跟Java類似。這兩個語言有許多一樣的date方法,且都將日期儲存為從1970年1月1號0時0分0秒以來的毫秒數(millisecond)。

+ +

Date 物件範圍是 -100,000,000 days to 100,000,000 days 以1970年1月1號0時0分0秒UTC為基準。

+ +

創建一個Date物件:

+ +
var dateObjectName = new Date([parameters]);
+
+ +

在這裡創建一個名為dateObjectNameDate 物件;它可以是一個新的物件或是某個以存在的物件當中的屬性。

+ +

Calling Date without the new keyword returns a string representing the current date and time.

+ +

The parameters in the preceding syntax can be any of the following:

+ + + +

 Date 的方法

+ +

The Date object methods for handling dates and times fall into these broad categories:

+ + + +

With the "get" and "set" methods you can get and set seconds, minutes, hours, day of the month, day of the week, months, and years separately. There is a getDay method that returns the day of the week, but no corresponding setDay method, because the day of the week is set automatically. These methods use integers to represent these values as follows:

+ + + +

舉例,假設你定義了一個日期如下:

+ +
var Xmas95 = new Date('December 25, 1995');
+
+ +

那 Xmas95.getMonth() 將會回傳 11, Xmas95.getFullYear() 會回傳 1995。

+ +

getTime 及 setTime 這兩個方法對於比較日期有幫助。 The getTime method returns the number of milliseconds since January 1, 1970, 00:00:00 for a Date object.

+ +

For example, the following code displays the number of days left in the current year:

+ +
var today = new Date();
+var endYear = new Date(1995, 11, 31, 23, 59, 59, 999); // Set day and month
+endYear.setFullYear(today.getFullYear()); // Set year to this year
+var msPerDay = 24 * 60 * 60 * 1000; // Number of milliseconds per day
+var daysLeft = (endYear.getTime() - today.getTime()) / msPerDay;
+var daysLeft = Math.round(daysLeft); //returns days left in the year
+
+ +

This example creates a Date object named today that contains today's date. It then creates a Date object named endYear and sets the year to the current year. Then, using the number of milliseconds per day, it computes the number of days between today and endYear, using getTime and rounding to a whole number of days.

+ +

The parse method is useful for assigning values from date strings to existing Date objects. For example, the following code uses parse and setTime to assign a date value to the IPOdate object:

+ +
var IPOdate = new Date();
+IPOdate.setTime(Date.parse('Aug 9, 1995'));
+
+ +

範例

+ +

下面這個範例,JSClock() 這個函式將會以數位時鐘的格式回傳時間。

+ +
function JSClock() {
+  var time = new Date();
+  var hour = time.getHours();
+  var minute = time.getMinutes();
+  var second = time.getSeconds();
+  var temp = '' + ((hour > 12) ? hour - 12 : hour);
+  if (hour == 0)
+    temp = '12';
+  temp += ((minute < 10) ? ':0' : ':') + minute;
+  temp += ((second < 10) ? ':0' : ':') + second;
+  temp += (hour >= 12) ? ' P.M.' : ' A.M.';
+  return temp;
+}
+
+ +

 JSClock 這個函式會先建立一個名為 time 的 Date 物件; 因為沒有提供任何引數,所以會放入目前的日期及時間。接著呼叫 getHours 、 getMinutes 以及 getSeconds 這三個方法將現在的時、分以及秒分別指定給 hour 、 minute 以及 second 這三個變數。

+ +

接著的四行指令將會建立一個時間的字串。第一行的指令建立了一個變數 temp,以條件運算式指定值; 如果 hour 大於 12,那就指定 (hour - 12),不然會直接指定 hour, 但如果 hour 等於 0 , 則改為 12。

+ +

接著下一行將 minute 加到 temp 中。如果 minute 小於 10, 則會在附加時補上一個零; 不然的話會直接加上冒號及分鐘數。秒數也是以同樣的作法附加到 temp 上。

+ +

最後,判斷 hour 是不是大於等於 12 ,如果是就在 temp 加上 "P.M." ,不然就加上 "A.M."。

+ +

{{PreviousNext("Web/JavaScript/Guide/Expressions_and_Operators", "Web/JavaScript/Guide/Text_formatting")}}

diff --git a/files/zh-tw/web/javascript/guide/regular_expressions/index.html b/files/zh-tw/web/javascript/guide/regular_expressions/index.html new file mode 100644 index 0000000000..f9b4235c2a --- /dev/null +++ b/files/zh-tw/web/javascript/guide/regular_expressions/index.html @@ -0,0 +1,700 @@ +--- +title: 正規表達式 +slug: Web/JavaScript/Guide/Regular_Expressions +tags: + - Guide + - JavaScript + - RegExp + - 正規表達式 +translation_of: Web/JavaScript/Guide/Regular_Expressions +--- +

{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Text_formatting", "Web/JavaScript/Guide/Indexed_collections")}}

+ +

正規表達式是被用來匹配字串中字元組合的模式。在 JavaScript 中,正規表達式也是物件,這些模式在 {{jsxref("RegExp")}} 的 {{jsxref("RegExp.exec", "exec")}} 和 {{jsxref("RegExp.test", "test")}} 方法中,以及 {{jsxref("String")}} 的 {{jsxref("String.match", "match")}}、{{jsxref("String.replace", "replace")}}、{{jsxref("String.search", "search")}}、{{jsxref("String.split", "split")}} 等方法中被運用。這一章節將解說 JavaScript 中的正規表達式。

+ +

建立正規表達式

+ +

您可透過下列兩種方法去創建一條正規表達式:

+ +

使用正規表達式字面值(regular expression literal),包含兩個 / 字元之間的模式如下:

+ +
var re = /ab+c/;
+
+ +

正規表達式字面值在 script 載入時會被編譯,當正規表達式為定值時,使用此方法可獲得較佳效能。

+ +

或呼叫 {{jsxref("RegExp")}} 物件的建構函式,如下:

+ +
var re = new RegExp('ab+c');
+
+ +

使用建構子函數供即時編譯正則表達式,當模式會異動、事先未知匹配模式、或者您將從其他地方取得時,使用建構子函數將較為合適。

+ +

撰寫正規表達模式

+ +

正規表達模式由數個簡易字元組成,例如 /abc/,或是由簡易字元及特殊符號組合而成,例如 /ab*c//Chapter (\d+)\.\d*/ )。最後一個範例用到了括號,這在正規表達式中用作記憶組,使用括號的匹配將會被留到後面使用,在 {{ web.link("#Using_Parenthesized_Substring_Matches", "使用帶括號的配對子字串 Using Parenthesized Substring Matches") }} 有更多解釋。

+ +

使用簡易模式

+ +

簡易的模式是有你找到的直接匹配所構成的。比如:/abc/ 這個模式就匹配了在一個字符串中,僅僅字符 'abc' 同時出現並按照這個順序。這兩個句子中「Hi, do you know your abc's?」和「The latest airplane designs evolved from slabcraft.」就會匹配成功。在上面的兩個實例中,匹配的是子字符串 'abc'。在字符串中的 "Grab crab"('ab c') 中將不會被匹配,因為它不包含任何的 'abc' 字符串。

+ +

使用特殊字元

+ +

當你需要搜尋一個比直接匹配需要更多條件的匹配,比如搜尋一或多個 'b',或者搜尋空格,那麼這個模式將要包含特殊字符。例如: 模式 /ab*c/ 匹配了一個單獨的 'a' 後面跟了零或多個 'b'(* 的意思是前面一項出現了零或多個),且後面跟著 'c' 的任何字符組合。在字符串 "cbbabbbbcdebc" 中,這個模式匹配了子字符串 "abbbbc"。

+ +

下面的表格列出了在正則表達式中可以利用的特殊字符完整列表以及描述。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
正則表達式中的特殊字元
字元解說
\ +

反斜線放在非特殊符號前面,使非特殊符號不會被逐字譯出,代表特殊作用。
+ 例如:'b' 如果沒有 '\' 在前頭,功能是找出小寫 b;若改為 '\b' 則代表的是邊界功能,block 用意。
+ /\bter\b/.test("interest")         //false
+ /\bter\b/.test("in ter est")       //true
+
+ /a*/ 找出0個或是1個以上的a;而 /a\*/ 找出 'a*' 這個字串
+ /aaaa*/g.test("caaady")    //true
+ /a\*/.test("caaady")           //false
+
+ '\' 也能使自身表現出來,表現 '\' ,必須以 '\\' 表達。
+ /[\\]/.test(">\\<")                 //true

+
^ +

匹配輸入的開頭,如果 multiline flag 被設為 true,則會匹配換行字元後。

+ +

例如:/^A/ 不會匹配「an A」的 A,但會匹配「An E」中的 A。

+ +

^」出現在字元集模式的字首中有不同的意思,詳見補字元集

+
$ +

匹配輸入的結尾,如果 multiline flag 被設為 true,則會匹配換行字元。

+ +

例如:/t$/ 不會匹配「eater」中的 t,卻會匹配「eat」中的 t。

+
* +

匹配前一字元 0 至多次。
+
+ 下面舉例要求基本 'aaaa' ,'a*' 後面有0個或多個a的意思
+ /aaaaa*/g.test("caaady")   //false

+ +

例如:/bo*/ 匹配「A ghost booooed」中的 boooo、「A bird warbled」中的 b,但在「A goat grunted」中不會匹配任何字串。

+
+ +

匹配前一字元 1 至多次,等同於 {1,}

+ +

例如:/a+/ 匹配「candy」中的 a,以及所有「caaaaaaandy」中的 a。

+
? +

匹配前一字元 0 至 1 次,等同於 {0,1}

+ +

例如:/e?le?/ 匹配「angel」中的 el、「angle」中的 le、以及「oslo」中的 l。

+ +

如果是使用在 *+?{} 等 quantifier 之後,將會使這些 quantifier non-greedy(也就是儘可能匹配最少的字元),與此相對的是 greedy(匹配儘可能多的字元)。例如:在「123abc」中應用 /\d+/ 可匹配「123」,但使用 /\d+?/ 在相同字串上只能匹配「1」。

+ +


+ Also used in lookahead assertions, as described in the x(?=y) and x(?!y) entries of this table.

+
. +

(小數點)匹配除了換行符號之外的單一字元。

+ +

例如:/.n/ 匹配「nay, an apple is on the tree」中的 an 和 on,但在「nay」中沒有匹配。

+
(x) +

Capturing Parentheses

+ +

匹配 'x' 並記住此次的匹配,如下面的範例所示。

+ +

在 正則表達示 /(foo) (bar) \1 \2/ 中的 (foo) 與 (bar) 可匹配了 "foo bar foo bar" 這段文字中的前兩個字,而 \1 與 \2 則匹配了後面的兩個字。注意, \1, \2, ..., \n 代表的就是前面的pattern,以本範例來說,/(foo) (bar) \1 \2/  等同於  /(foo) (bar) (foo) (bar)/。

+ +

用於取代(replace)的話,則是用 $1, $2,...,$n。如 'bar boo'.replace(/(...) (...)/, '$2 $1').
+ $& means the whole matched string.

+
(?:x) +

Non-Capturing Parentheses

+ +

找出 'x',這動作不會記憶
+ ()為 group 的意思,檢查時會再 wrap 一次,若有 g flag 會無效,
+ ?: 代表只要 group 就好,不要 wrap

+ +

有無 () 差別 ?
+ 'foo'.match(/(foo)/) 
+ // ['foo', 'foo', index: 0, input: 'foo' ]
+ 'foo'.match(/foo/)
+ // [ 'foo', index: 0, input: 'foo' ]

+ +

 有無?:差別?
+ 'foo'.match(/(foo){1,2}/)
+ // [ 'foo', 'foo', index: 0, input: 'foo' ]
+ 'foo'.match(/(?:foo){1,2}/)
+ [ 'foo', index: 0, input: 'foo' ]

+ 連()都沒有,則{1,2}只是適用在foo的第二個o的條件而已。

+ +

更多資訊詳見 Using parentheses 。

+
x(?=y) +

符合'x',且後接的是'y'。'y'為'x'存在的意義。
+
+ 例如:/Jack(?=Sprat)/,在後面是Sprat的存在下,Jack才有意義。
+ /Jack(?=Sprat|Frost)/後面是Sprat「或者是」Frost的存在下,Jack才有意義。但我們要找的目標是Jack,後面的條件都只是filter/條件的功能而已。

+
x(?!y) +

符合'x',且後接的不是'y'。'y'為否定'x'存在的意義,後面不行前功盡棄(negated lookahead)。
+
+ 例如: /\d+(?!\.)/ ,要找一個或多個數字時,在後面接的不是「點」的情況下成立。
+
+ var result = /\d+(?!\.)/.exec("3.141") ,
+ result執行出來為[ '141', index: 2, input: '3.141'],
+ index:2,代表141從index = 2開始。

+
x|y +

符合「x」或「y」。

+ +

舉例來說,/green|red/  的話,會匹配 "green apple" 中的 "green" 以及 "red apple." 的 "red"

+
{n} +

規定符號確切發生的次數,n為正整數

+ +

例如:/a{2}/無法在 "candy" 找到、但 "caandy" 行;若字串擁有2個以上 "caaandy" 還是只會認前面2個。

+
{n,m} +

搜尋條件:n為至少、m為至多,其n、m皆為正整數。若把m設定為0,則為Invalid regular expression。

+ +

例如:/a{1,3}/ 無法在 "cndy" 匹配到;而在 "candy" 中的第1個"a"符合;在 "caaaaaaandy" 中的前3個 "aaa" 符合,雖然此串有許多a,但只認前面3個。

+
[xyz]字元的集合。此格式會匹配中括號內所有字元, including escape sequences。特殊字元,例如點(.) 和米字號(*),在字元集合中不具特殊意義,所以不需轉換。若要設一個字元範圍的集合,可以使用橫線 "-" ,如下例所示:
+
+ [a-d] 等同於 [abcd]。會匹配 "brisket" 的 "b" 、"city" 的 'c' ……等。 而/[a-z.]+/ /[\w.]+/ 均可匹配字串 "test.i.ng" 。
[^xyz] +

bracket中寫入的字元將被否定,匹配非出現在bracket中的符號。
+  可用 '-' 來界定字元的範圍。一般直接表達的符號都可以使用這種方式。

+ +

[^abc]可以寫作[^a-c]. "brisket" 中找到 'r' 、"chop."中找到 'h'。

+
[\b]吻合倒退字元 (U+0008). (不會跟 \b 混淆)
\b +

吻合文字邊界。A word boundary matches the position where a word character is not followed or preceded by another word-character. Note that a matched word boundary is not included in the match. In other words, the length of a matched word boundary is zero. (Not to be confused with [\b].)

+ +

Examples:
+ /\bm/ matches the 'm' in "moon" ;
+ /oo\b/ does not match the 'oo' in "moon", because 'oo' is followed by 'n' which is a word character;
+ /oon\b/ matches the 'oon' in "moon", because 'oon' is the end of the string, thus not followed by a word character;
+ /\w\b\w/ will never match anything, because a word character can never be followed by both a non-word and a word character.

+ +

Note: JavaScript's regular expression engine defines a specific set of characters to be "word" characters. Any character not in that set is considered a word break. This set of characters is fairly limited: it consists solely of the Roman alphabet in both upper- and lower-case, decimal digits, and the underscore character. Accented characters, such as "é" or "ü" are, unfortunately, treated as word breaks.

+
\B +

吻合非文字邊界。This matches a position where the previous and next character are of the same type: Either both must be words, or both must be non-words. The beginning and end of a string are considered non-words.

+ +

For example, /\B../ matches 'oo' in "noonday", and /y\B./ matches 'ye' in "possibly yesterday."

+
\cX +

Where X is a character ranging from A to Z. Matches a control character in a string.

+ +

For example, /\cM/ matches control-M (U+000D) in a string.

+
\d +

吻合數字,寫法等同於 [0-9] 。

+ +

例如:/\d/ 或 /[0-9]/ 在 "B2 is the suite number." 中找到 '2'

+
\D +

吻合非數字,寫法等同於 [^0-9]。

+ +

例如:/\D/ 或/[^0-9]/ 在 "B2 is the suite number." 中找到 'B' 。

+
\fMatches a form feed (U+000C).
\nMatches a line feed (U+000A).
\rMatches a carriage return (U+000D).
\s +

Matches a single white space character, including space, tab, form feed, line feed. Equivalent to [ \f\n\r\t\v\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff].

+ +

For example, /\s\w*/ matches ' bar' in "foo bar."

+
\S +

Matches a single character other than white space. Equivalent to [^ \f\n\r\t\v​\u00a0\​\u1680u180e\u2000​\u2001\u2002​\u2003\u2004​\u2005\u2006​\u2007\u2008​\u2009\u200a​\u2028\u2029​\u202f\u205f​\u3000].

+ +

For example, /\S\w*/ matches 'foo' in "foo bar."

+
\tMatches a tab (U+0009).
\vMatches a vertical tab (U+000B).
\w +

包含數字字母與底線,等同於[A-Za-z0-9_]。

+ +

例如: /\w/ 符合 'apple'中的 'a' 、'$5.28中的 '5' 以及 '3D' 中的 '3'。

+ +

For example, /\w/ matches 'a' in "apple," '5' in "$5.28," and '3' in "3D."

+
\W +

Matches any non-word character. Equivalent to [^A-Za-z0-9_].

+ +

For example, /\W/ or /[^A-Za-z0-9_]/ matches '%' in "50%."

+
\n +

Where n is a positive integer, a back reference to the last substring matching the n parenthetical in the regular expression (counting left parentheses).

+ +

For example, /apple(,)\sorange\1/ matches 'apple, orange,' in "apple, orange, cherry, peach."

+
\0Matches a NULL (U+0000) character. Do not follow this with another digit, because \0<digits> is an octal escape sequence. Instead use \x00.
\xhhMatches the character with the code hh (two hexadecimal digits)
\uhhhhMatches the character with the code hhhh (four hexadecimal digits).
+ +

Escaping user input that is to be treated as a literal string within a regular expression—that would otherwise be mistaken for a special character—can be accomplished by simple replacement:

+ +
function escapeRegExp(string) {
+  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
+}
+
+ +

The g after the regular expression is an option or flag that performs a global search, looking in the whole string and returning all matches. It is explained in detail below in Advanced Searching With Flags.

+ +

使用括號 

+ +

Parentheses around any part of the regular expression pattern causes that part of the matched substring to be remembered. Once remembered, the substring can be recalled for other use, as described in {{ web.link("#Using_parenthesized_substring_matches", "Using Parenthesized Substring Matches") }}.

+ +

For example, the pattern /Chapter (\d+)\.\d*/ illustrates additional escaped and special characters and indicates that part of the pattern should be remembered. It matches precisely the characters 'Chapter ' followed by one or more numeric characters (\d means any numeric character and + means 1 or more times), followed by a decimal point (which in itself is a special character; preceding the decimal point with \ means the pattern must look for the literal character '.'), followed by any numeric character 0 or more times (\d means numeric character, * means 0 or more times). In addition, parentheses are used to remember the first matched numeric characters.

+ +

This pattern is found in "Open Chapter 4.3, paragraph 6" and '4' is remembered. The pattern is not found in "Chapter 3 and 4", because that string does not have a period after the '3'.

+ +

To match a substring without causing the matched part to be remembered, within the parentheses preface the pattern with ?:. For example, (?:\d+) matches one or more numeric characters but does not remember the matched characters.

+ +

運用正規表達式

+ +

Regular expressions are used with the RegExp methods test and exec and with the String methods match, replace, search, and split. These methods are explained in detail in the JavaScript reference.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Methods that use regular expressions
MethodDescription
{{jsxref("RegExp.exec", "exec")}}A RegExp method that executes a search for a match in a string. It returns an array of information or null on a mismatch.
{{jsxref("RegExp.test", "test")}}A RegExp method that tests for a match in a string. It returns true or false.
{{jsxref("String.match", "match")}}A String method that executes a search for a match in a string. It returns an array of information or null on a mismatch.
{{jsxref("String.search", "search")}}A String method that tests for a match in a string. It returns the index of the match, or -1 if the search fails.
{{jsxref("String.replace", "replace")}}A String method that executes a search for a match in a string, and replaces the matched substring with a replacement substring.
{{jsxref("String.split", "split")}}A String method that uses a regular expression or a fixed string to break a string into an array of substrings.
+ +

When you want to know whether a pattern is found in a string, use the test or search method; for more information (but slower execution) use the exec or match methods. If you use exec or match and if the match succeeds, these methods return an array and update properties of the associated regular expression object and also of the predefined regular expression object, RegExp. If the match fails, the exec method returns null (which coerces to false).

+ +

In the following example, the script uses the exec method to find a match in a string.

+ +
var myRe = /d(b+)d/g;
+var myArray = myRe.exec('cdbbdbsbz');
+
+ +

If you do not need to access the properties of the regular expression, an alternative way of creating myArray is with this script:

+ +
var myArray = /d(b+)d/g.exec('cdbbdbsbz'); // similar to "cdbbdbsbz".match(/d(b+)d/g); however,
+    // the latter outputs Array [ "dbbd" ], while
+    // /d(b+)d/g.exec('cdbbdbsbz') outputs Array [ "dbbd", "bb" ].
+    // See below for further info (CTRL+F "The behavior associated with the".)
+ +

If you want to construct the regular expression from a string, yet another alternative is this script:

+ +
var myRe = new RegExp('d(b+)d', 'g');
+var myArray = myRe.exec('cdbbdbsbz');
+
+ +

With these scripts, the match succeeds and returns the array and updates the properties shown in the following table.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Results of regular expression execution.
物件Property or index說明範例
myArrayThe matched string and all remembered substrings.['dbbd', 'bb', index: 1, input: 'cdbbdbsbz']
indexThe 0-based index of the match in the input string.1
inputThe original string."cdbbdbsbz"
[0]The last matched characters."dbbd"
myRelastIndexThe index at which to start the next match. (This property is set only if the regular expression uses the g option, described in {{ web.link("#Advanced_searching_with_flags", "Advanced Searching With Flags") }}.)5
sourceThe text of the pattern. Updated at the time that the regular expression is created, not executed."d(b+)d"
+ +

As shown in the second form of this example, you can use a regular expression created with an object initializer without assigning it to a variable. If you do, however, every occurrence is a new regular expression. For this reason, if you use this form without assigning it to a variable, you cannot subsequently access the properties of that regular expression. For example, assume you have this script:

+ +
var myRe = /d(b+)d/g;
+var myArray = myRe.exec('cdbbdbsbz');
+console.log('The value of lastIndex is ' + myRe.lastIndex);
+
+// "The value of lastIndex is 5"
+
+ +

However, if you have this script:

+ +
var myArray = /d(b+)d/g.exec('cdbbdbsbz');
+console.log('The value of lastIndex is ' + /d(b+)d/g.lastIndex);
+
+// "The value of lastIndex is 0"
+
+ +

The occurrences of /d(b+)d/g in the two statements are different regular expression objects and hence have different values for their lastIndex property. If you need to access the properties of a regular expression created with an object initializer, you should first assign it to a variable.

+ +

Using Parenthesized Substring Matches

+ +

Including parentheses in a regular expression pattern causes the corresponding submatch to be remembered. For example, /a(b)c/ matches the characters 'abc' and remembers 'b'. To recall these parenthesized substring matches, use the Array elements [1], ..., [n].

+ +

The number of possible parenthesized substrings is unlimited. The returned array holds all that were found. The following examples illustrate how to use parenthesized substring matches.

+ +

下面這個 script 以 {{jsxref("String.replace", "replace()")}} 方法移轉字串位置。對於要被置換的文字內容,以 $1$2 來代表先前 re 這個變數裡面,找出來綑綁且照順序來表示兩個子字串。

+ +
var re = /(\w+)\s(\w+)/;
+var str = 'John Smith';
+var newstr = str.replace(re, '$2, $1');
+console.log(newstr);
+
+// "Smith, John"
+
+ +

Advanced Searching With Flags

+ +

Regular expressions have five optional flags that allow for global and case insensitive searching. These flags can be used separately or together in any order, and are included as part of the regular expression.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Regular expression flags
FlagDescription
gGlobal search.
iCase-insensitive search.
mMulti-line search.
uunicode; treat a pattern as a sequence of unicode code points
yPerform a "sticky" search that matches starting at the current position in the target string. See {{jsxref("RegExp.sticky", "sticky")}}
+ +

To include a flag with the regular expression, use this syntax:

+ +
var re = /pattern/flags;
+
+ +

or

+ +
var re = new RegExp('pattern', 'flags');
+
+ +

Note that the flags are an integral part of a regular expression. They cannot be added or removed later.

+ +

For example, re = /\w+\s/g creates a regular expression that looks for one or more characters followed by a space, and it looks for this combination throughout the string.

+ +
var re = /\w+\s/g;
+var str = 'fee fi fo fum';
+var myArray = str.match(re);
+console.log(myArray);
+
+// ["fee ", "fi ", "fo "]
+
+ +

You could replace the line:

+ +
var re = /\w+\s/g;
+
+ +

with:

+ +
var re = new RegExp('\\w+\\s', 'g');
+
+ +

and get the same result.

+ +

The behavior associated with the 'g' flag is different when the .exec() method is used.  (The roles of "class" and "argument" get reversed: In the case of .match(), the string class (or data type) owns the method and the regular expression is just an argument, while in the case of .exec(), it is the regular expression that owns the method, with the string being the argument.  Contrast str.match(re) versus re.exec(str).)  The 'g' flag is used with the .exec() method to get iterative progression.

+ +
var xArray; while(xArray = re.exec(str)) console.log(xArray);
+// produces:
+// ["fee ", index: 0, input: "fee fi fo fum"]
+// ["fi ", index: 4, input: "fee fi fo fum"]
+// ["fo ", index: 7, input: "fee fi fo fum"]
+ +

The m flag is used to specify that a multiline input string should be treated as multiple lines. If the m flag is used, ^ and $ match at the start or end of any line within the input string instead of the start or end of the entire string.

+ +

範例

+ +

The following examples show some uses of regular expressions.

+ +

Changing the order in an input string

+ +

The following example illustrates the formation of regular expressions and the use of string.split() and string.replace(). It cleans a roughly formatted input string containing names (first name last) separated by blanks, tabs and exactly one semicolon. Finally, it reverses the name order (last name first) and sorts the list.

+ +
// The name string contains multiple spaces and tabs,
+// and may have multiple spaces between first and last names.
+var names = 'Orange Trump ;Fred Barney; Helen Rigby ; Bill Abel ; Chris Hand ';
+
+var output = ['---------- Original String\n', names + '\n'];
+
+// Prepare two regular expression patterns and array storage.
+// Split the string into array elements.
+
+// pattern: possible white space then semicolon then possible white space
+var pattern = /\s*;\s*/;
+
+// Break the string into pieces separated by the pattern above and
+// store the pieces in an array called nameList
+var nameList = names.split(pattern);
+
+// new pattern: one or more characters then spaces then characters.
+// Use parentheses to "memorize" portions of the pattern.
+// The memorized portions are referred to later.
+pattern = /(\w+)\s+(\w+)/;
+
+// Below is the new array for holding names being processed.
+var bySurnameList = [];
+
+// Display the name array and populate the new array
+// with comma-separated names, last first.
+//
+// The replace method removes anything matching the pattern
+// and replaces it with the memorized string—the second memorized portion
+// followed by a comma, a space and the first memorized portion.
+//
+// The variables $1 and $2 refer to the portions
+// memorized while matching the pattern.
+
+output.push('---------- After Split by Regular Expression');
+
+var i, len;
+for (i = 0, len = nameList.length; i < len; i++) {
+  output.push(nameList[i]);
+  bySurnameList[i] = nameList[i].replace(pattern, '$2, $1');
+}
+
+// Display the new array.
+output.push('---------- Names Reversed');
+for (i = 0, len = bySurnameList.length; i < len; i++) {
+  output.push(bySurnameList[i]);
+}
+
+// Sort by last name, then display the sorted array.
+bySurnameList.sort();
+output.push('---------- Sorted');
+for (i = 0, len = bySurnameList.length; i < len; i++) {
+  output.push(bySurnameList[i]);
+}
+
+output.push('---------- End');
+
+console.log(output.join('\n'));
+
+ +

使用特殊字元驗證輸入

+ +

In the following example, the user is expected to enter a phone number. When the user presses the "Check" button, the script checks the validity of the number. If the number is valid (matches the character sequence specified by the regular expression), the script shows a message thanking the user and confirming the number. If the number is invalid, the script informs the user that the phone number is not valid.

+ +

Within non-capturing parentheses (?: , the regular expression looks for three numeric characters \d{3} OR | a left parenthesis \( followed by three digits \d{3}, followed by a close parenthesis \), (end non-capturing parenthesis )), followed by one dash, forward slash, or decimal point and when found, remember the character ([-\/\.]), followed by three digits \d{3}, followed by the remembered match of a dash, forward slash, or decimal point \1, followed by four digits \d{4}.

+ +

The Change event activated when the user presses Enter sets the value of RegExp.input.

+ +
<!DOCTYPE html>
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+    <meta http-equiv="Content-Script-Type" content="text/javascript">
+    <script type="text/javascript">
+      var re = /(?:\d{3}|\(\d{3}\))([-\/\.])\d{3}\1\d{4}/;
+      function testInfo(phoneInput) {
+        var OK = re.exec(phoneInput.value);
+        if (!OK)
+          window.alert(phoneInput.value + ' isn\'t a phone number with area code!');
+        else
+          window.alert('Thanks, your phone number is ' + OK[0]);
+      }
+    </script>
+  </head>
+  <body>
+    <p>Enter your phone number (with area code) and then click "Check".
+        <br>The expected format is like ###-###-####.</p>
+    <form action="#">
+      <input id="phone"><button onclick="testInfo(document.getElementById('phone'));">Check</button>
+    </form>
+  </body>
+</html>
+
+ +

{{PreviousNext("Web/JavaScript/Guide/Text_formatting", "Web/JavaScript/Guide/Indexed_collections")}}

diff --git a/files/zh-tw/web/javascript/guide/using_promises/index.html b/files/zh-tw/web/javascript/guide/using_promises/index.html new file mode 100644 index 0000000000..1df6376ffd --- /dev/null +++ b/files/zh-tw/web/javascript/guide/using_promises/index.html @@ -0,0 +1,256 @@ +--- +title: 使用 Promise +slug: Web/JavaScript/Guide/Using_promises +translation_of: Web/JavaScript/Guide/Using_promises +--- +
{{jsSidebar("JavaScript Guide")}}
+ +

{{jsxref("Promise")}} 是一個表示非同步運算的最終完成或失敗的物件。由於多數人使用預建立的 Promise,這個導覽會先講解回傳 Promise 的使用方式,之後再介紹如何建立。

+ +

基本上,一個 Promise 是一個根據附加給他的 Callback 回傳的物件,以取代傳遞 Callback 到這個函數。

+ +

舉例來說,下方的範例若用舊方式應該會有兩個 Callback,並根據成功或失敗來決定使用哪個:

+ +
function successCallback(result) {
+  console.log("It succeeded with " + result);
+}
+
+function failureCallback(error) {
+  console.log("It failed with " + error);
+}
+
+doSomething(successCallback, failureCallback);
+
+ +

而新作法會回傳一個 Promise,這樣你就可以附加 Callback:

+ +
let promise = doSomething();
+promise.then(successCallback, failureCallback);
+ +

再簡單點:

+ +
doSomething().then(successCallback, failureCallback);
+ +

我們稱之為 非同步函數呼叫。這個做法有許多好處,我們接下來看看。

+ +

保證

+ +

不如舊做法,一個 Promise 有這些保證:

+ + + +

但 Promise 主要的立即好處是串連。

+ +

串連

+ +

有個常見的需求是依序呼叫兩個以上的非同步函數,我們稱之為建立 Promise 鏈

+ +

看看魔術:then 函數回傳一個新的 Promise,不同於原本。

+ +
let promise = doSomething();
+let promise2 = promise.then(successCallback, failureCallback);
+
+ +

+ +
let promise2 = doSomething().then(successCallback, failureCallback);
+
+ +

第二個 Promise 不只代表 doSomething() 完成,還有successCallbackfailureCallback ,這兩個非同步函數回傳另一個 Promise。如此一來,任何 Callback 附加給 promise2 會被排在 successCallbackfailureCallback 之後。

+ +

基本上,每個 Promise 代表著鏈中另外一個非同步函數的完成。

+ +

在古時候,多個非同步函數會使用 Callback 方式,導致波動拳問題:

+ +

(原文 Pyramid of Doom 查無中文翻譯,以較常見之波動拳取代)

+ +
doSomething(function(result) {
+  doSomethingElse(result, function(newResult) {
+    doThirdThing(newResult, function(finalResult) {
+      console.log('Got the final result: ' + finalResult);
+    }, failureCallback);
+  }, failureCallback);
+}, failureCallback);
+
+ +

有了新方法,我們附加 Callback 到回傳的 Promise 上,來製造 Promise 鏈

+ +
doSomething().then(function(result) {
+  return doSomethingElse(result);
+})
+.then(function(newResult) {
+  return doThirdThing(newResult);
+})
+.then(function(finalResult) {
+  console.log('Got the final result: ' + finalResult);
+})
+.catch(failureCallback);
+
+ +

then 的函數是選用的,以及 catch(failureCallback)then(null, failureCallback) 的簡寫。你也許會想用箭頭函數取代:

+ +
doSomething()
+.then(result => doSomethingElse(result))
+.then(newResult => doThirdThing(newResult))
+.then(finalResult => {
+  console.log(`Got the final result: ${finalResult}`);
+})
+.catch(failureCallback);
+
+ +

注意:永遠要回傳結果,否則 Callback 不會獲得前一個 Promise 的結果。

+ +

獲錯後串接

+ +

失敗的串接是可行的,也就是說 catch 會非常好用,即使鏈中出錯。看看這個範例:

+ +
new Promise((resolve, reject) => {
+    console.log('Initial');
+
+    resolve();
+})
+.then(() => {
+    throw new Error('Something failed');
+
+    console.log('Do this');
+})
+.catch(() => {
+    console.log('Do that');
+})
+.then(() => {
+    console.log('Do this whatever happened before');
+});
+
+ +

他會輸出:

+ +
Initial
+Do that
+Do this whatever happened before
+
+ +

注意「Do this」沒有被輸出,因為「Something failed」錯誤導致拒絕。

+ +

錯誤傳遞

+ +

在波動拳狀況中,你可能會看到三次 failureCallback ,在 Promise 鏈中只需要在尾端使用一次:

+ +
doSomething()
+.then(result => doSomethingElse(result))
+.then(newResult => doThirdThing(newResult))
+.then(finalResult => console.log(`Got the final result: ${finalResult}`))
+.catch(failureCallback);
+
+ +

基本上,一個 Promise 鏈遇到錯誤時會往下尋找 Catch 處理器。這是經過模組化的非同步程式:

+ +
try {
+  let result = syncDoSomething();
+  let newResult = syncDoSomethingElse(result);
+  let finalResult = syncDoThirdThing(newResult);
+  console.log(`Got the final result: ${finalResult}`);
+} catch(error) {
+  failureCallback(error);
+}
+
+ +

在 ECMAScript 2017 中,在有 async/await 語法糖的同步程式達到高峰:

+ +
async function foo() {
+  try {
+    let result = await doSomething();
+    let newResult = await doSomethingElse(result);
+    let finalResult = await doThirdThing(newResult);
+    console.log(`Got the final result: ${finalResult}`);
+  } catch(error) {
+    failureCallback(error);
+  }
+}
+
+ +

這基於 Promise,例如 doSomething()和之前一樣。你可以閱讀在這裡閱讀更多。

+ +

Promise 藉由捕捉所有錯誤,包含例外和程式錯誤,解決了 Callback 地獄的缺點。這是非同步運算的基本特性。

+ +

在舊有 API 上建立 Promise

+ +

{{jsxref("Promise")}} 可以透過建構子建立。所以用建構子包裹舊的 API即可。

+ +

在理想情況,所有非同步函數都會回傳 Promise,然而許多 API 仍然用舊的方式來傳遞成功、失敗 Callback,有個典型的例子是{{domxref("WindowTimers.setTimeout", "setTimeout()")}} :

+ +
setTimeout(() => saySomething("10 seconds passed"), 10000);
+
+ +

混合古代 Callback 和 Promise 是有問題的。如果 saySomething 失敗或有程式錯誤,那不會有任何錯誤被捕捉。

+ +

幸運地,我們可以用 Promise 包裹他,最好盡可能的在最底層包裹,並永遠不要再直接呼叫他們:

+ +
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
+
+wait(10000).then(() => saySomething("10 seconds")).catch(failureCallback);
+
+ +

基本上,Promise 建構子需要一個運作函數來正規地處理或拒絕 Promise。但因為 setTimeout 不會失敗,所以我們捨棄 reject。

+ +

組合

+ +

{{jsxref("Promise.resolve()")}} 和 {{jsxref("Promise.reject()")}} 是用來正規地建立已經處理或拒絕的 Promise。他們在某些情況特別有用。

+ +

{{jsxref("Promise.all()")}} 和 {{jsxref("Promise.race()")}} 是兩個組合工具用於使 Promise 平行運作。

+ +

連續關聯是可行的,這是極簡 JavaScript 範例:

+ +
[func1, func2].reduce((p, f) => p.then(f), Promise.resolve());
+
+ +

基本上,我們摺疊(Reduce)一個非同步函數陣列成一個 Promise 鏈:Promise.resolve().then(func1).then(func2);

+ +

這可以用可重用的構成函數完成,通常用函數式編程:

+ +
let applyAsync = (acc,val) => acc.then(val);
+let composeAsync = (...funcs) => x => funcs.reduce(applyAsync, Promise.resolve(x));
+ +

composeAsync 接受任何數量的函數作為參數,並回傳一個接受一個初始值用來傳給組合的新函數。這個好處是無論其中函數是非同步或否,都會保證用正確的順序執行:

+ +
let transformData = composeAsync(func1, asyncFunc1, asyncFunc2, func2);
+transformData(data);
+
+ +

ECMAScript 2017 中連續組合利用 async/await 更簡單:

+ +
for (let f of [func1, func2]) {
+  await f();
+}
+
+ +

計時

+ +

為了避免意外,傳給 then 的函數不會被同步地呼叫,即使是完成的 Promise:

+ +
Promise.resolve().then(() => console.log(2));
+console.log(1); // 1, 2
+
+ +

被傳入的函數會被放在子任務佇列而非立即執行,因此他會在當前的事件迴圈結束、佇列清空時執行,例如:

+ +
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
+
+wait().then(() => console.log(4));
+Promise.resolve().then(() => console.log(2)).then(() => console.log(3));
+console.log(1); // 1, 2, 3, 4
+
+ +

看更多

+ + diff --git a/files/zh-tw/web/javascript/guide/working_with_objects/index.html b/files/zh-tw/web/javascript/guide/working_with_objects/index.html new file mode 100644 index 0000000000..823f9c1e4b --- /dev/null +++ b/files/zh-tw/web/javascript/guide/working_with_objects/index.html @@ -0,0 +1,499 @@ +--- +title: 物件的使用 +slug: Web/JavaScript/Guide/Working_with_Objects +translation_of: Web/JavaScript/Guide/Working_with_Objects +--- +
{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Regular_Expressions", "Web/JavaScript/Guide/Details_of_the_Object_Model")}}
+ +

JavaScript is designed on a simple object-based paradigm. An object is a collection of properties, and a property is an association between a name (or key) and a value. A property's value can be a function, in which case the property is known as a method. In addition to objects that are predefined in the browser, you can define your own objects. This chapter describes how to use objects, properties, functions, and methods, and how to create your own objects.

+ +

"物件"概觀

+ +

就如同其他程式語言一般,JacaScript裡頭的"物件"可以與真實生活中的物件做類比。其概念可以用生活中有形的物體來做理解。

+ +

在JavaScript裡,"物件"是一個擁有自己的屬性、型別、獨立的實體。這裡我們以杯子舉例:我們可以從顏色、設計、重量、材質等方面來描述他的屬性。同樣的,我們也可以用各種屬性來描述JavaScript中某個物體的特性。

+ +

物件與屬性

+ +

JavaScript的物件有其關聯的屬性。物件的屬性可以用附著在物件上的變數來描述。

+ +

 Object properties are basically the same as ordinary JavaScript variables, except for the attachment to objects. The properties of an object define the characteristics of the object. You access the properties of an object with a simple dot-notation:

+ +
objectName.propertyName
+
+ +

Like all JavaScript variables, both the object name (which could be a normal variable) and property name are case sensitive. You can define a property by assigning it a value. For example, let's create an object named myCar and give it properties named make, model, and year as follows:

+ +
var myCar = new Object();
+myCar.make = 'Ford';
+myCar.model = 'Mustang';
+myCar.year = 1969;
+
+ +

Unassigned properties of an object are {{jsxref("undefined")}} (and not {{jsxref("null")}}).

+ +
myCar.color; // undefined
+ +

Properties of JavaScript objects can also be accessed or set using a bracket notation (for more details see property accessors). Objects are sometimes called associative arrays, since each property is associated with a string value that can be used to access it. So, for example, you could access the properties of the myCar object as follows:

+ +
myCar['make'] = 'Ford';
+myCar['model'] = 'Mustang';
+myCar['year'] = 1969;
+
+ +

An object property name can be any valid JavaScript string, or anything that can be converted to a string, including the empty string. However, any property name that is not a valid JavaScript identifier (for example, a property name that has a space or a hyphen, or that starts with a number) can only be accessed using the square bracket notation. This notation is also very useful when property names are to be dynamically determined (when the property name is not determined until runtime). Examples are as follows:

+ +
// four variables are created and assigned in a single go,
+// separated by commas
+var myObj = new Object(),
+    str = 'myString',
+    rand = Math.random(),
+    obj = new Object();
+
+myObj.type              = 'Dot syntax';
+myObj['date created']   = 'String with space';
+myObj[str]              = 'String value';
+myObj[rand]             = 'Random Number';
+myObj[obj]              = 'Object';
+myObj['']               = 'Even an empty string';
+
+console.log(myObj);
+
+ +

Please note that all keys in the square bracket notation are converted to String type, since objects in JavaScript can only have String type as key type. For example, in the above code, when the key obj is added to the myObj, JavaScript will call the obj.toString() method, and use this result string as the new key.

+ +

You can also access properties by using a string value that is stored in a variable:

+ +
var propertyName = 'make';
+myCar[propertyName] = 'Ford';
+
+propertyName = 'model';
+myCar[propertyName] = 'Mustang';
+
+ +

You can use the bracket notation with for...in to iterate over all the enumerable properties of an object. To illustrate how this works, the following function displays the properties of the object when you pass the object and the object's name as arguments to the function:

+ +
function showProps(obj, objName) {
+  var result = '';
+  for (var i in obj) {
+    // obj.hasOwnProperty() is used to filter out properties from the object's prototype chain
+    if (obj.hasOwnProperty(i)) {
+      result += objName + '.' + i + ' = ' + obj[i] + '\n';
+    }
+  }
+  return result;
+}
+
+ +

So, the function call showProps(myCar, "myCar") would return the following:

+ +
myCar.make = Ford
+myCar.model = Mustang
+myCar.year = 1969
+ +

Enumerate the properties of an object

+ +

Starting with ECMAScript 5, there are three native ways to list/traverse object properties:

+ + + +

Before ECMAScript 5, there was no native way to list all properties of an object. However, this can be achieved with the following function:

+ +
function listAllProperties(o) {
+	var objectToInspect;
+	var result = [];
+
+	for(objectToInspect = o; objectToInspect !== null; objectToInspect = Object.getPrototypeOf(objectToInspect)) {
+      result = result.concat(Object.getOwnPropertyNames(objectToInspect));
+	}
+
+	return result;
+}
+
+ +

This can be useful to reveal "hidden" properties (properties in the prototype chain which are not accessible through the object, because another property has the same name earlier in the prototype chain). Listing accessible properties only can easily be done by removing duplicates in the array.

+ +

Creating new objects

+ +

JavaScript has a number of predefined objects. In addition, you can create your own objects. You can create an object using an object initializer. Alternatively, you can first create a constructor function and then instantiate an object invoking that function in conjunction with the new operator.

+ +

Using object initializers

+ +

In addition to creating objects using a constructor function, you can create objects using an object initializer. Using object initializers is sometimes referred to as creating objects with literal notation. "Object initializer" is consistent with the terminology used by C++.

+ +

The syntax for an object using an object initializer is:

+ +
var obj = { property_1:   value_1,   // property_# may be an identifier...
+            2:            value_2,   // or a number...
+            // ...,
+            'property n': value_n }; // or a string
+
+ +

where obj is the name of the new object, each property_i is an identifier (either a name, a number, or a string literal), and each value_i is an expression whose value is assigned to the property_i. The obj and assignment is optional; if you do not need to refer to this object elsewhere, you do not need to assign it to a variable. (Note that you may need to wrap the object literal in parentheses if the object appears where a statement is expected, so as not to have the literal be confused with a block statement.)

+ +

Object initializers are expressions, and each object initializer results in a new object being created whenever the statement in which it appears is executed. Identical object initializers create distinct objects that will not compare to each other as equal. Objects are created as if a call to new Object() were made; that is, objects made from object literal expressions are instances of Object.

+ +

The following statement creates an object and assigns it to the variable x if and only if the expression cond is true:

+ +
if (cond) var x = {greeting: 'hi there'};
+
+ +

The following example creates myHonda with three properties. Note that the engine property is also an object with its own properties.

+ +
var myHonda = {color: 'red', wheels: 4, engine: {cylinders: 4, size: 2.2}};
+
+ +

You can also use object initializers to create arrays. See array literals.

+ +

Using a constructor function

+ +

Alternatively, you can create an object with these two steps:

+ +
    +
  1. Define the object type by writing a constructor function. There is a strong convention, with good reason, to use a capital initial letter.
  2. +
  3. Create an instance of the object with new.
  4. +
+ +

To define an object type, create a function for the object type that specifies its name, properties, and methods. For example, suppose you want to create an object type for cars. You want this type of object to be called car, and you want it to have properties for make, model, and year. To do this, you would write the following function:

+ +
function Car(make, model, year) {
+  this.make = make;
+  this.model = model;
+  this.year = year;
+}
+
+ +

Notice the use of this to assign values to the object's properties based on the values passed to the function.

+ +

Now you can create an object called mycar as follows:

+ +
var mycar = new Car('Eagle', 'Talon TSi', 1993);
+
+ +

This statement creates mycar and assigns it the specified values for its properties. Then the value of mycar.make is the string "Eagle", mycar.year is the integer 1993, and so on.

+ +

You can create any number of car objects by calls to new. For example,

+ +
var kenscar = new Car('Nissan', '300ZX', 1992);
+var vpgscar = new Car('Mazda', 'Miata', 1990);
+
+ +

An object can have a property that is itself another object. For example, suppose you define an object called person as follows:

+ +
function Person(name, age, sex) {
+  this.name = name;
+  this.age = age;
+  this.sex = sex;
+}
+
+ +

and then instantiate two new person objects as follows:

+ +
var rand = new Person('Rand McKinnon', 33, 'M');
+var ken = new Person('Ken Jones', 39, 'M');
+
+ +

Then, you can rewrite the definition of car to include an owner property that takes a person object, as follows:

+ +
function Car(make, model, year, owner) {
+  this.make = make;
+  this.model = model;
+  this.year = year;
+  this.owner = owner;
+}
+
+ +

To instantiate the new objects, you then use the following:

+ +
var car1 = new Car('Eagle', 'Talon TSi', 1993, rand);
+var car2 = new Car('Nissan', '300ZX', 1992, ken);
+
+ +

Notice that instead of passing a literal string or integer value when creating the new objects, the above statements pass the objects rand and ken as the arguments for the owners. Then if you want to find out the name of the owner of car2, you can access the following property:

+ +
car2.owner.name
+
+ +

Note that you can always add a property to a previously defined object. For example, the statement

+ +
car1.color = 'black';
+
+ +

adds a property color to car1, and assigns it a value of "black." However, this does not affect any other objects. To add the new property to all objects of the same type, you have to add the property to the definition of the car object type.

+ +

Using the Object.create method

+ +

Objects can also be created using the {{jsxref("Object.create()")}} method. This method can be very useful, because it allows you to choose the prototype object for the object you want to create, without having to define a constructor function.

+ +
// Animal properties and method encapsulation
+var Animal = {
+  type: 'Invertebrates', // Default value of properties
+  displayType: function() {  // Method which will display type of Animal
+    console.log(this.type);
+  }
+};
+
+// Create new animal type called animal1
+var animal1 = Object.create(Animal);
+animal1.displayType(); // Output:Invertebrates
+
+// Create new animal type called Fishes
+var fish = Object.create(Animal);
+fish.type = 'Fishes';
+fish.displayType(); // Output:Fishes
+ +

Inheritance

+ +

All objects in JavaScript inherit from at least one other object. The object being inherited from is known as the prototype, and the inherited properties can be found in the prototype object of the constructor. See Inheritance and the prototype chain for more information.

+ +

Indexing object properties

+ +

You can refer to a property of an object either by its property name or by its ordinal index. If you initially define a property by its name, you must always refer to it by its name, and if you initially define a property by an index, you must always refer to it by its index.

+ +

This restriction applies when you create an object and its properties with a constructor function (as we did previously with the Car object type) and when you define individual properties explicitly (for example, myCar.color = "red"). If you initially define an object property with an index, such as myCar[5] = "25 mpg", you subsequently refer to the property only as myCar[5].

+ +

The exception to this rule is objects reflected from HTML, such as the forms array. You can always refer to objects in these arrays by either their ordinal number (based on where they appear in the document) or their name (if defined). For example, if the second <FORM> tag in a document has a NAME attribute of "myForm", you can refer to the form as document.forms[1] or document.forms["myForm"] or document.forms.myForm.

+ +

Defining properties for an object type

+ +

You can add a property to a previously defined object type by using the prototype property. This defines a property that is shared by all objects of the specified type, rather than by just one instance of the object. The following code adds a color property to all objects of type car, and then assigns a value to the color property of the object car1.

+ +
Car.prototype.color = null;
+car1.color = 'black';
+
+ +

See the prototype property of the Function object in the JavaScript reference for more information.

+ +

Defining methods

+ +

A method is a function associated with an object, or, simply put, a method is a property of an object that is a function. Methods are defined the way normal functions are defined, except that they have to be assigned as the property of an object. See also method definitions for more details. An example is:

+ +
objectName.methodname = function_name;
+
+var myObj = {
+  myMethod: function(params) {
+    // ...do something
+  }
+
+  // OR THIS WORKS TOO
+
+  myOtherMethod(params) {
+    // ...do something else
+  }
+};
+
+ +

where objectName is an existing object, methodname is the name you are assigning to the method, and function_name is the name of the function.

+ +

You can then call the method in the context of the object as follows:

+ +
object.methodname(params);
+
+ +

You can define methods for an object type by including a method definition in the object constructor function. You could define a function that would format and display the properties of the previously-defined car objects; for example,

+ +
function displayCar() {
+  var result = 'A Beautiful ' + this.year + ' ' + this.make
+    + ' ' + this.model;
+  pretty_print(result);
+}
+
+ +

where pretty_print is a function to display a horizontal rule and a string. Notice the use of this to refer to the object to which the method belongs.

+ +

You can make this function a method of car by adding the statement

+ +
this.displayCar = displayCar;
+
+ +

to the object definition. So, the full definition of car would now look like

+ +
function Car(make, model, year, owner) {
+  this.make = make;
+  this.model = model;
+  this.year = year;
+  this.owner = owner;
+  this.displayCar = displayCar;
+}
+
+ +

Then you can call the displayCar method for each of the objects as follows:

+ +
car1.displayCar();
+car2.displayCar();
+
+ +

Using this for object references

+ +

JavaScript has a special keyword, this, that you can use within a method to refer to the current object. For example, suppose you have a function called validate that validates an object's value property, given the object and the high and low values:

+ +
function validate(obj, lowval, hival) {
+  if ((obj.value < lowval) || (obj.value > hival)) {
+    alert('Invalid Value!');
+  }
+}
+
+ +

Then, you could call validate in each form element's onchange event handler, using this to pass it the element, as in the following example:

+ +
<input type="text" name="age" size="3"
+  onChange="validate(this, 18, 99)">
+
+ +

In general, this refers to the calling object in a method.

+ +

When combined with the form property, this can refer to the current object's parent form. In the following example, the form myForm contains a Text object and a button. When the user clicks the button, the value of the Text object is set to the form's name. The button's onclick event handler uses this.form to refer to the parent form, myForm.

+ +
<form name="myForm">
+<p><label>Form name:<input type="text" name="text1" value="Beluga"></label>
+<p><input name="button1" type="button" value="Show Form Name"
+     onclick="this.form.text1.value = this.form.name">
+</p>
+</form>
+ +

Defining getters and setters

+ +

A getter is a method that gets the value of a specific property. A setter is a method that sets the value of a specific property. You can define getters and setters on any predefined core object or user-defined object that supports the addition of new properties. The syntax for defining getters and setters uses the object literal syntax.

+ +

The following illustrates how getters and setters could work for a user-defined object o.

+ +
var o = {
+  a: 7,
+  get b() {
+    return this.a + 1;
+  },
+  set c(x) {
+    this.a = x / 2;
+  }
+};
+
+console.log(o.a); // 7
+console.log(o.b); // 8
+o.c = 50;
+console.log(o.a); // 25
+
+ +

The o object's properties are:

+ + + +

Please note that function names of getters and setters defined in an object literal using "[gs]et property()" (as opposed to __define[GS]etter__ ) are not the names of the getters themselves, even though the [gs]et propertyName(){ } syntax may mislead you to think otherwise. To name a function in a getter or setter using the "[gs]et property()" syntax, define an explicitly named function programmatically using Object.defineProperty (or the Object.prototype.__defineGetter__ legacy fallback).

+ +

The following code illustrates how getters and setters can extend the {{jsxref("Date")}} prototype to add a year property to all instances of the predefined Date class. It uses the Date class's existing getFullYear and setFullYear methods to support the year property's getter and setter.

+ +

These statements define a getter and setter for the year property:

+ +
var d = Date.prototype;
+Object.defineProperty(d, 'year', {
+  get: function() { return this.getFullYear(); },
+  set: function(y) { this.setFullYear(y); }
+});
+
+ +

These statements use the getter and setter in a Date object:

+ +
var now = new Date();
+console.log(now.year); // 2000
+now.year = 2001; // 987617605170
+console.log(now);
+// Wed Apr 18 11:13:25 GMT-0700 (Pacific Daylight Time) 2001
+
+ +

In principle, getters and setters can be either

+ + + +

When defining getters and setters using object initializers all you need to do is to prefix a getter method with get and a setter method with set. Of course, the getter method must not expect a parameter, while the setter method expects exactly one parameter (the new value to set). For instance:

+ +
var o = {
+  a: 7,
+  get b() { return this.a + 1; },
+  set c(x) { this.a = x / 2; }
+};
+
+ +

Getters and setters can also be added to an object at any time after creation using the Object.defineProperties method. This method's first parameter is the object on which you want to define the getter or setter. The second parameter is an object whose property names are the getter or setter names, and whose property values are objects for defining the getter or setter functions. Here's an example that defines the same getter and setter used in the previous example:

+ +
var o = { a: 0 };
+
+Object.defineProperties(o, {
+    'b': { get: function() { return this.a + 1; } },
+    'c': { set: function(x) { this.a = x / 2; } }
+});
+
+o.c = 10; // Runs the setter, which assigns 10 / 2 (5) to the 'a' property
+console.log(o.b); // Runs the getter, which yields a + 1 or 6
+
+ +

Which of the two forms to choose depends on your programming style and task at hand. If you already go for the object initializer when defining a prototype you will probably most of the time choose the first form. This form is more compact and natural. However, if you need to add getters and setters later — because you did not write the prototype or particular object — then the second form is the only possible form. The second form probably best represents the dynamic nature of JavaScript — but it can make the code hard to read and understand.

+ +

Deleting properties

+ +

You can remove a non-inherited property by using the delete operator. The following code shows how to remove a property.

+ +
// Creates a new object, myobj, with two properties, a and b.
+var myobj = new Object;
+myobj.a = 5;
+myobj.b = 12;
+
+// Removes the a property, leaving myobj with only the b property.
+delete myobj.a;
+console.log ('a' in myobj); // yields "false"
+
+ +

You can also use delete to delete a global variable if the var keyword was not used to declare the variable:

+ +
g = 17;
+delete g;
+
+ +

Comparing Objects

+ +

In JavaScript objects are a reference type. Two distinct objects are never equal, even if they have the same properties. Only comparing the same object reference with itself yields true.

+ +
// Two variables, two distinct objects with the same properties
+var fruit = {name: 'apple'};
+var fruitbear = {name: 'apple'};
+
+fruit == fruitbear; // return false
+fruit === fruitbear; // return false
+ +
// Two variables, a single object
+var fruit = {name: 'apple'};
+var fruitbear = fruit;  // assign fruit object reference to fruitbear
+
+// here fruit and fruitbear are pointing to same object
+fruit == fruitbear; // return true
+fruit === fruitbear; // return true
+
+ +
fruit.name = 'grape';
+console.log(fruitbear);    // yields { name: "grape" } instead of { name: "apple" }
+
+ +

For more information about comparison operators, see Comparison operators.

+ +

See also

+ + + +

{{PreviousNext("Web/JavaScript/Guide/Regular_Expressions", "Web/JavaScript/Guide/Details_of_the_Object_Model")}}

-- cgit v1.2.3-54-g00ecf