diff --git a/files/zh-tw/learn/javascript/asynchronous/index.md b/files/zh-tw/learn/javascript/asynchronous/index.md index 75abee1697c512..c754e606d55393 100644 --- a/files/zh-tw/learn/javascript/asynchronous/index.md +++ b/files/zh-tw/learn/javascript/asynchronous/index.md @@ -17,50 +17,41 @@ tags: - setTimeout - 非同步的 --- -
{{LearnSidebar}}
+{{LearnSidebar}} -

在本單元我們來討論非同步的( {{Glossary("asynchronous")}} ) {{Glossary("JavaScript")}} ,為何其如此重要,並了解它如何有效率的處理像是從伺服器獲取資源的這類潛在性阻塞( blocking )操作

+在本單元我們來討論非同步的( {{Glossary("asynchronous")}} ) {{Glossary("JavaScript")}} ,為何其如此重要,並了解它如何有效率的處理像是從伺服器獲取資源的這類潛在性阻塞( blocking )操作 -
-

想要成為前端開發人員?

+> **標註:** +> +> #### 想要成為前端開發人員? +> +> 我們已為你準備一門實現你目標所需要具備的所有基礎知識課程 +> +> [**立即開始**](/zh-TW/docs/Learn/Front-end_web_developer) -

我們已為你準備一門實現你目標所需要具備的所有基礎知識課程

+## 事前準備 -

立即開始

+非同步的 JavaScript 是一個相當進階的主題,因此建議你在嘗試本單元前能先通過 [JavaScript 初探](/zh-TW/docs/Learn/JavaScript/First_steps)以及 [JavaScript 構成元素](/zh-TW/docs/Learn/JavaScript/Building_blocks)單元。 -
+如果你對非同步程式設計的概念還不太熟悉,強烈建議你應該先從[非同步程式設計通用概念](/zh-TW/docs/Learn/JavaScript/Asynchronous/Concepts)的文章開始學習。如果你已經具備其概念,那麼你或許可以跳至[非同步的 JavaScript 介紹](/zh-TW/docs/Learn/JavaScript/Asynchronous/Introducing)單元開始。 -

事前準備

+> **備註:** 如果你正在使用電腦/平板/任何其它無法讓你建立檔案的裝置上,你可以嘗試在 [JSBin](https://jsbin.com/) 或是 [Glitch](https://glitch.com) 上測試本單元的範例程式碼。 -

非同步的 JavaScript 是一個相當進階的主題,因此建議你在嘗試本單元前能先通過 JavaScript 初探以及 JavaScript 構成元素單元。

+## 指南 -

如果你對非同步程式設計的概念還不太熟悉,強烈建議你應該先從非同步程式設計通用概念的文章開始學習。如果你已經具備其概念,那麼你或許可以跳至非同步的 JavaScript 介紹單元開始。

+- [非同步程式設計通用概念](/zh-TW/docs/Learn/JavaScript/Asynchronous/Concepts) + - : 在本篇文章我們會介紹一些關於非同步程式設計的重要觀念,以及在網頁瀏覽器和 JavaScript 中的行為。在閱讀其他文章之前你應該先具備這些觀念。 +- [非同步的 JavaScript 介紹](/zh-TW/docs/Learn/JavaScript/Asynchronous/Introducing) + - : 在本篇文章中我們會先簡短的回顧我們在同步的 JavaScript 中所遭遇到的問題,並預先看看稍後將會使用哪些非同步的 JavaScript 技巧來解決此問題。 +- [協同的非同步 JavaScript : Timeout 和 interval](/zh-TW/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals) + - : 在這裡看看我們在傳統上是如何透過設定的時間到期或是透過定時的方式(例如:每秒發生的次數)讓 Javascript 能夠以非同步的方式執行程式碼,談談這些方法有哪些用處以及存在哪些既有的問題。 +- [優雅的使用 Promise 來處理非同步操作](/zh-TW/docs/Learn/JavaScript/Asynchronous/Promises) + - : Promise 是在 Javascript 語言中相對較新的功能,它能夠讓你延遲活動直到先前的活動回報完成或失敗。這方法對設置一連串的操作並讓其正確的循序執行相當有用。本篇文章向你展示 Promise 是如何運作,你將會看到如何被使用在 WebAPI ,以及如何寫出屬於自己的 Promise 。 +- [利用 async 及 await 讓非同步程式設計變得更容易](/zh-TW/docs/Learn/JavaScript/Asynchronous/Async_await) + - : Promise 在設置上可能會有些複雜並難以理解,因此現代瀏覽器已經實作出 `async` 函式以及 `await` 運算子。前者能夠讓標準的函式隱含的使用 Promise 方式來實現非同步行為,然而後者可以被用在 `async` 函式內部,讓程式碼繼續執行之前去等待一個 Promise 完成。這能讓我們在鏈結一連串的 Promise 的情況之下更加簡潔易懂。 +- [選擇正確的方法](/zh-TW/docs/Learn/JavaScript/Asynchronous/Choosing_the_right_approach) + - : 在結束本單元前,我們將會從先前所討論過的不同程式技巧和功能,幫助你考量在甚麼情況下應該使用哪一個,並適當的提供一些有關常見的陷阱的建議及提醒。 -
-

備註: 如果你正在使用電腦/平板/任何其它無法讓你建立檔案的裝置上,你可以嘗試在 JSBin 或是 Glitch 上測試本單元的範例程式碼。

-
+## 參閱 -

指南

- -
-
非同步程式設計通用概念
-
-

在本篇文章我們會介紹一些關於非同步程式設計的重要觀念,以及在網頁瀏覽器和 JavaScript 中的行為。在閱讀其他文章之前你應該先具備這些觀念。

-
-
非同步的 JavaScript 介紹
-
在本篇文章中我們會先簡短的回顧我們在同步的 JavaScript 中所遭遇到的問題,並預先看看稍後將會使用哪些非同步的 JavaScript 技巧來解決此問題。
-
協同的非同步 JavaScript : Timeout 和 interval
-
在這裡看看我們在傳統上是如何透過設定的時間到期或是透過定時的方式(例如:每秒發生的次數)讓 Javascript 能夠以非同步的方式執行程式碼,談談這些方法有哪些用處以及存在哪些既有的問題。
-
優雅的使用 Promise 來處理非同步操作
-
Promise 是在 Javascript 語言中相對較新的功能,它能夠讓你延遲活動直到先前的活動回報完成或失敗。這方法對設置一連串的操作並讓其正確的循序執行相當有用。本篇文章向你展示 Promise 是如何運作,你將會看到如何被使用在 WebAPI ,以及如何寫出屬於自己的 Promise 。
-
利用 async 及 await 讓非同步程式設計變得更容易
-
Promise 在設置上可能會有些複雜並難以理解,因此現代瀏覽器已經實作出 async 函式以及 await 運算子。前者能夠讓標準的函式隱含的使用 Promise 方式來實現非同步行為,然而後者可以被用在 async 函式內部,讓程式碼繼續執行之前去等待一個 Promise 完成。這能讓我們在鏈結一連串的 Promise 的情況之下更加簡潔易懂。
-
選擇正確的方法
-
在結束本單元前,我們將會從先前所討論過的不同程式技巧和功能,幫助你考量在甚麼情況下應該使用哪一個,並適當的提供一些有關常見的陷阱的建議及提醒。
-
- -

參閱

- - +- [Asynchronous Programming](https://eloquentjavascript.net/11_async.html) 來自作者為 Marijn Haverbeke 的極佳的 [Eloquent JavaScript](https://eloquentjavascript.net/) 電子書。 diff --git a/files/zh-tw/learn/javascript/asynchronous/introducing/index.md b/files/zh-tw/learn/javascript/asynchronous/introducing/index.md index 5c3d504238f193..299830b91ef5c2 100644 --- a/files/zh-tw/learn/javascript/asynchronous/introducing/index.md +++ b/files/zh-tw/learn/javascript/asynchronous/introducing/index.md @@ -14,102 +14,105 @@ tags: - await - callbacks --- -
{{LearnSidebar}}
+{{LearnSidebar}}{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Concepts", "Learn/JavaScript/Asynchronous/Timeouts_and_intervals", "Learn/JavaScript/Asynchronous")}} -
{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Concepts", "Learn/JavaScript/Asynchronous/Timeouts_and_intervals", "Learn/JavaScript/Asynchronous")}}
- -

在本篇文章中我們會先簡短回顧在同步的 JavaScript 中所遭遇到的問題,並預先看看往後將會使用哪些非同步的 JavaScript 技巧來解決此問題。

+在本篇文章中我們會先簡短回顧在同步的 JavaScript 中所遭遇到的問題,並預先看看往後將會使用哪些非同步的 JavaScript 技巧來解決此問題。 - - - - - - - - - - + + + + + + + + + +
先備知識:基本的電腦概念,理解 JavaScript 的基本原理。
學習目標:熟悉什麼是非同步的 Javascript ,與同步的 Javascript 有甚麼差異,以及有哪些使用案例。
先備知識:基本的電腦概念,理解 JavaScript 的基本原理。
學習目標: + 熟悉什麼是非同步的 Javascript ,與同步的 Javascript + 有甚麼差異,以及有哪些使用案例。 +
-

同步的 JavaScript

+## 同步的 JavaScript -

在我們了解什麼是非同步({{Glossary("asynchronous")}} ) JavaScript 之前,我們應該確保我們已經了解什麼是同步( {{Glossary("synchronous")}} ) JavaScript 。在本章節會回顧我們在前篇文章所看到的一些資訊。

+在我們了解什麼是非同步(**{{Glossary("asynchronous")}}** ) JavaScript 之前,我們應該確保我們已經了解什麼是同步( **{{Glossary("synchronous")}}** ) JavaScript 。在本章節會回顧我們在前篇文章所看到的一些資訊。 -

我們在前一個學習單元所看到的大部分功能都是同步的——當跑一段程式碼,瀏覽器會將執行結果盡快的回傳。我們來看看一則簡單的範例(線上範例原始碼):

+我們在前一個學習單元所看到的大部分功能都是同步的——當跑一段程式碼,瀏覽器會將執行結果盡快的回傳。我們來看看一則簡單的範例([線上範例](https://mdn.github.io/learning-area/javascript/asynchronous/introducing/basic-function.html),[原始碼](https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/basic-function.html)): -
const btn = document.querySelector('button');
-btn.addEventListener('click', () => {
+```js
+const btn = document.querySelector('button');
+btn.addEventListener('click', () => {
   alert('你點擊了我!');
 
   let pElem = document.createElement('p');
   pElem.textContent = '這是一段新加的文字。';
   document.body.appendChild(pElem);
 });
-
+``` + +在這個程式區塊,指令是一行接著一行的執行: -

在這個程式區塊,指令是一行接著一行的執行:

+1. 我們先取得已經在 DOM 裡面的 {{htmlelement("button")}} 元素的參考。 +2. 我們加入一個 [`click`](/zh-TW/docs/Web/API/Element/click_event) 事件監聽器,因此當按鈕被點擊時: -
    -
  1. 我們先取得已經在 DOM 裡面的 {{htmlelement("button")}} 元素的參考。
  2. -
  3. 我們加入一個 click 事件監聽器,因此當按鈕被點擊時: -
      -
    1. 彈出 alert() 訊息。
    2. -
    3. 當關閉警告訊息後,我們新增一個 {{htmlelement("p")}} 元素。
    4. -
    5. 給予一段文字內容。
    6. -
    7. 最後,我們將文字段落嵌在文件本體( document body )上。
    8. -
    -
  4. -
+ 1. 彈出 [`alert()`](/zh-TW/docs/Web/API/Window/alert) 訊息。 + 2. 當關閉警告訊息後,我們新增一個 {{htmlelement("p")}} 元素。 + 3. 給予一段文字內容。 + 4. 最後,我們將文字段落嵌在文件本體( document body )上。 -

當正在執行每一個操作時,什麼事情都不會發生——渲染暫時停止。我們在上一篇文章有提到,這是因為 Javascript 是跑在一條單執行緒。主執行緒在同一時間只能做一件事情,直到目前操作完成為止其他的操作都會暫停。

+當正在執行每一個操作時,什麼事情都不會發生——渲染暫時停止。我們在[上一篇文章](/zh-TW/docs/Learn/JavaScript/Asynchronous/Introducing)有提到,這是因為 [Javascript 是跑在一條單執行緒](/zh-TW/docs/Learn/JavaScript/Asynchronous/Concepts#javascript_is_single_threaded)。主執行緒在同一時間只能做一件事情,直到目前操作完成為止其他的操作都會暫停。 -

所以在上面的範例中,直到警告視窗的確認按鈕被按下為止之前,文字段落都不會出現。你可以試試看底下的範例:

+所以在上面的範例中,直到警告視窗的確認按鈕被按下為止之前,文字段落都不會出現。你可以試試看底下的範例: - +```html hidden + +``` -

{{EmbedLiveSample('同步的 JavaScript', '100%', '70px')}}

+{{EmbedLiveSample('同步的 JavaScript', '100%', '70px')}} -
-

備註: 使用 alert() 有個很重要的事情要記得,雖然用在證明是否為同步的阻塞操作非常有用,但非常不建議用在實際的應用程式上,使用體驗將會相當糟糕。

-
+> **備註:** 使用 [`alert()`](/zh-TW/docs/Web/API/Window/alert) 有個很重要的事情要記得,雖然用在證明是否為同步的阻塞操作非常有用,但非常不建議用在實際的應用程式上,使用體驗將會相當糟糕。 -

非同步 JavaScript

+## 非同步 JavaScript -

如同先前所描述的原因(例如:和阻塞有關),現在許多 Web API 功能都是使用非同步程式碼的做法,特別是那些要從外部裝置存取或抓取某些種類資源的功能,像是從網路抓取檔案、存取資料庫並回傳資料、從網路攝影機取得視訊串流或者是將畫面透過廣播的方式顯示到 VR 頭盔等。

+如同先前所描述的原因(例如:和阻塞有關),現在許多 Web API 功能都是使用非同步程式碼的做法,特別是那些要從外部裝置存取或抓取某些種類資源的功能,像是從網路抓取檔案、存取資料庫並回傳資料、從網路攝影機取得視訊串流或者是將畫面透過廣播的方式顯示到 VR 頭盔等。 -

為什麼同步的程式碼很難做到這些事?我們快速地看個例子。當你想要從伺服器抓取一張影像,你是沒有辦法馬上獲得結果的。這個意思是下面的程式碼(虛擬碼)是行不通的:

+為什麼同步的程式碼很難做到這些事?我們快速地看個例子。當你想要從伺服器抓取一張影像,你是沒有辦法馬上獲得結果的。這個意思是下面的程式碼(虛擬碼)是行不通的: -
let response = fetch('myImage.png'); // fetch is asynchronous
+```js
+let response = fetch('myImage.png'); // fetch is asynchronous
 let blob = response.blob();
-// display your image blob in the UI somehow
+// display your image blob in the UI somehow +``` -

那是因為你不知道下載影像需要花上多少時間,所以當執行第二行時它將拋出一段錯誤(也許間歇性的發生,也許每一次都發生)這是因為 response 尚未取得結果還不能使用。因此在它嘗試做接下來的任何事之前,你需要讓你的程式碼先等到 response 回傳結果。

+那是因為你不知道下載影像需要花上多少時間,所以當執行第二行時它將拋出一段錯誤(也許間歇性的發生,也許每一次都發生)這是因為 `response` 尚未取得結果還不能使用。因此在它嘗試做接下來的任何事之前,你需要讓你的程式碼先等到 `response` 回傳結果。 -

你將會在 Javascript 程式碼碰到兩種主要類型的非同步程式碼風格,舊式的回呼風格(callback)以及新式的 promise 風格。在接下來的章節我們將會依次的檢視它們。

+你將會在 Javascript 程式碼碰到兩種主要類型的非同步程式碼風格,舊式的回呼風格(callback)以及新式的 promise 風格。在接下來的章節我們將會依次的檢視它們。 -

非同步回呼

+## 非同步回呼 -

呼叫一個將在背景開始執行程式碼的函式,非同步回呼是作為該呼叫函式的引數( argument )的函式。當在背景環境執行的程式碼完成時,就會呼叫回呼函式( callback function )讓你知道工作已經結束或者是一些有趣的事情已經發生了。現在回呼似乎有點過時了,但你依然會在一些較舊但仍常被使用的 API 發現到這樣的寫法。

+呼叫一個將在背景開始執行程式碼的函式,非同步回呼是作為該呼叫函式的引數( argument )的函式。當在背景環境執行的程式碼完成時,就會呼叫回呼函式( callback function )讓你知道工作已經結束或者是一些有趣的事情已經發生了。現在回呼似乎有點過時了,但你依然會在一些較舊但仍常被使用的 API 發現到這樣的寫法。 -

一個非同步回呼的例子就是作為在 {{domxref("EventTarget.addEventListener", "addEventListener()")}} 的第二個參數(正如我們在之前的例子看到的):

+一個非同步回呼的例子就是作為在 {{domxref("EventTarget.addEventListener", "addEventListener()")}} 的第二個參數(正如我們在之前的例子看到的): -
btn.addEventListener('click', () => {
+```js
+btn.addEventListener('click', () => {
   alert('你點擊了我!');
 
   let pElem = document.createElement('p');
   pElem.textContent = '這是一段新加的文字。';
   document.body.appendChild(pElem);
-});
+}); +``` -

第一個參數是要被監聽的事件類型,第二個參數是當事件被觸發時會呼叫的回呼函式。

+第一個參數是要被監聽的事件類型,第二個參數是當事件被觸發時會呼叫的回呼函式。 -

當我們將回呼函式做為一個引數給其他函式時,我們只傳送該回呼函式的參考做為引數,換句話說,回呼函式並非立即被執行。它將會在呼叫函式的內部某個地方非同步的「回去呼叫」它(因此稱回呼)。呼叫函式有責任的在適當的時機去執行該回呼函式。

+當我們將回呼函式做為一個引數給其他函式時,我們只傳送該回呼函式的參考做為引數,換句話說,回呼函式**並非**立即被執行。它將會在呼叫函式的內部某個地方非同步的「回去呼叫」它(因此稱回呼)。呼叫函式有責任的在適當的時機去執行該回呼函式。 -

自己寫一個包含回呼的呼叫函式很容易。我們來看看另一個從 XMLHttpRequest API 載入資源的例子(線上範例原始碼):

+自己寫一個包含回呼的呼叫函式很容易。我們來看看另一個從 [`XMLHttpRequest`](/zh-TW/docs/Web/API/XMLHttpRequest) API 載入資源的例子([線上範例](https://mdn.github.io/learning-area/javascript/asynchronous/introducing/xhr-async-callback.html),[原始碼](https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/xhr-async-callback.html)): -
function loadAsset(url, type, callback) {
+```js
+function loadAsset(url, type, callback) {
   let xhr = new XMLHttpRequest();
   xhr.open('GET', url);
   xhr.responseType = type;
@@ -129,157 +132,154 @@ function displayImage(blob) {
   document.body.appendChild(image);
 }
 
-loadAsset('coffee.jpg', 'blob', displayImage);
+loadAsset('coffee.jpg', 'blob', displayImage); +``` -

在這裡我們新增一個 displayImage() 函式,將 blob 做為一個參數傳進去函式來產生物件網址,然後建立一個可以用網址來顯示的影像,並將這個網址附加在 document 的 <body> 標籤內。然而,我們再新增一個 loadAsset() 函式,將回呼作為參數並伴隨抓取資源的網址以及內容型式傳進去函式。它使用 XMLHttpRequest (通常縮寫成「 XHR 」)根據網址去抓取資源,並將回傳結果傳送到回呼去做一些事情。在這個例子回呼正在等待 XHR 完成下載資源(使用 onload 事件處理器)後,再將資源傳遞給回呼。

+在這裡我們新增一個 `displayImage()` 函式,將 blob 做為一個參數傳進去函式來產生物件網址,然後建立一個可以用網址來顯示的影像,並將這個網址附加在 document 的 `` 標籤內。然而,我們再新增一個 `loadAsset()` 函式,將回呼作為參數並伴隨抓取資源的網址以及內容型式傳進去函式。它使用 `XMLHttpRequest` (通常縮寫成「 XHR 」)根據網址去抓取資源,並將回傳結果傳送到回呼去做一些事情。在這個例子回呼正在等待 XHR 完成下載資源(使用 [`onload`](/zh-TW/docs/Web/API/XMLHttpRequestEventTarget/onload) 事件處理器)後,再將資源傳遞給回呼。 -

回呼是多樣性的——它不只可以讓你控制執行函式的呼叫順序以及在不同函式間傳遞的參數,還可以讓你根據情況將資料傳遞到不同的函式。所以你可以根據下載的回傳結果採取不同的處理方式,例如 processJSON()displayText() 等等。

+回呼是多樣性的——它不只可以讓你控制執行函式的呼叫順序以及在不同函式間傳遞的參數,還可以讓你根據情況將資料傳遞到不同的函式。所以你可以根據下載的回傳結果採取不同的處理方式,例如 `processJSON()` , `displayText()` 等等。 -

注意不是所有的回呼都是非同步的——有些是跑在同步上。有個例子是當我們使用 {{jsxref("Array.prototype.forEach()")}} 在陣列裡面用迴圈來遍歷每一個項目(線上範例原始碼):

+注意不是所有的回呼都是非同步的——有些是跑在同步上。有個例子是當我們使用 {{jsxref("Array.prototype.forEach()")}} 在陣列裡面用迴圈來遍歷每一個項目([線上範例](https://mdn.github.io/learning-area/javascript/asynchronous/introducing/foreach.html),[原始碼](https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/foreach.html)): -
const gods = ['Apollo', 'Artemis', 'Ares', 'Zeus'];
+```js
+const gods = ['Apollo', 'Artemis', 'Ares', 'Zeus'];
 
 gods.forEach(function (eachName, index){
   console.log(index + '. ' + eachName);
-});
+}); +``` -

在這個例子我們用迴圈遍歷一個含有希臘諸神名稱的陣列並在控制台印出每一個索引和值。 forEach() 的預期參數是一個回呼函式,它有兩個參數代表陣列每個項目的名稱和索引。然而,它並未等待任何事情——它是立即的執行。

+在這個例子我們用迴圈遍歷一個含有希臘諸神名稱的陣列並在控制台印出每一個索引和值。 `forEach()` 的預期參數是一個回呼函式,它有兩個參數代表陣列每個項目的名稱和索引。然而,它並未等待任何事情——它是立即的執行。 -

Promise

+## Promise -

Promise 是作為非同步程式碼的一種新風格樣式,你將會在現代 Web API 看到這種用法。一個好例子是 fetch() API ,它基本上就像更新穎、更有效率版本的 {{domxref("XMLHttpRequest")}} 。我們藉由從伺服器提取資料這一篇文章快速地來看一個例子吧:

+Promise 是作為非同步程式碼的一種新風格樣式,你將會在現代 Web API 看到這種用法。一個好例子是 [`fetch()`](/zh-TW/docs/Web/API/fetch) API ,它基本上就像更新穎、更有效率版本的 {{domxref("XMLHttpRequest")}} 。我們藉由[從伺服器提取資料](/zh-TW/docs/Learn/JavaScript/Client-side_web_APIs/Fetching_data)這一篇文章快速地來看一個例子吧: -
fetch('products.json').then(function(response) {
+```js
+fetch('products.json').then(function(response) {
   return response.json();
 }).then(function(json) {
   let products = json;
   initialize(products);
 }).catch(function(err) {
   console.log('Fetch problem: ' + err.message);
-});
+}); +``` -
-

備註: 你可以在 GitHub (原始碼線上範例)找到最終版本。

-
+> **備註:** 你可以在 GitHub ([原始碼](https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/can-store/can-script.js),[線上範例](https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/))找到最終版本。 -

在這裡我們看到 fetch() 帶一個參數——你想要在網路上提取資源的網址——並回傳一個 promise 。這一個 promise 用來表示一個完成或失敗的非同步操作的物件。它代表的是一種中間的狀態。本質上,它代表瀏覽器述說著:「我承諾我會盡快給予你一個答覆」,因此名稱就叫做「 promise 」。

+在這裡我們看到 ` fetch``() ` 帶一個參數——你想要在網路上提取資源的網址——並回傳一個 [promise](/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Promise) 。這一個 promise 用來表示一個完成或失敗的非同步操作的物件。它代表的是一種中間的狀態。本質上,它代表瀏覽器述說著:「我承諾我會盡快給予你一個答覆」,因此名稱就叫做「 promise 」。 -

我們可以透過練習來習慣這個概念;在動作上它感覺有點像是 薛丁格的貓。任何可能的結果已經發生,所以 fetch 指令正在等待瀏覽器在未來的某個時間點完成操作後並回傳的結果。我們接著在 fetch() 的結束會鏈結三個程式區塊:

+我們可以透過練習來習慣這個概念;在動作上它感覺有點像是 [薛丁格的貓](https://zh.wikipedia.org/wiki/薛丁格的貓)。任何可能的結果已經發生,所以 fetch 指令正在等待瀏覽器在未來的某個時間點完成操作後並回傳的結果。我們接著在 `fetch()` 的結束會鏈結三個程式區塊: - +- 兩個 [`then()`](/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Promise/then) 區塊。兩個都含有回呼函式且先前的操作成功時就會執行,每一個回呼函式都會收到上一個成功完成操作的結果,因此你可以繼續執行一些事情。每一個 `.then()` 區塊都會回傳另一個 promise ,代表你可以將多個 `.then()` 區塊彼此作連結,所以多個非同步操作可以一個接著一個被用來依序執行。 +- 如果任何一個 `.then()` 區塊失敗就會跑到 [`catch()`](/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch) 區塊——類似像在同步區塊內部的 [`try...catch`](/zh-TW/docs/Web/JavaScript/Reference/Statements/try...catch) 做法,在 `catch()` 內部會提供一個失敗的物件,可以用來報告是發生甚麼類型的錯誤。要注意到同步的 `try...catch` 不能與 promises 一起做使用,儘管它可以和 [async / await](/zh-TW/docs/Learn/JavaScript/Asynchronous/Async_await) 待配使用,這稍後將會學習到。 -
-

備註: 你將會在稍後的單元學習到更多關於 promise 的觀念,即使現在尚未完全理解你也不需要太擔心。

-
+> **備註:** 你將會在稍後的單元學習到更多關於 promise 的觀念,即使現在尚未完全理解你也不需要太擔心。 -

事件佇列

+### 事件佇列 -

像 promise 的非同步的操作被放進事件佇列中,佇列內的任務會在主執行緒完成它自己的工作後執行,所以它並不會阻擋後續 Javascript 程式碼的執行。在佇列內的操作會盡快完成並將結果回傳給 Javascript 環境。

+像 promise 的非同步的操作被放進**事件佇列**中,佇列內的任務會在主執行緒完成它自己的工作後執行,所以它**並不會阻擋**後續 Javascript 程式碼的執行。在佇列內的操作會盡快完成並將結果回傳給 Javascript 環境。 -

Promise 對上回呼

+### Promise 對上回呼 -

Promise 和舊式的回呼有一些相似點。他們本質上都是回傳一個物件到回呼函式,而並非必須將回呼傳遞到呼叫函式。

+Promise 和舊式的回呼有一些相似點。他們本質上都是回傳一個物件到回呼函式,而並非必須將回呼傳遞到呼叫函式。 -

然而 promise 更是專門設計用來處理非同步的操作,比起舊式的回呼有更多的優點:

+然而 promise 更是專門設計用來處理非同步的操作,比起舊式的回呼有更多的優點: - +- 你可以使用 `.then()` 來鏈結多的非同步操作,將一個操作的結果作為下一個操作的輸入。這個對經常會導致「死亡金字塔」( pyramid of doom ,通常也被稱作為 [回呼地獄](http://callbackhell.com/))的回呼來說相當難辦到。 +- Promise 的執行順序總是依照放入事件佇列的順序來嚴格執行。 +- 更佳的錯誤處理機制——所有的錯誤都在最後的 `.catch()` 區塊來統一處理,而不是在個別的區塊做處理。 +- Promise 避免失去控制權,不像舊式的回呼那樣傳送給第三方程式庫後就會無法控制回呼函式怎麼被執行。 -

非同步程式碼的天性

+## 非同步程式碼的天性 -

我們來探索一個可以更進一步說明非同步程式碼天性的範例,並說明當我們無法充分了解程式碼的執行順序以及嘗試將非同步程式碼當作同步程式碼會發生什麼事。底下的範例和我們之前看到的相當相似( 線上範例原始碼)。區別在於我們加入一些 {{domxref("console.log()")}} 來說明程式碼是否會依照你以為的順序來執行。

+我們來探索一個可以更進一步說明非同步程式碼天性的範例,並說明當我們無法充分了解程式碼的執行順序以及嘗試將非同步程式碼當作同步程式碼會發生什麼事。底下的範例和我們之前看到的相當相似( [線上範例](https://mdn.github.io/learning-area/javascript/asynchronous/introducing/async-sync.html),[原始碼](https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/async-sync.html))。區別在於我們加入一些 {{domxref("console.log()")}} 來說明程式碼是否會依照你以為的順序來執行。 -
console.log ('Starting');
+```js
+console.log ('Starting');
 let image;
 
-fetch('coffee.jpg').then((response) => {
+fetch('coffee.jpg').then((response) => {
   console.log('It worked :)')
   return response.blob();
-}).then((myBlob) => {
+}).then((myBlob) => {
   let objectURL = URL.createObjectURL(myBlob);
   image = document.createElement('img');
   image.src = objectURL;
   document.body.appendChild(image);
-}).catch((error) => {
+}).catch((error) => {
   console.log('There has been a problem with your fetch operation: ' + error.message);
 });
 
-console.log ('All done!');
+console.log ('All done!'); +``` -

瀏覽器將開始執行程式碼,看到第一行顯示 Startingconsole.log() 並執行它,接著新增一個 image 變數。

+瀏覽器將開始執行程式碼,看到第一行顯示 `Starting` 的 `console.log()` 並執行它,接著新增一個 `image` 變數。 -

接著它會移動至下一行並開始執行 fetch() 區塊,但是因為執行 fetch() 是非同步的且不會阻塞,所以程式碼會跳過 promise 相關的程式碼後繼續執行,因此到達最後All done!console.log() 並顯示在控制台上。

+接著它會移動至下一行並開始執行 `fetch()` 區塊,但是因為執行 `fetch()` 是非同步的且不會阻塞,所以程式碼會跳過 promise 相關的程式碼後繼續執行,因此到達最後`All done!` 的 `console.log()` 並顯示在控制台上。 -

只有在完成 fetch() 並將回傳結果傳送至 .then() 之後,我們才會看到第二個 console.log() 顯示 It worked :) 的訊息出現。所以訊息的出現順序也許與你所期待的順序不同:

+只有在完成 `fetch()` 並將回傳結果傳送至 `.then()` 之後,我們才會看到第二個 `console.log()` 顯示 `It worked :)` 的訊息出現。所以訊息的出現順序也許與你所期待的順序不同: - +- Starting +- All done! +- It worked :) -

如果上面的例子讓你感到混淆,那麼考慮一下下面更簡單的例子:

+如果上面的例子讓你感到混淆,那麼考慮一下下面更簡單的例子: -
console.log("registering click handler");
+```js
+console.log("registering click handler");
 
-button.addEventListener('click', () => {
+button.addEventListener('click', () => {
   console.log("get click");
 });
 
-console.log("all done");
+console.log("all done"); +``` -

這是一個非常相似的行為——第一個和第三個 console.log() 的訊息將會立即的顯示,但是第二個正在被阻塞直到有人點擊了滑鼠按鈕。和上一個例子運作的方式類似,除了之前的例子在第二步是因為要等待取得資源之後並顯示在螢幕上才會被阻塞住,而不是等待被點擊。

+這是一個非常相似的行為——第一個和第三個 `console.log()` 的訊息將會立即的顯示,但是第二個正在被阻塞直到有人點擊了滑鼠按鈕。和上一個例子運作的方式類似,除了之前的例子在第二步是因為要等待取得資源之後並顯示在螢幕上才會被阻塞住,而不是等待被點擊。 -

在這個不直觀的例子中,這樣的設定方式可能會導致問題的發生——你不能在同步的程式碼去依賴一個非同步程式碼的執行結果,你不能保證非同步的函式會在瀏覽器執行下一個同步程式碼之前回傳結果。

+在這個不直觀的例子中,這樣的設定方式可能會導致問題的發生——你不能在同步的程式碼去依賴一個非同步程式碼的執行結果,你不能保證非同步的函式會在瀏覽器執行下一個同步程式碼之前回傳結果。 -

為了看這個動作,試著在本地複製我們的程式碼,並將最後的 console.log() 的呼叫改成底下的方式:

+為了看這個動作,試著在本地複製我們的[程式碼](https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/async-sync.html),並將最後的 `console.log()` 的呼叫改成底下的方式: -
console.log ('All done! ' + image.src + 'displayed.');
+```js +console.log ('All done! ' + image.src + 'displayed.'); +``` -

你應該會在控制台收到一個錯誤的訊息:

+你應該會在控制台收到一個錯誤的訊息: -
TypeError: image is undefined; can't access its "src" property
+```plain +TypeError: image is undefined; can't access its "src" property +``` -

這是因為在這個時間點瀏覽器試著去執行最後的 console.log()fetch() 還沒有完成執行,所以 image 變數尚未賦予值因而導致錯誤。

+這是因為在這個時間點瀏覽器試著去執行最後的 `console.log()` 時 `fetch()` 還沒有完成執行,所以 `image` 變數尚未賦予值因而導致錯誤。 -
-

備註: 由於安全性考量,你沒辦法呼叫 fetch() 從你的本地檔案系統抓取資料(或者其他在本地的相關操作)如果要在本地執行上面的範例,你需要在本地架起一個網路伺服器來執行。

-
+> **備註:** 由於安全性考量,你沒辦法呼叫 `fetch()` 從你的本地檔案系統抓取資料(或者其他在本地的相關操作)如果要在本地執行上面的範例,你需要在本地架起一個[網路伺服器](/zh-TW/docs/Learn/Common_questions/set_up_a_local_testing_server)來執行。 -

主動學習:讓一切非同步化!

+## 主動學習:讓一切非同步化! -

為了修復 fetch() 這個有問題的範例讓三個 console.log() 的敘述可以依序被執行你可以讓第三個 console.log() 也非同步的被執行。你可以新增一個新的 .then() 再將其放在裡面,這個新增的 .then() 再串連 promise 區塊的倒數第二個結尾;或者也可以將其放入原本的 .then() 中來解決這個問題。試著現在修復這個問題吧。

+為了修復 `fetch()` 這個有問題的範例讓三個 `console.log()` 的敘述可以依序被執行你可以讓第三個 `console.log()` 也非同步的被執行。你可以新增一個新的 `.then()` 再將其放在裡面,這個新增的 `.then()` 再串連 promise 區塊的倒數第二個結尾;或者也可以將其放入原本的 `.then()` 中來解決這個問題。試著現在修復這個問題吧。 -
-

備註: 如果你的想法卡住,你可以在這裡找到解答(線上範例)。你也可以在我們稍後的優雅的使用 Promise 來處理非同步操作 這個單元找到更多關於 promise 的資訊。

-
+> **備註:** 如果你的想法卡住,你可以[在這裡](https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/async-sync-fixed.html)找到解答([線上範例](https://mdn.github.io/learning-area/javascript/asynchronous/introducing/async-sync-fixed.html))。你也可以在我們稍後的[優雅的使用 Promise 來處理非同步操作](/zh-TW/docs/Learn/JavaScript/Asynchronous/Promises) 這個單元找到更多關於 promise 的資訊。 -

結論

+## 結論 -

Javascript 基本上是一個同步性的、阻塞的,且是跑在單一執行緒的程式語言,也就是在同一時間只能執行一個操作。但是瀏覽器所定義的函式和 API 允許我們註冊一個不該被同步執行的函式,且這個函式應該在某些事件發生時需要非同步的被呼叫(到達指定的時間、使用者透過滑鼠互動,或者取得透過從網路所取到的資料)。這代表你可以讓你的程式碼在同時間做一些事情而不需暫停或阻塞你的主執行緒。

+Javascript 基本上是一個同步性的、阻塞的,且是跑在單一執行緒的程式語言,也就是在同一時間只能執行一個操作。但是瀏覽器所定義的函式和 API 允許我們註冊一個不該被同步執行的函式,且這個函式應該在某些事件發生時需要非同步的被呼叫(到達指定的時間、使用者透過滑鼠互動,或者取得透過從網路所取到的資料)。這代表你可以讓你的程式碼在同時間做一些事情而不需暫停或阻塞你的主執行緒。 -

我們要讓程式碼同步地或非同步地執行取決於我們要做甚麼。

+我們要讓程式碼同步地或非同步地執行取決於我們要做甚麼。 -

有時候我們希望事情能夠立即的被載入並發生。例如某些使用者定義的樣式,你希望網頁能夠盡快地使用那些樣式。

+有時候我們希望事情能夠立即的被載入並發生。例如某些使用者定義的樣式,你希望網頁能夠盡快地使用那些樣式。 -

如果我們正在執行一些需要花一點時間的操作,就好比查詢資料庫並將結果填充到模板上,最好的方式是不要在主執行緒上執行並用非同步的方法來完成。隨著時間的學習,你將會了解到在更多的情況下,選擇使用非同步的技巧會比選擇同步的方式來的更合理。

+如果我們正在執行一些需要花一點時間的操作,就好比查詢資料庫並將結果填充到模板上,最好的方式是不要在主執行緒上執行並用非同步的方法來完成。隨著時間的學習,你將會了解到在更多的情況下,選擇使用非同步的技巧會比選擇同步的方式來的更合理。 -

{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Concepts", "Learn/JavaScript/Asynchronous/Timeouts_and_intervals", "Learn/JavaScript/Asynchronous")}}

+{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Concepts", "Learn/JavaScript/Asynchronous/Timeouts_and_intervals", "Learn/JavaScript/Asynchronous")}} -

在本單元

+## 在本單元 - +- [非同步程式設計通用概念](/zh-TW/docs/Learn/JavaScript/Asynchronous/Concepts) +- [非同步的 JavaScript 介紹](/zh-TW/docs/Learn/JavaScript/Asynchronous/Introducing) +- [協同的非同步 JavaScript : Timeout 和 interval](/zh-TW/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals) +- [優雅的使用 Promise 來處理非同步操作](/zh-TW/docs/Learn/JavaScript/Asynchronous/Promises) +- [利用 async 及 await 讓非同步程式設計變得更容易](/zh-TW/docs/Learn/JavaScript/Asynchronous/Async_await) +- [選擇正確的方法](/zh-TW/docs/Learn/JavaScript/Asynchronous/Choosing_the_right_approach) diff --git a/files/zh-tw/learn/javascript/asynchronous/promises/index.md b/files/zh-tw/learn/javascript/asynchronous/promises/index.md index 7aab1eab1a097c..0a3d66be7fb4e8 100644 --- a/files/zh-tw/learn/javascript/asynchronous/promises/index.md +++ b/files/zh-tw/learn/javascript/asynchronous/promises/index.md @@ -14,93 +14,94 @@ tags: - finally - then --- -
{{LearnSidebar}}
+{{LearnSidebar}}{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Timeouts_and_intervals", "Learn/JavaScript/Asynchronous/Async_await", "Learn/JavaScript/Asynchronous")}} -
{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Timeouts_and_intervals", "Learn/JavaScript/Asynchronous/Async_await", "Learn/JavaScript/Asynchronous")}}
- -

Promises 是在 Javascript 語言中相對較新的功能,它能夠讓你延遲活動直到先前的活動回報完成或失敗。這方法對設置一連串的操作並讓其正確的循序執行相當有用。本篇文章向您展示 promises 是如何運作,您將會看到如何被使用在 WebAPIs,以及如何寫出屬於自己的 promises。

+**Promises** 是在 Javascript 語言中相對較新的功能,它能夠讓你延遲活動直到先前的活動回報完成或失敗。這方法對設置一連串的操作並讓其正確的循序執行相當有用。本篇文章向您展示 promises 是如何運作,您將會看到如何被使用在 WebAPIs,以及如何寫出屬於自己的 promises。 - - - - - - - - - - + + + + + + + + + +
Prerequisites:Basic computer literacy, a reasonable understanding of JavaScript fundamentals.
Objective:To understand promises and how to use them.
Prerequisites: + Basic computer literacy, a reasonable understanding of JavaScript + fundamentals. +
Objective:To understand promises and how to use them.
-

What are promises?

+## What are promises? -

We looked at Promises briefly in the first article of the course, but here we'll look at them in a lot more depth.

+We looked at [Promises](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) briefly in the first article of the course, but here we'll look at them in a lot more depth. -

Essentially, a Promise is an object that represents an intermediate state of an operation — in effect, a promise that a result of some kind will be returned at some point in the future. There is no guarantee of exactly when the operation will complete and the result will be returned, but there is a guarantee that when the result is available, or the promise fails, the code you provide will be executed in order to do something else with a successful result, or to gracefully handle a failure case.

+Essentially, a Promise is an object that represents an intermediate state of an operation — in effect, a _promise_ that a result of some kind will be returned at some point in the future. There is no guarantee of exactly when the operation will complete and the result will be returned, but there _is_ a guarantee that when the result is available, or the promise fails, the code you provide will be executed in order to do something else with a successful result, or to gracefully handle a failure case. -

Generally, you are less interested in the amount of time an async operation will take to return its result (unless of course, it takes far too long!), and more interested in being able to respond to it being returned, whenever that is. And of course, it's nice that it doesn't block the rest of the code execution.

+Generally, you are less interested in the amount of time an async operation will take to return its result (unless of course, it takes _far_ too long!), and more interested in being able to respond to it being returned, whenever that is. And of course, it's nice that it doesn't block the rest of the code execution. -

One of the most common engagements you'll have with promises is with web APIs that return a promise. Let's consider a hypothetical video chat application. The application has a window with a list of the user's friends, and clicking on a button next to a user starts a video call to that user.

+One of the most common engagements you'll have with promises is with web APIs that return a promise. Let's consider a hypothetical video chat application. The application has a window with a list of the user's friends, and clicking on a button next to a user starts a video call to that user. -

That button's handler calls {{domxref("MediaDevices.getUserMedia", "getUserMedia()")}} in order to get access to the user's camera and microphone. Since getUserMedia() has to ensure that the user has permission to use those devices and ask the user which microphone to use and which camera to use (or whether to be a voice-only call, among other possible options), it can block until not only all of those decisions are made, but also the camera and microphone have been engaged. Also, the user may not respond immediately to these permission requests. This can potentially take a long time.

+That button's handler calls {{domxref("MediaDevices.getUserMedia", "getUserMedia()")}} in order to get access to the user's camera and microphone. Since `getUserMedia()` has to ensure that the user has permission to use those devices _and_ ask the user which microphone to use and which camera to use (or whether to be a voice-only call, among other possible options), it can block until not only all of those decisions are made, but also the camera and microphone have been engaged. Also, the user may not respond immediately to these permission requests. This can potentially take a long time. -

Since the call to getUserMedia() is made from the browser's main thread, the entire browser is blocked until getUserMedia() returns! Obviously, that's not an acceptable option; without promises, everything in the browser becomes unusable until the user decides what to do about the camera and microphone. So instead of waiting for the user, getting the chosen devices enabled, and directly returning the {{domxref("MediaStream")}} for the stream created from the selected sources, getUserMedia() returns a {{jsxref("promise")}} which is resolved with the {{domxref("MediaStream")}} once it's available.

+Since the call to `getUserMedia()` is made from the browser's main thread, the entire browser is blocked until `getUserMedia()` returns! Obviously, that's not an acceptable option; without promises, everything in the browser becomes unusable until the user decides what to do about the camera and microphone. So instead of waiting for the user, getting the chosen devices enabled, and directly returning the {{domxref("MediaStream")}} for the stream created from the selected sources, `getUserMedia()` returns a {{jsxref("promise")}} which is resolved with the {{domxref("MediaStream")}} once it's available. -

The code that the video chat application would use might look something like this:

+The code that the video chat application would use might look something like this: -
function handleCallButton(evt) {
+```js
+function handleCallButton(evt) {
   setStatusMessage("Calling...");
   navigator.mediaDevices.getUserMedia({video: true, audio: true})
-    .then(chatStream => {
+    .then(chatStream => {
       selfViewElem.srcObject = chatStream;
-      chatStream.getTracks().forEach(track => myPeerConnection.addTrack(track, chatStream));
+      chatStream.getTracks().forEach(track => myPeerConnection.addTrack(track, chatStream));
       setStatusMessage("Connected");
-    }).catch(err => {
+    }).catch(err => {
       setStatusMessage("Failed to connect");
     });
 }
-
+``` -

This function starts by using a function called setStatusMessage() to update a status display with the message "Calling...", indicating that a call is being attempted. It then calls getUserMedia(), asking for a stream that has both video and audio tracks, then once that's been obtained, sets up a video element to show the stream coming from the camera as a "self view," then takes each of the stream's tracks and adds them to the WebRTC {{domxref("RTCPeerConnection")}} representing a connection to another user. After that, the status display is updated to say "Connected".

+This function starts by using a function called `setStatusMessage()` to update a status display with the message "Calling...", indicating that a call is being attempted. It then calls `getUserMedia()`, asking for a stream that has both video and audio tracks, then once that's been obtained, sets up a video element to show the stream coming from the camera as a "self view," then takes each of the stream's tracks and adds them to the [WebRTC](/en-US/docs/Web/API/WebRTC_API) {{domxref("RTCPeerConnection")}} representing a connection to another user. After that, the status display is updated to say "Connected". -

If getUserMedia() fails, the catch block runs. This uses setStatusMessage() to update the status box to indicate that an error occurred.

+If `getUserMedia()` fails, the `catch` block runs. This uses `setStatusMessage()` to update the status box to indicate that an error occurred. -

The important thing here is that the getUserMedia() call returns almost immediately, even if the camera stream hasn't been obtained yet. Even if the handleCallButton() function has already returned to the code that called it, when getUserMedia() has finished working, it calls the handler you provide. As long as the app doesn't assume that streaming has begun, it can just keep on running.

+The important thing here is that the `getUserMedia()` call returns almost immediately, even if the camera stream hasn't been obtained yet. Even if the `handleCallButton()` function has already returned to the code that called it, when `getUserMedia()` has finished working, it calls the handler you provide. As long as the app doesn't assume that streaming has begun, it can just keep on running. -
-

備註: You can learn more about this somewhat advanced topic, if you're interested, in the article Signaling and video calling. Code similar to this, but much more complete, is used in that example.

-
+> **備註:** You can learn more about this somewhat advanced topic, if you're interested, in the article [Signaling and video calling](/en-US/docs/Web/API/WebRTC_API/Signaling_and_video_calling). Code similar to this, but much more complete, is used in that example. -

The trouble with callbacks

+## The trouble with callbacks -

To fully understand why promises are a good thing, it helps to think back to old-style callbacks and to appreciate why they are problematic.

+To fully understand why promises are a good thing, it helps to think back to old-style callbacks and to appreciate why they are problematic. -

Let's talk about ordering pizza as an analogy. There are certain steps that you have to take for your order to be successful, which doesn't really make sense to try to execute out of order, or in order but before each previous step has quite finished:

+Let's talk about ordering pizza as an analogy. There are certain steps that you have to take for your order to be successful, which doesn't really make sense to try to execute out of order, or in order but before each previous step has quite finished: -
    -
  1. You choose what toppings you want. This can take a while if you are indecisive, and may fail if you just can't make up your mind, or decide to get a curry instead.
  2. -
  3. You then place your order. This can take a while to return a pizza and may fail if the restaurant does not have the required ingredients to cook it.
  4. -
  5. You then collect your pizza and eat. This might fail if, say, you forgot your wallet so can't pay for the pizza!
  6. -
+1. You choose what toppings you want. This can take a while if you are indecisive, and may fail if you just can't make up your mind, or decide to get a curry instead. +2. You then place your order. This can take a while to return a pizza and may fail if the restaurant does not have the required ingredients to cook it. +3. You then collect your pizza and eat. This might fail if, say, you forgot your wallet so can't pay for the pizza! -

With old-style callbacks, a pseudo-code representation of the above functionality might look something like this:

+With old-style [callbacks](/en-US/docs/Learn/JavaScript/Asynchronous/Introducing#callbacks), a pseudo-code representation of the above functionality might look something like this: -
chooseToppings(function(toppings) {
+```js
+chooseToppings(function(toppings) {
   placeOrder(toppings, function(order) {
     collectOrder(order, function(pizza) {
       eatPizza(pizza);
     }, failureCallback);
   }, failureCallback);
-}, failureCallback);
+}, failureCallback); +``` -

This is messy and hard to read (often referred to as "callback hell"), requires the failureCallback() to be called multiple times (once for each nested function), with other issues besides.

+This is messy and hard to read (often referred to as "[callback hell](http://callbackhell.com/)"), requires the `failureCallback()` to be called multiple times (once for each nested function), with other issues besides. -

Improvements with promises

+### Improvements with promises -

Promises make situations like the above much easier to write, parse, and run. If we represented the above pseudo-code using asynchronous promises instead, we'd end up with something like this:

+Promises make situations like the above much easier to write, parse, and run. If we represented the above pseudo-code using asynchronous promises instead, we'd end up with something like this: -
chooseToppings()
+```js
+chooseToppings()
 .then(function(toppings) {
   return placeOrder(toppings);
 })
@@ -110,346 +111,336 @@ tags:
 .then(function(pizza) {
   eatPizza(pizza);
 })
-.catch(failureCallback);
+.catch(failureCallback); +``` -

This is much better — it is easier to see what is going on, we only need a single .catch() block to handle all the errors, it doesn't block the main thread (so we can keep playing video games while we wait for the pizza to be ready to collect), and each operation is guaranteed to wait for previous operations to complete before running. We're able to chain multiple asynchronous actions to occur one after another this way because each .then() block returns a new promise that resolves when the .then() block is done running. Clever, right?

+This is much better — it is easier to see what is going on, we only need a single `.catch()` block to handle all the errors, it doesn't block the main thread (so we can keep playing video games while we wait for the pizza to be ready to collect), and each operation is guaranteed to wait for previous operations to complete before running. We're able to chain multiple asynchronous actions to occur one after another this way because each `.then()` block returns a new promise that resolves when the `.then()` block is done running. Clever, right? -

Using arrow functions, you can simplify the code even further:

+Using arrow functions, you can simplify the code even further: -
chooseToppings()
-.then(toppings =>
+```js
+chooseToppings()
+.then(toppings =>
   placeOrder(toppings)
 )
-.then(order =>
+.then(order =>
   collectOrder(order)
 )
-.then(pizza =>
+.then(pizza =>
   eatPizza(pizza)
 )
-.catch(failureCallback);
+.catch(failureCallback); +``` -

Or even this:

+Or even this: -
chooseToppings()
-.then(toppings => placeOrder(toppings))
-.then(order => collectOrder(order))
-.then(pizza => eatPizza(pizza))
-.catch(failureCallback);
+```js +chooseToppings() +.then(toppings => placeOrder(toppings)) +.then(order => collectOrder(order)) +.then(pizza => eatPizza(pizza)) +.catch(failureCallback); +``` -

This works because with arrow functions () => x is valid shorthand for () => { return x; }.

+This works because with arrow functions `() => x` is valid shorthand for `() => { return x; }`. -

You could even do this, since the functions just pass their arguments directly, so there isn't any need for that extra layer of functions:

+You could even do this, since the functions just pass their arguments directly, so there isn't any need for that extra layer of functions: -
chooseToppings().then(placeOrder).then(collectOrder).then(eatPizza).catch(failureCallback);
+```js +chooseToppings().then(placeOrder).then(collectOrder).then(eatPizza).catch(failureCallback); +``` -

This is not quite as easy to read, however, and this syntax might not be usable if your blocks are more complex than what we've shown here.

+This is not quite as easy to read, however, and this syntax might not be usable if your blocks are more complex than what we've shown here. -
-

備註: You can make further improvements with async/await syntax, which we'll dig into in the next article.

-
+> **備註:** You can make further improvements with `async`/`await` syntax, which we'll dig into in the next article. -

At their most basic, promises are similar to event listeners, but with a few differences:

+At their most basic, promises are similar to event listeners, but with a few differences: - +- A promise can only succeed or fail once. It cannot succeed or fail twice and it cannot switch from success to failure or vice versa once the operation has completed. +- If a promise has succeeded or failed and you later add a success/failure callback, the correct callback will be called, even though the event took place earlier. -

Explaining basic promise syntax: A real example

+## Explaining basic promise syntax: A real example -

Promises are important to understand because most modern Web APIs use them for functions that perform potentially lengthy tasks. To use modern web technologies you'll need to use promises. Later on in the chapter, we'll look at how to write your own promise, but for now, we'll look at some simple examples that you'll encounter in Web APIs.

+Promises are important to understand because most modern Web APIs use them for functions that perform potentially lengthy tasks. To use modern web technologies you'll need to use promises. Later on in the chapter, we'll look at how to write your own promise, but for now, we'll look at some simple examples that you'll encounter in Web APIs. -

In the first example, we'll use the fetch() method to fetch an image from the web, the {{domxref("Body.blob", "blob()")}} method to transform the fetch response's raw body contents into a {{domxref("Blob")}} object, and then display that blob inside an {{htmlelement("img")}} element. This is very similar to the example we looked at in the first article of the series, but we'll do it a bit differently as we get you building your own promise-based code.

+In the first example, we'll use the [`fetch()`](/en-US/docs/Web/API/fetch) method to fetch an image from the web, the {{domxref("Body.blob", "blob()")}} method to transform the fetch response's raw body contents into a {{domxref("Blob")}} object, and then display that blob inside an {{htmlelement("img")}} element. This is very similar to the example we looked at in the [first article of the series](/en-US/docs/Learn/JavaScript/Asynchronous/Introducing#asynchronous_javascript), but we'll do it a bit differently as we get you building your own promise-based code. -
-

備註: The following example will not work if you just run it directly from the file (i.e. via a file:// URL). You need to run it through a local testing server, or use an online solution such as Glitch or GitHub pages.

-
+> **備註:** The following example will not work if you just run it directly from the file (i.e. via a `file://` URL). You need to run it through a [local testing server](/en-US/docs/Learn/Common_questions/set_up_a_local_testing_server), or use an online solution such as [Glitch](https://glitch.com/) or [GitHub pages](/en-US/docs/Learn/Common_questions/Using_Github_pages). -
    -
  1. -

    First of all, download our simple HTML template and the sample image file that we'll fetch.

    -
  2. -
  3. -

    Add a {{htmlelement("script")}} element at the bottom of the HTML {{htmlelement("body")}}.

    -
  4. -
  5. -

    Inside your {{HTMLElement("script")}} element, add the following line:

    +1. First of all, download our [simple HTML template](https://github.com/mdn/learning-area/blob/master/html/introduction-to-html/getting-started/index.html) and the [sample image file](https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/promises/coffee.jpg) that we'll fetch. +2. Add a {{htmlelement("script")}} element at the bottom of the HTML {{htmlelement("body")}}. +3. Inside your {{HTMLElement("script")}} element, add the following line: -
    let promise = fetch('coffee.jpg');
    + ```js + let promise = fetch('coffee.jpg'); + ``` -

    This calls the fetch() method, passing it the URL of the image to fetch from the network as a parameter. This can also take an options object as an optional second parameter, but we are just using the simplest version for now. We are storing the promise object returned by fetch() inside a variable called promise. As we said before, this object represents an intermediate state that is initially neither success nor failure — the official term for a promise in this state is pending.

    -
  6. -
  7. -

    To respond to the successful completion of the operation whenever that occurs (in this case, when a {{domxref("Response")}} is returned), we invoke the .then() method of the promise object. The callback inside the .then() block runs only when the promise call completes successfully and returns the {{domxref("Response")}} object — in promise-speak, when it has been fulfilled. It is passed the returned {{domxref("Response")}} object as a parameter.

    + This calls the `fetch()` method, passing it the URL of the image to fetch from the network as a parameter. This can also take an options object as an optional second parameter, but we are just using the simplest version for now. We are storing the promise object returned by `fetch()` inside a variable called `promise`. As we said before, this object represents an intermediate state that is initially neither success nor failure — the official term for a promise in this state is **pending**. -
    -

    備註: The way that a .then() block works is similar to when you add an event listener to an object using AddEventListener(). It doesn't run until an event occurs (when the promise fulfills). The most notable difference is that a .then() will only run once for each time it is used, whereas an event listener could be invoked multiple times.

    -
    +4. To respond to the successful completion of the operation whenever that occurs (in this case, when a {{domxref("Response")}} is returned), we invoke the [`.then()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then) method of the promise object. The callback inside the `.then()` block runs only when the promise call completes successfully and returns the {{domxref("Response")}} object — in promise-speak, when it has been **fulfilled**. It is passed the returned {{domxref("Response")}} object as a parameter. -

    We immediately run the blob() method on this response to ensure that the response body is fully downloaded, and when it is available transform it into a Blob object that we can do something with. The result of this is returned like so:

    + > **備註:** The way that a `.then()` block works is similar to when you add an event listener to an object using `AddEventListener()`. It doesn't run until an event occurs (when the promise fulfills). The most notable difference is that a `.then()` will only run once for each time it is used, whereas an event listener could be invoked multiple times. -
    response => response.blob()
    + We immediately run the `blob()` method on this response to ensure that the response body is fully downloaded, and when it is available transform it into a `Blob` object that we can do something with. The result of this is returned like so: -

    which is shorthand for

    + ```js + response => response.blob() + ``` -
    function(response) {
    -  return response.blob();
    -}
    + which is shorthand for -

    Unfortunately, we need to do slightly more than this. Fetch promises do not fail on 404 or 500 errors — only on something catastrophic like a network failure. Instead, they succeed, but with the response.ok property set to false. To produce an error on a 404, for example, we need to check the value of response.ok, and if false, throw an error, only returning the blob if it is true. This can be done like so — add the following lines below your first line of JavaScript.

    + ```js + function(response) { + return response.blob(); + } + ``` -
    let promise2 = promise.then(response => {
    -  if (!response.ok) {
    -    throw new Error(`HTTP error! status: ${response.status}`);
    -  } else {
    -    return response.blob();
    -  }
    -});
    -
  8. -
  9. -

    Each call to .then() creates a new promise. This is very useful; because the blob() method also returns a promise, we can handle the Blob object it returns on fulfillment by invoking the .then() method of the second promise. Because we want to do something a bit more complex to the blob than just run a single method on it and return the result, we'll need to wrap the function body in curly braces this time (otherwise it'll throw an error).

    + Unfortunately, we need to do slightly more than this. Fetch promises do not fail on 404 or 500 errors — only on something catastrophic like a network failure. Instead, they succeed, but with the [`response.ok`](/en-US/docs/Web/API/Response/ok) property set to `false`. To produce an error on a 404, for example, we need to check the value of `response.ok`, and if `false`, throw an error, only returning the blob if it is `true`. This can be done like so — add the following lines below your first line of JavaScript. + + ```js + let promise2 = promise.then(response => { + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } else { + return response.blob(); + } + }); + ``` + +5. Each call to `.then()` creates a new promise. This is very useful; because the `blob()` method also returns a promise, we can handle the `Blob` object it returns on fulfillment by invoking the `.then()` method of the second promise. Because we want to do something a bit more complex to the blob than just run a single method on it and return the result, we'll need to wrap the function body in curly braces this time (otherwise it'll throw an error). + + Add the following to the end of your code: -

    Add the following to the end of your code:

    + ```js + let promise3 = promise2.then(myBlob => { -
    let promise3 = promise2.then(myBlob => {
    +    })
    +    ```
     
    -})
    -
  10. -
  11. -

    Now let's fill in the body of the .then() callback. Add the following lines inside the curly braces:

    +6. Now let's fill in the body of the `.then()` callback. Add the following lines inside the curly braces: -
    let objectURL = URL.createObjectURL(myBlob);
    -let image = document.createElement('img');
    -image.src = objectURL;
    -document.body.appendChild(image);
    + ```js + let objectURL = URL.createObjectURL(myBlob); + let image = document.createElement('img'); + image.src = objectURL; + document.body.appendChild(image); + ``` -

    Here we are running the {{domxref("URL.createObjectURL()")}} method, passing it as a parameter the Blob returned when the second promise fulfills. This will return a URL pointing to the object. Then we create an {{htmlelement("img")}} element, set its src attribute to equal the object URL and append it to the DOM, so the image will display on the page!

    -
  12. -
+ Here we are running the {{domxref("URL.createObjectURL()")}} method, passing it as a parameter the `Blob` returned when the second promise fulfills. This will return a URL pointing to the object. Then we create an {{htmlelement("img")}} element, set its `src` attribute to equal the object URL and append it to the DOM, so the image will display on the page! -

If you save the HTML file you've just created and load it in your browser, you'll see that the image is displayed in the page as expected. Good work!

+If you save the HTML file you've just created and load it in your browser, you'll see that the image is displayed in the page as expected. Good work! -
-

備註: You will probably notice that these examples are somewhat contrived. You could just do away with the whole fetch() and blob() chain, and just create an <img> element and set its src attribute value to the URL of the image file, coffee.jpg. We did, however, pick this example because it demonstrates promises in a nice simple fashion, rather than for its real-world appropriateness.

-
+> **備註:** You will probably notice that these examples are somewhat contrived. You could just do away with the whole `fetch()` and `blob()` chain, and just create an `` element and set its `src` attribute value to the URL of the image file, `coffee.jpg`. We did, however, pick this example because it demonstrates promises in a nice simple fashion, rather than for its real-world appropriateness. -

Responding to failure

+### Responding to failure -

Something is missing — currently, there is nothing to explicitly handle errors if one of the promises fails (rejects, in promise-speak). We can add error handling by running the .catch() method off the previous promise. Add this now:

+Something is missing — currently, there is nothing to explicitly handle errors if one of the promises fails (**rejects**, in promise-speak). We can add error handling by running the [`.catch()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch) method off the previous promise. Add this now: -
let errorCase = promise3.catch(e => {
+```js
+let errorCase = promise3.catch(e => {
   console.log('There has been a problem with your fetch operation: ' + e.message);
-});
+}); +``` -

To see this in action, try misspelling the URL to the image and reloading the page. The error will be reported in the console of your browser's developer tools.

+To see this in action, try misspelling the URL to the image and reloading the page. The error will be reported in the console of your browser's developer tools. -

This doesn't do much more than it would if you just didn't bother including the .catch() block at all, but think about it — this allows us to control error handling exactly how we want. In a real app, your .catch() block could retry fetching the image, or show a default image, or prompt the user to provide a different image URL, or whatever.

+This doesn't do much more than it would if you just didn't bother including the `.catch()` block at all, but think about it — this allows us to control error handling exactly how we want. In a real app, your `.catch()` block could retry fetching the image, or show a default image, or prompt the user to provide a different image URL, or whatever. -
-

備註: You can see our version of the example live (see the source code also).

-
+> **備註:** You can see [our version of the example live](https://mdn.github.io/learning-area/javascript/asynchronous/promises/simple-fetch.html) (see the [source code](https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/promises/simple-fetch.html) also). -

Chaining the blocks together

+### Chaining the blocks together -

This is a very longhand way of writing this out; we've deliberately done this to help you understand what is going on clearly. As shown earlier on in the article, you can chain together .then() blocks (and also .catch() blocks). The above code could also be written like this (see also simple-fetch-chained.html on GitHub):

+This is a very longhand way of writing this out; we've deliberately done this to help you understand what is going on clearly. As shown earlier on in the article, you can chain together `.then()` blocks (and also `.catch()` blocks). The above code could also be written like this (see also [simple-fetch-chained.html](https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/promises/simple-fetch-chained.html) on GitHub): -
fetch('coffee.jpg')
-.then(response => {
+```js
+fetch('coffee.jpg')
+.then(response => {
   if (!response.ok) {
     throw new Error(`HTTP error! status: ${response.status}`);
   } else {
     return response.blob();
   }
 })
-.then(myBlob => {
+.then(myBlob => {
   let objectURL = URL.createObjectURL(myBlob);
   let image = document.createElement('img');
   image.src = objectURL;
   document.body.appendChild(image);
 })
-.catch(e => {
+.catch(e => {
   console.log('There has been a problem with your fetch operation: ' + e.message);
-});
+}); +``` -

Bear in mind that the value returned by a fulfilled promise becomes the parameter passed to the next .then() block's callback function.

+Bear in mind that the value returned by a fulfilled promise becomes the parameter passed to the next `.then()` block's callback function. -
-

備註: .then()/.catch() blocks in promises are basically the async equivalent of a try...catch block in sync code. Bear in mind that synchronous try...catch won't work in async code.

-
+> **備註:** `.then()`/`.catch()` blocks in promises are basically the async equivalent of a [`try...catch`](/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block in sync code. Bear in mind that synchronous `try...catch` won't work in async code. -

Promise terminology recap

+## Promise terminology recap -

There was a lot to cover in the above section, so let's go back over it quickly to give you a short guide that you can bookmark and use to refresh your memory in the future. You should also go over the above section again a few more time to make sure these concepts stick.

+There was a lot to cover in the above section, so let's go back over it quickly to give you a [short guide that you can bookmark](/en-US/docs/Learn/JavaScript/Asynchronous/Promises#promise_terminology_recap) and use to refresh your memory in the future. You should also go over the above section again a few more time to make sure these concepts stick. -
    -
  1. When a promise is created, it is neither in a success or failure state. It is said to be pending.
  2. -
  3. When a promise returns, it is said to be resolved. -
      -
    1. A successfully resolved promise is said to be fulfilled. It returns a value, which can be accessed by chaining a .then() block onto the end of the promise chain. The callback function inside the .then() block will contain the promise's return value.
    2. -
    3. An unsuccessful resolved promise is said to be rejected. It returns a reason, an error message stating why the promise was rejected. This reason can be accessed by chaining a .catch() block onto the end of the promise chain.
    4. -
    -
  4. -
+1. When a promise is created, it is neither in a success or failure state. It is said to be **pending**. +2. When a promise returns, it is said to be **resolved**. -

Running code in response to multiple promises fulfilling

+ 1. A successfully resolved promise is said to be **fulfilled**. It returns a value, which can be accessed by chaining a `.then()` block onto the end of the promise chain. The callback function inside the `.then()` block will contain the promise's return value. + 2. An unsuccessful resolved promise is said to be **rejected**. It returns a **reason**, an error message stating why the promise was rejected. This reason can be accessed by chaining a `.catch()` block onto the end of the promise chain. -

The above example showed us some of the real basics of using promises. Now let's look at some more advanced features. For a start, chaining processes to occur one after the other is all fine, but what if you want to run some code only after a whole bunch of promises have all fulfilled?

+## Running code in response to multiple promises fulfilling -

You can do this with the ingeniously named Promise.all() static method. This takes an array of promises as an input parameter and returns a new Promise object that will fulfil only if and when all promises in the array fulfil. It looks something like this:

+The above example showed us some of the real basics of using promises. Now let's look at some more advanced features. For a start, chaining processes to occur one after the other is all fine, but what if you want to run some code only after a whole bunch of promises have _all_ fulfilled? -
Promise.all([a, b, c]).then(values => {
+You can do this with the ingeniously named [`Promise.all()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) static method. This takes an array of promises as an input parameter and returns a new `Promise` object that will fulfil only if and when _all_ promises in the array fulfil. It looks something like this:
+
+```js
+Promise.all([a, b, c]).then(values => {
   ...
-});
+}); +``` -

If they all fulfil, the chained .then() block's callback function will be passed an array containing all those results as a parameter. If any of the promises passed to Promise.all() reject, the whole block will reject.

+If they all fulfil, the chained `.then()` block's callback function will be passed an array containing all those results as a parameter. If any of the promises passed to `Promise.all()` reject, the whole block will reject. -

This can be very useful. Imagine that we’re fetching information to dynamically populate a UI feature on our page with content. In many cases, it makes sense to receive all the data and only then show the complete content, rather than displaying partial information.

+This can be very useful. Imagine that we’re fetching information to dynamically populate a UI feature on our page with content. In many cases, it makes sense to receive all the data and only then show the complete content, rather than displaying partial information. -

Let's build another example to show this in action.

+Let's build another example to show this in action. -
    -
  1. -

    Download a fresh copy of our page template, and again put a <script> element just before the closing </body> tag.

    -
  2. -
  3. -

    Download our source files (coffee.jpg, tea.jpg, and description.txt), or feel free to substitute your own.

    -
  4. -
  5. -

    In our script, we'll first define a function that returns the promises we want to send to Promise.all(). This would be easy if we just wanted to run the Promise.all() block in response to three fetch() operations completing. We could just do something like:

    +1. Download a fresh copy of our [page template](https://github.com/mdn/learning-area/blob/master/html/introduction-to-html/getting-started/index.html), and again put a ` + + +
    Click me!
    +
    + + +``` -

    Try this example below:

    +Try this example below: -

    {{ EmbedLiveSample('Example_using_event_attributes', '', '', '') }}

    +{{ EmbedLiveSample('Example_using_event_attributes', '', '', '') }} -

    Example using event listeners

    +## Example using event listeners -

    Instead, you should use {{domxref("EventTarget.addEventListener()")}}, as shown here:

    +Instead, you should use {{domxref("EventTarget.addEventListener()")}}, as shown here: -
    <!doctype html>
    -<html>
    -  <head>
    -    <title>Event Attribute Example</title>
    -    <script>
    +```html
    +
    +
    +  
    +    Event Attribute Example
    +    
    +  
    +  
    +    
    Click me!
    +
    + + +``` + +You can see this in action below: + +{{ EmbedLiveSample('Example_using_event_listeners', '', '', '') }} diff --git a/files/zh-tw/learn/javascript/building_blocks/functions/index.md b/files/zh-tw/learn/javascript/building_blocks/functions/index.md index eb4f2ed85a9813..1f788485e10613 100644 --- a/files/zh-tw/learn/javascript/building_blocks/functions/index.md +++ b/files/zh-tw/learn/javascript/building_blocks/functions/index.md @@ -3,320 +3,389 @@ title: 函數 - 可重複使用的代碼塊 slug: Learn/JavaScript/Building_blocks/Functions translation_of: Learn/JavaScript/Building_blocks/Functions --- -
    {{LearnSidebar}}
    +{{LearnSidebar}}{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Looping_code","Learn/JavaScript/Building_blocks/Build_your_own_function", "Learn/JavaScript/Building_blocks")}} -
    {{PreviousMenuNext("Learn/JavaScript/Building_blocks/Looping_code","Learn/JavaScript/Building_blocks/Build_your_own_function", "Learn/JavaScript/Building_blocks")}}
    - -

    編碼中的另一個基本概念是函數,它允許您存儲一段代碼,該代碼在定義的塊內執行單個任務,然後在需要時使用一個簡短命令調用該代碼 - 而不必輸入相同的代碼 代碼多次。 在本文中,我們將探索函數背後的基本概念,例如基本語法,如何調用和定義它們,範圍和參數。

    +編碼中的另一個基本概念是函數,它允許您存儲一段代碼,該代碼在定義的塊內執行單個任務,然後在需要時使用一個簡短命令調用該代碼 - 而不必輸入相同的代碼 代碼多次。 在本文中,我們將探索函數背後的基本概念,例如基本語法,如何調用和定義它們,範圍和參數。 - - - - - - - - - - + + + + + + + + + +
    Prerequisites:Basic computer literacy, a basic understanding of HTML and CSS, JavaScript first steps.
    Objective:To understand the fundamental concepts behind JavaScript functions.
    Prerequisites: + Basic computer literacy, a basic understanding of HTML and CSS, + JavaScript first steps. +
    Objective: + To understand the fundamental concepts behind JavaScript functions. +
    -

    Where do I find functions?

    +## Where do I find functions? -

    In JavaScript, you'll find functions everywhere. In fact, we've been using functions all the way through the course so far; we've just not been talking about them very much. Now is the time, however, for us to start talking about functions explicitly, and really exploring their syntax.

    +In JavaScript, you'll find functions everywhere. In fact, we've been using functions all the way through the course so far; we've just not been talking about them very much. Now is the time, however, for us to start talking about functions explicitly, and really exploring their syntax. -

    Pretty much anytime you make use of a JavaScript structure that features a pair of parentheses — () — and you're not using a common built-in language structure like a for loop, while or do...while loop, or if...else statement, you are making use of a function.

    +Pretty much anytime you make use of a JavaScript structure that features a pair of parentheses — `()` — and you're **not** using a common built-in language structure like a [for loop](/en-US/Learn/JavaScript/Building_blocks/Looping_code#The_standard_for_loop), [while or do...while loop](/en-US/Learn/JavaScript/Building_blocks/Looping_code#while_and_do_..._while), or [if...else statement](/en-US/Learn/JavaScript/Building_blocks/conditionals#if_..._else_statements), you are making use of a function. -

    Built-in browser functions

    +## Built-in browser functions -

    We've made use of functions built in to the browser a lot in this course. Every time we manipulated a text string, for example:

    +We've made use of functions built in to the browser a lot in this course. Every time we manipulated a text string, for example: -
    var myText = 'I am a string';
    +```js
    +var myText = 'I am a string';
     var newString = myText.replace('string', 'sausage');
     console.log(newString);
     // the replace() string function takes a string,
     // replaces one substring with another, and returns
    -// a new string with the replacement made
    +// a new string with the replacement made +``` -

    Or every time we manipulated an array:

    +Or every time we manipulated an array: -
    var myArray = ['I', 'love', 'chocolate', 'frogs'];
    +```js
    +var myArray = ['I', 'love', 'chocolate', 'frogs'];
     var madeAString = myArray.join(' ');
     console.log(madeAString);
     // the join() function takes an array, joins
     // all the array items together into a single
    -// string, and returns this new string
    +// string, and returns this new string +``` -

    Or every time we generated a random number:

    +Or every time we generated a random number: -
    var myNumber = Math.random();
    +```js
    +var myNumber = Math.random();
     // the random() function generates a random
     // number between 0 and 1, and returns that
    -// number
    +// number +``` -

    ...we were using a function!

    +...we were using a function! -
    -

    備註: Feel free to enter these lines into your browser's JavaScript console to re-familiarize yourself with their functionality, if needed.

    -
    +> **備註:** Feel free to enter these lines into your browser's JavaScript console to re-familiarize yourself with their functionality, if needed. -

    The JavaScript language has many built-in functions to allow you to do useful things without having to write all that code yourself. In fact, some of the code you are calling when you invoke (a fancy word for run, or execute) a built in browser function couldn't be written in JavaScript — many of these functions are calling parts of the background browser code, which is written largely in low-level system languages like C++, not web languages like JavaScript.

    +The JavaScript language has many built-in functions to allow you to do useful things without having to write all that code yourself. In fact, some of the code you are calling when you **invoke** (a fancy word for run, or execute) a built in browser function couldn't be written in JavaScript — many of these functions are calling parts of the background browser code, which is written largely in low-level system languages like C++, not web languages like JavaScript. -

    Bear in mind that some built-in browser functions are not part of the core JavaScript language — some are defined as part of browser APIs, which build on top of the default language to provide even more functionality (refer to this early section of our course for more descriptions). We'll look at using browser APIs in more detail in a later module.

    +Bear in mind that some built-in browser functions are not part of the core JavaScript language — some are defined as part of browser APIs, which build on top of the default language to provide even more functionality (refer to [this early section of our course](/en-US/Learn/JavaScript/First_steps/What_is_JavaScript#So_what_can_it_really_do) for more descriptions). We'll look at using browser APIs in more detail in a later module. -

    Functions versus methods

    +## Functions versus methods -

    One thing we need to clear up before we move on — technically speaking, built in browser functions are not functions — they are methods. This sounds a bit scary and confusing, but don't worry — the words function and method are largely interchangeable, at least for our purposes, at this stage in your learning.

    +One thing we need to clear up before we move on — technically speaking, built in browser functions are not functions — they are **methods**. This sounds a bit scary and confusing, but don't worry — the words function and method are largely interchangeable, at least for our purposes, at this stage in your learning. -

    The distinction is that methods are functions defined inside objects. Built-in browser functions (methods) and variables (which are called properties) are stored inside structured objects, to make the code more efficient and easier to handle.

    +The distinction is that methods are functions defined inside objects. Built-in browser functions (methods) and variables (which are called **properties**) are stored inside structured objects, to make the code more efficient and easier to handle. -

    You don't need to learn about the inner workings of structured JavaScript objects yet — you can wait until our later module that will teach you all about the inner workings of objects, and how to create your own. For now, we just wanted to clear up any possible confusion of method versus function — you are likely to meet both terms as you look at the available related resources across the Web.

    +You don't need to learn about the inner workings of structured JavaScript objects yet — you can wait until our later module that will teach you all about the inner workings of objects, and how to create your own. For now, we just wanted to clear up any possible confusion of method versus function — you are likely to meet both terms as you look at the available related resources across the Web. -

    Custom functions

    +## Custom functions -

    You've also seen a lot of custom functions in the course so far — functions defined in your code, not inside the browser. Anytime you saw a custom name with parentheses straight after it, you were using a custom function. In our random-canvas-circles.html example (see also the full source code) from our loops article, we included a custom draw() function that looked like this:

    +You've also seen a lot of **custom functions** in the course so far — functions defined in your code, not inside the browser. Anytime you saw a custom name with parentheses straight after it, you were using a custom function. In our [random-canvas-circles.html](http://mdn.github.io/learning-area/javascript/building-blocks/loops/random-canvas-circles.html) example (see also the full [source code](https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/loops/random-canvas-circles.html)) from our [loops article](/en-US/docs/Learn/JavaScript/Building_blocks/Looping_code), we included a custom `draw()` function that looked like this: -
    function draw() {
    +```js
    +function draw() {
       ctx.clearRect(0,0,WIDTH,HEIGHT);
    -  for (var i = 0; i < 100; i++) {
    +  for (var i = 0; i < 100; i++) {
         ctx.beginPath();
         ctx.fillStyle = 'rgba(255,0,0,0.5)';
         ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
         ctx.fill();
       }
    -}
    +} +``` -

    This function draws 100 random circles inside an {{htmlelement("canvas")}} element. Every time we want to do that, we can just invoke the function with this

    +This function draws 100 random circles inside an {{htmlelement("canvas")}} element. Every time we want to do that, we can just invoke the function with this -
    draw();
    +```js +draw(); +``` -

    rather than having to write all that code out again every time we want to repeat it. And functions can contain whatever code you like — you can even call other functions from inside functions. The above function for example calls the random() function three times, which is defined by the following code:

    +rather than having to write all that code out again every time we want to repeat it. And functions can contain whatever code you like — you can even call other functions from inside functions. The above function for example calls the `random()` function three times, which is defined by the following code: -
    function random(number) {
    +```js
    +function random(number) {
       return Math.floor(Math.random()*number);
    -}
    +} +``` -

    We needed this function because the browser's built-in Math.random() function only generates a random decimal number between 0 and 1. We wanted a random whole number between 0 and a specified number.

    +We needed this function because the browser's built-in [Math.random()](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random) function only generates a random decimal number between 0 and 1. We wanted a random whole number between 0 and a specified number. -

    Invoking functions

    +## Invoking functions -

    You are probably clear on this by now, but just in case ... to actually use a function after it has been defined, you've got to run — or invoke — it. This is done by including the name of the function in the code somewhere, followed by parentheses.

    +You are probably clear on this by now, but just in case ... to actually use a function after it has been defined, you've got to run — or invoke — it. This is done by including the name of the function in the code somewhere, followed by parentheses. -
    function myFunction() {
    +```js
    +function myFunction() {
       alert('hello');
     }
     
     myFunction()
    -// calls the function once
    +// calls the function once +``` -

    Anonymous functions

    +## Anonymous functions -

    You may see functions defined and invoked in slightly different ways. So far we have just created a function like so:

    +You may see functions defined and invoked in slightly different ways. So far we have just created a function like so: -
    function myFunction() {
    +```js
    +function myFunction() {
       alert('hello');
    -}
    +} +``` -

    But you can also create a function that doesn't have a name:

    +But you can also create a function that doesn't have a name: -
    function() {
    +```js
    +function() {
       alert('hello');
    -}
    +} +``` -

    This is called an anonymous function — it has no name! It also won't do anything on its own. You generally use an anonymous function along with an event handler, for example the following would run the code inside the function whenever the associated button is clicked:

    +This is called an **anonymous function** — it has no name! It also won't do anything on its own. You generally use an anonymous function along with an event handler, for example the following would run the code inside the function whenever the associated button is clicked: -
    var myButton = document.querySelector('button');
    +```js
    +var myButton = document.querySelector('button');
     
     myButton.onclick = function() {
       alert('hello');
    -}
    +} +``` -

    The above example would require there to be a {{htmlelement("button")}} element available on the page to select and click. You've already seen this structure a few times throughout the course, and you'll learn more about and see it in use in the next article.

    +The above example would require there to be a {{htmlelement("button")}} element available on the page to select and click. You've already seen this structure a few times throughout the course, and you'll learn more about and see it in use in the next article. -

    You can also assign an anonymous function to be the value of a variable, for example:

    +You can also assign an anonymous function to be the value of a variable, for example: -
    var myGreeting = function() {
    +```js
    +var myGreeting = function() {
       alert('hello');
    -}
    +} +``` -

    This function could now be invoked using:

    +This function could now be invoked using: -
    myGreeting();
    +```js +myGreeting(); +``` -

    This effectively gives the function a name; you can also assign the function to be the value of multiple variables, for example:

    +This effectively gives the function a name; you can also assign the function to be the value of multiple variables, for example: -
    var anotherGreeting = function() {
    +```js
    +var anotherGreeting = function() {
       alert('hello');
    -}
    +} +``` -

    This function could now be invoked using either of

    +This function could now be invoked using either of -
    myGreeting();
    -anotherGreeting();
    +```js +myGreeting(); +anotherGreeting(); +``` -

    But this would just be confusing, so don't do it! When creating functions, it is better to just stick to this form:

    +But this would just be confusing, so don't do it! When creating functions, it is better to just stick to this form: -
    function myGreeting() {
    +```js
    +function myGreeting() {
       alert('hello');
    -}
    +} +``` -

    You will mainly use anonymous functions to just run a load of code in response to an event firing — like a button being clicked — using an event handler. Again, this looks something like this:

    +You will mainly use anonymous functions to just run a load of code in response to an event firing — like a button being clicked — using an event handler. Again, this looks something like this: -
    myButton.onclick = function() {
    +```js
    +myButton.onclick = function() {
       alert('hello');
       // I can put as much code
       // inside here as I want
    -}
    +} +``` -

    Function parameters

    +## Function parameters -

    Some functions require parameters to be specified when you are invoking them — these are values that need to be included inside the function parentheses, which it needs to do its job properly.

    +Some functions require **parameters** to be specified when you are invoking them — these are values that need to be included inside the function parentheses, which it needs to do its job properly. -
    -

    備註: Parameters are sometimes called arguments, properties, or even attributes.

    -
    +> **備註:** Parameters are sometimes called arguments, properties, or even attributes. -

    As an example, the browser's built-in Math.random() function doesn't require any parameters. When called, it always returns a random number between 0 and 1:

    +As an example, the browser's built-in [Math.random()](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random) function doesn't require any parameters. When called, it always returns a random number between 0 and 1: -
    var myNumber = Math.random();
    +```js +var myNumber = Math.random(); +``` -

    The browser's built-in string replace() function however needs two parameters — the substring to find in the main string, and the substring to replace that string with:

    +The browser's built-in string [replace()](/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace) function however needs two parameters — the substring to find in the main string, and the substring to replace that string with: -
    var myText = 'I am a string';
    -var newString = myText.replace('string', 'sausage');
    +```js +var myText = 'I am a string'; +var newString = myText.replace('string', 'sausage'); +``` -
    -

    備註: When you need to specify multiple parameters, they are separated by commas.

    -
    +> **備註:** When you need to specify multiple parameters, they are separated by commas. -

    It should also be noted that sometimes parameters are optional — you don't have to specify them. If you don't, the function will generally adopt some kind of default behavior. As an example, the array join() function's parameter is optional:

    +It should also be noted that sometimes parameters are optional — you don't have to specify them. If you don't, the function will generally adopt some kind of default behavior. As an example, the array [join()](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join) function's parameter is optional: -
    var myArray = ['I', 'love', 'chocolate', 'frogs'];
    +```js
    +var myArray = ['I', 'love', 'chocolate', 'frogs'];
     var madeAString = myArray.join(' ');
     // returns 'I love chocolate frogs'
     var madeAString = myArray.join();
    -// returns 'I,love,chocolate,frogs'
    +// returns 'I,love,chocolate,frogs' +``` -

    If no parameter is included to specify a joining/delimiting character, a comma is used by default.

    +If no parameter is included to specify a joining/delimiting character, a comma is used by default. -

    Function scope and conflicts

    +## Function scope and conflicts -

    Let's talk a bit about {{glossary("scope")}} — a very important concept when dealing with functions. When you create a function, the variables and other things defined inside the function are inside their own separate scope, meaning that they are locked away in their own separate compartments, unreachable from inside other functions or from code outside the functions.

    +Let's talk a bit about {{glossary("scope")}} — a very important concept when dealing with functions. When you create a function, the variables and other things defined inside the function are inside their own separate **scope**, meaning that they are locked away in their own separate compartments, unreachable from inside other functions or from code outside the functions. -

    The top level outside all your functions is called the global scope. Values defined in the global scope are accessible from everywhere in the code.

    +The top level outside all your functions is called the **global scope**. Values defined in the global scope are accessible from everywhere in the code. -

    JavaScript is set up like this for various reasons — but mainly because of security and organization. Sometimes you don't want variables to be accessible from everywhere in the code — external scripts that you call in from elsewhere could start to mess with your code and cause problems because they happen to be using the same variable names as other parts of the code, causing conflicts. This might be done maliciously, or just by accident.

    +JavaScript is set up like this for various reasons — but mainly because of security and organization. Sometimes you don't want variables to be accessible from everywhere in the code — external scripts that you call in from elsewhere could start to mess with your code and cause problems because they happen to be using the same variable names as other parts of the code, causing conflicts. This might be done maliciously, or just by accident. -

    For example, say you have an HTML file that is calling in two external JavaScript files, and both of them have a variable and a function defined that use the same name:

    +For example, say you have an HTML file that is calling in two external JavaScript files, and both of them have a variable and a function defined that use the same name: -
    <!-- Excerpt from my HTML -->
    -<script src="first.js"></script>
    -<script src="second.js"></script>
    -<script>
    +```html
    +
    +
    +
    +
    +```
     
    -
    // first.js
    +```js
    +// first.js
     var name = 'Chris';
     function greeting() {
       alert('Hello ' + name + ': welcome to our company.');
    -}
    +} +``` -
    // second.js
    +```js
    +// second.js
     var name = 'Zaptec';
     function greeting() {
       alert('Our company is called ' + name + '.');
    -}
    +} +``` -

    Both functions you want to call are called greeting(), but you can only ever access the second.js file's greeting() function — it is applied to the HTML later on in the source code, so its variable and function overwrite the ones in first.js.

    +Both functions you want to call are called `greeting()`, but you can only ever access the `second.js` file's `greeting()` function — it is applied to the HTML later on in the source code, so its variable and function overwrite the ones in `first.js`. -
    -

    備註: You can see this example running live on GitHub (see also the source code).

    -
    +> **備註:** You can see this example [running live on GitHub](http://mdn.github.io/learning-area/javascript/building-blocks/functions/conflict.html) (see also the [source code](https://github.com/mdn/learning-area/tree/master/javascript/building-blocks/functions)). -

    Keeping parts of your code locked away in functions avoids such problems, and is considered best practice.

    +Keeping parts of your code locked away in functions avoids such problems, and is considered best practice. -

    It is a bit like a zoo. The lions, zebras, tigers, and penguins are kept in their own enclosures, and only have access to the things inside their enclosures — in the same manner as the function scopes. If they were able to get into other enclosures, problems would occur. At best, different animals would feel really uncomfortable inside unfamiliar habitats — a lion or tiger would feel terrible inside the penguins' watery, icy domain. At worst, the lions and tigers might try to eat the penguins!

    +It is a bit like a zoo. The lions, zebras, tigers, and penguins are kept in their own enclosures, and only have access to the things inside their enclosures — in the same manner as the function scopes. If they were able to get into other enclosures, problems would occur. At best, different animals would feel really uncomfortable inside unfamiliar habitats — a lion or tiger would feel terrible inside the penguins' watery, icy domain. At worst, the lions and tigers might try to eat the penguins! -

    +![](mdn-mozilla-zoo.png) -

    The zoo keeper is like the global scope — he or she has the keys to access every enclosure, to restock food, tend to sick animals, etc.

    +The zoo keeper is like the global scope — he or she has the keys to access every enclosure, to restock food, tend to sick animals, etc. -

    Active learning: Playing with scope

    +### Active learning: Playing with scope -

    Let's look at a real example to demonstrate scoping.

    +Let's look at a real example to demonstrate scoping. -
      -
    1. First, make a local copy of our function-scope.html example. This contains two functions called a() and b(), and three variables — x, y, and z — two of which are defined inside the functions, and one in the global scope. It also contains a third function called output(), which takes a single parameter and outputs it in a paragraph on the page.
    2. -
    3. Open the example up in a browser and in your text editor.
    4. -
    5. Open the JavaScript console in your browser developer tools. In the JavaScript console, enter the following command: -
      output(x);
      - You should see the value of variable x output to the screen.
    6. -
    7. Now try entering the following in your console -
      output(y);
      -output(z);
      - Both of these should return an error along the lines of "ReferenceError: y is not defined". Why is that? Because of function scope — y and z are locked inside the a() and b() functions, so output() can't access them when called from the global scope.
    8. -
    9. However, what about when it's called from inside another function? Try editing a() and b() so they look like this: -
      function a() {
      -  var y = 2;
      -  output(y);
      -}
      +1.  First, make a local copy of our [function-scope.html](https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/functions/function-scope.html) example. This contains two functions called `a()` and `b()`, and three variables — `x`, `y`, and `z` — two of which are defined inside the functions, and one in the global scope. It also contains a third function called `output()`, which takes a single parameter and outputs it in a paragraph on the page.
      +2.  Open the example up in a browser and in your text editor.
      +3.  Open the JavaScript console in your browser developer tools. In the JavaScript console, enter the following command:
       
      -function b() {
      -  var z = 3;
      -  output(z);
      -}
      - Save the code and reload it in your browser, then try calling the a() and b() functions from the JavaScript console: - -
      a();
      -b();
      - You should see the y and z values output in the page. This works fine, as the output() function is being called inside the other functions — in the same scope as the variables it is printing are defined in, in each case. output() itself is available from anywhere, as it is defined in the global scope.
    10. -
    11. Now try updating your code like this: -
      function a() {
      -  var y = 2;
      -  output(x);
      -}
      +    ```js
      +    output(x);
      +    ```
       
      -function b() {
      -  var z = 3;
      -  output(x);
      -}
      - Save and reload again, and try this again in your JavaScript console: - -
      a();
      -b();
      - Both the a() and b() call should output the value of x — 1. These work fine because even though the output() calls are not in the same scope as x is defined in, x is a global variable so is available inside all code, everywhere.
    12. -
    13. Finally, try updating your code like this: -
      function a() {
      -  var y = 2;
      -  output(z);
      -}
      +    You should see the value of variable `x` output to the screen.
      +
      +4.  Now try entering the following in your console
      +
      +    ```js
      +    output(y);
      +    output(z);
      +    ```
       
      -function b() {
      -  var z = 3;
      -  output(y);
      -}
      - Save and reload again, and try this again in your JavaScript console: + Both of these should return an error along the lines of "[ReferenceError: y is not defined](/en-US/docs/Web/JavaScript/Reference/Errors/Not_defined)". Why is that? Because of function scope — `y` and `z` are locked inside the `a()` and `b()` functions, so `output()` can't access them when called from the global scope. -
      a();
      -b();
      - This time the a() and b() calls will both return that annoying "ReferenceError: z is not defined" error — this is because the output() calls and the variables they are trying to print are not defined inside the same function scopes — the variables are effectively invisible to those function calls.
    14. -
    +5. However, what about when it's called from inside another function? Try editing `a()` and `b()` so they look like this: -
    -

    備註: The same scoping rules do not apply to loop (e.g. for() { ... }) and conditional blocks (e.g. if() { ... }) — they look very similar, but they are not the same thing! Take care not to get these confused.

    -
    + ```js + function a() { + var y = 2; + output(y); + } -
    -

    備註: The ReferenceError: "x" is not defined error is one of the most common you'll encounter. If you get this error and you are sure that you have defined the variable in question, check what scope it is in.

    -
    + function b() { + var z = 3; + output(z); + } + ``` -

    Functions inside functions

    + Save the code and reload it in your browser, then try calling the `a()` and `b()` functions from the JavaScript console: -

    Keep in mind that you can call a function from anywhere, even inside another function. This is often used as a way to keep code tidy — if you have a big complex function, it is easier to understand if you break it down into several sub-functions:

    + ```js + a(); + b(); + ``` -
    function myBigFunction() {
    +    You should see the `y` and `z` values output in the page. This works fine, as the `output()` function is being called inside the other functions — in the same scope as the variables it is printing are defined in, in each case. `output()` itself is available from anywhere, as it is defined in the global scope.
    +
    +6.  Now try updating your code like this:
    +
    +    ```js
    +    function a() {
    +      var y = 2;
    +      output(x);
    +    }
    +
    +    function b() {
    +      var z = 3;
    +      output(x);
    +    }
    +    ```
    +
    +    Save and reload again, and try this again in your JavaScript console:
    +
    +    ```js
    +    a();
    +    b();
    +    ```
    +
    +    Both the `a()` and `b()` call should output the value of x — 1. These work fine because even though the `output()` calls are not in the same scope as `x` is defined in, `x` is a global variable so is available inside all code, everywhere.
    +
    +7.  Finally, try updating your code like this:
    +
    +    ```js
    +    function a() {
    +      var y = 2;
    +      output(z);
    +    }
    +
    +    function b() {
    +      var z = 3;
    +      output(y);
    +    }
    +    ```
    +
    +    Save and reload again, and try this again in your JavaScript console:
    +
    +    ```js
    +    a();
    +    b();
    +    ```
    +
    +    This time the `a()` and `b()` calls will both return that annoying "[ReferenceError: z is not defined](/en-US/docs/Web/JavaScript/Reference/Errors/Not_defined)" error — this is because the `output()` calls and the variables they are trying to print are not defined inside the same function scopes — the variables are effectively invisible to those function calls.
    +
    +> **備註:** The same scoping rules do not apply to loop (e.g. `for() { ... }`) and conditional blocks (e.g. `if() { ... }`) — they look very similar, but they are not the same thing! Take care not to get these confused.
    +
    +> **備註:** The [ReferenceError: "x" is not defined](/en-US/docs/Web/JavaScript/Reference/Errors/Not_defined) error is one of the most common you'll encounter. If you get this error and you are sure that you have defined the variable in question, check what scope it is in.
    +
    +### Functions inside functions
    +
    +Keep in mind that you can call a function from anywhere, even inside another function. This is often used as a way to keep code tidy — if you have a big complex function, it is easier to understand if you break it down into several sub-functions:
    +
    +```js
    +function myBigFunction() {
       var myValue;
     
       subFunction1();
    @@ -335,11 +404,12 @@ function subFunction2() {
     function subFunction3() {
       console.log(myValue);
     }
    -
    +``` -

    Just make sure that the values being used inside the function are properly in scope. The example above would throw an error ReferenceError: myValue is not defined, because although the myValue variable is defined in the same scope as the function calls, it is not defined inside the function definitions — the actual code that is run when the functions are called. To make this work, you'd have to pass the value into the function as a parameter, like this:

    +Just make sure that the values being used inside the function are properly in scope. The example above would throw an error `ReferenceError: myValue is not defined`, because although the `myValue` variable is defined in the same scope as the function calls, it is not defined inside the function definitions — the actual code that is run when the functions are called. To make this work, you'd have to pass the value into the function as a parameter, like this: -
    function myBigFunction() {
    +```js
    +function myBigFunction() {
       var myValue = 1;
     
       subFunction1(myValue);
    @@ -357,32 +427,27 @@ function subFunction2(value) {
     
     function subFunction3(value) {
       console.log(value);
    -}
    - -

    Conclusion

    +} +``` -

    This article has explored the fundamental concepts behind functions, paving the way for the next one in which we get practical and take you through the steps to building up your own custom function.

    +## Conclusion -

    See also

    +This article has explored the fundamental concepts behind functions, paving the way for the next one in which we get practical and take you through the steps to building up your own custom function. - +## See also -

    {{PreviousMenuNext("Learn/JavaScript/Building_blocks/Looping_code","Learn/JavaScript/Building_blocks/Build_your_own_function", "Learn/JavaScript/Building_blocks")}}

    +- [Functions detailed guide](/en-US/docs/Web/JavaScript/Guide/Functions) — covers some advanced features not included here. +- [Functions reference](/en-US/docs/Web/JavaScript/Reference/Functions) +- [Default parameters](/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters), [Arrow functions](/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) — advanced concept references -

    +{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Looping_code","Learn/JavaScript/Building_blocks/Build_your_own_function", "Learn/JavaScript/Building_blocks")}} -

    In this module

    +## In this module - +- [Making decisions in your code — conditionals](/en-US/docs/Learn/JavaScript/Building_blocks/conditionals) +- [Looping code](/en-US/docs/Learn/JavaScript/Building_blocks/Looping_code) +- [Functions — reusable blocks of code](/en-US/docs/Learn/JavaScript/Building_blocks/Functions) +- [Build your own function](/en-US/docs/Learn/JavaScript/Building_blocks/Build_your_own_function) +- [Function return values](/en-US/docs/Learn/JavaScript/Building_blocks/Return_values) +- [Introduction to events](/en-US/docs/Learn/JavaScript/Building_blocks/Events) +- [Image gallery](/en-US/docs/Learn/JavaScript/Building_blocks/Image_gallery) diff --git a/files/zh-tw/learn/javascript/building_blocks/image_gallery/index.md b/files/zh-tw/learn/javascript/building_blocks/image_gallery/index.md index 2522d72f61dc43..3b98f1d00b6141 100644 --- a/files/zh-tw/learn/javascript/building_blocks/image_gallery/index.md +++ b/files/zh-tw/learn/javascript/building_blocks/image_gallery/index.md @@ -14,119 +14,107 @@ tags: - 迴圈 translation_of: Learn/JavaScript/Building_blocks/Image_gallery --- -
    {{LearnSidebar}}
    +{{LearnSidebar}}{{PreviousMenu("Learn/JavaScript/Building_blocks/Events", "Learn/JavaScript/Building_blocks")}} -
    {{PreviousMenu("Learn/JavaScript/Building_blocks/Events", "Learn/JavaScript/Building_blocks")}}
    - -

    現在我們已經看過了基本的JavaScript組建,我們將讓你做一個測試,從建立一個在很多網站上常見的事物 — JavaScript基礎的影像圖庫,來測試你對迴圈、函數、條件式及事件的知識。

    +現在我們已經看過了基本的 JavaScript 組建,我們將讓你做一個測試,從建立一個在很多網站上常見的事物 — JavaScript 基礎的影像圖庫,來測試你對迴圈、函數、條件式及事件的知識。 - - - - - - - - - - + + + + + + + + + +
    先修課程:在進行這個評量前,你應已閱讀、練習了本模組中所有的文章。
    目的:測試對JavaScript中迴圈、函數、條件式及事件的瞭解程度。
    先修課程:在進行這個評量前,你應已閱讀、練習了本模組中所有的文章。
    目的:測試對JavaScript中迴圈、函數、條件式及事件的瞭解程度。
    -

    從這裡開始

    +## 從這裡開始 -

    要進行這個評量,你要先下載 grab the ZIP 檔案,解壓縮在你電腦中的某個檔案夾作為範例。

    +要進行這個評量,你要先下載 [grab the ZIP](https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/gallery/gallery-start.zip?raw=true) 檔案,解壓縮在你電腦中的某個檔案夾作為範例。 -
    -

    備註: 你也可以在某些網站進行評鑑,如 JSBinThimble。你可以把這些HTML、CSS和JavaScript貼到這些線上編輯器中。如果你用了一個沒法把JavaScript/CSS分別放在不同面板的線上編輯器,你可以放心的把這些<script>/<style>元件改成inline貼進HTML網頁裡。

    -
    +> **備註:** 你也可以在某些網站進行評鑑,如 [JSBin](http://jsbin.com/) 或[Thimble](https://thimble.mozilla.org/)。你可以把這些 HTML、CSS 和 JavaScript 貼到這些線上編輯器中。如果你用了一個沒法把 JavaScript/CSS 分別放在不同面板的線上編輯器,你可以放心的把這些` - </body> -</html>
    - + + +``` -

    {{ EmbedLiveSample('不用麻煩', '100%', 400) }}

    +{{ EmbedLiveSample('不用麻煩', '100%', 400) }} -

    您現在不必了解所有代碼,但讓我們看一下實際繪製100個圓圈的代碼部分:

    +您現在不必了解所有代碼,但讓我們看一下實際繪製 100 個圓圈的代碼部分: -
    for (var i = 0; i < 100; i++) {
    +```js
    +for (var i = 0; i < 100; i++) {
       ctx.beginPath();
       ctx.fillStyle = 'rgba(255,0,0,0.5)';
       ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
       ctx.fill();
    -}
    +} +``` -
      -
    • 代碼前面定義的random(),返回0到x-1之間的整數。
    • -
    • WIDTH和HEIGHT是內部瀏覽器窗口的寬度和高度。
    • -
    +- `代碼前面定義的random(),返回0到x-1之間的整數。` +- `WIDTH和HEIGHT是內部瀏覽器窗口的寬度和高度。` -

    您應該了解基本概念-我們正在使用一個循環來運行此代碼的100次迭代,每個迭代在頁面上的隨機位置繪製一個圓圈。 無論我們繪製100個圓,1000個還是10,000個,所需的代碼量都是相同的。 只需更改一個數字。

    +您應該了解基本概念-我們正在使用一個循環來運行此代碼的 100 次迭代,每個迭代在頁面上的隨機位置繪製一個圓圈。 無論我們繪製 100 個圓,1000 個還是 10,000 個,所需的代碼量都是相同的。 只需更改一個數字。 -

    如果我們不在此處使用循環,則必須為每個要繪製的圓重複以下代碼:

    +如果我們不在此處使用循環,則必須為每個要繪製的圓重複以下代碼: -
    ctx.beginPath();
    +```js
    +ctx.beginPath();
     ctx.fillStyle = 'rgba(255,0,0,0.5)';
     ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
    -ctx.fill();
    +ctx.fill(); +``` -

    這將變得很無聊,並且很難很快維護。 循環確實是最好的。

    +這將變得很無聊,並且很難很快維護。 循環確實是最好的。 -

    循環的規範

    +## 循環的規範 -

    讓我們開始探索一些特定的循環結構。 第一個是for循環,您將在大多數時候使用它,它具有以下語法:

    +讓我們開始探索一些特定的循環結構。 第一個是 for 循環,您將在大多數時候使用它,它具有以下語法: -
    for (initializer; exit-condition; final-expression) {
    +```js
    +for (initializer; exit-condition; final-expression) {
       // code to run
    -}
    +} +``` + +這裡我們有: + +1. 關鍵字“ for”,即跟隨其後的一些括號。 +2. 在括號內,我們有三個項目,以 ; 分隔: -

    這裡我們有:

    + 1. 初始化程序-通常是一個設置為數字的變量,該變量將遞增以計算循環運行的次數。 有時也稱為計數器變量。 + 2. 退出條件-如前所述,它定義了循環何時應停止循環。 通常,這是一個具有比較運算符的表達式,該測試用於檢驗是否滿足退出條件。 + 3. 最終表達式—每當循環經過完整的迭代時,總是對它進行評估(或運行)。 它通常用於遞增(或在某些情況下遞減)計數器變量,以使其更接近退出條件值。 -
      -
    1. 關鍵字“ for”,即跟隨其後的一些括號。
    2. -
    3. 在括號內,我們有三個項目,以 ; 分隔: -
        -
      1. 初始化程序-通常是一個設置為數字的變量,該變量將遞增以計算循環運行的次數。 有時也稱為計數器變量。
      2. -
      3. 退出條件-如前所述,它定義了循環何時應停止循環。 通常,這是一個具有比較運算符的表達式,該測試用於檢驗是否滿足退出條件。
      4. -
      5. 最終表達式—每當循環經過完整的迭代時,總是對它進行評估(或運行)。 它通常用於遞增(或在某些情況下遞減)計數器變量,以使其更接近退出條件值。
      6. -
      -
    4. -
    5. 一些花括號包含一個代碼塊-每次循環迭代時都將運行此代碼。
    6. -
    +3. 一些花括號包含一個代碼塊-每次循環迭代時都將運行此代碼。 -

    讓我們看一個真實的例子,以便我們可以更清楚地看到它們的作用。

    +讓我們看一個真實的例子,以便我們可以更清楚地看到它們的作用。 -
    var cats = ['Bill', 'Jeff', 'Pete', 'Biggles', 'Jasmin'];
    +```js
    +var cats = ['Bill', 'Jeff', 'Pete', 'Biggles', 'Jasmin'];
     var info = 'My cats are called ';
     var para = document.querySelector('p');
     
    -for (var i = 0; i < cats.length; i++) {
    +for (var i = 0; i < cats.length; i++) {
       info += cats[i] + ', ';
     }
     
    -para.textContent = info;
    +para.textContent = info; +``` -

    這為我們提供了以下輸出:

    +這為我們提供了以下輸出: - + + +``` -

    {{ EmbedLiveSample('不用麻煩', '100%', 60, "", "", "hide-codepen-jsfiddle") }}

    +{{ EmbedLiveSample('不用麻煩', '100%', 60, "", "", "hide-codepen-jsfiddle") }} -
    -

    備註: You can find this example code on GitHub too (also see it running live).

    -
    +> **備註:** You can find this [example code on GitHub](https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/loops/basic-for.html) too (also [see it running live](http://mdn.github.io/learning-area/javascript/building-blocks/loops/basic-for.html)). -

    這顯示了一個循環,該循環用於遍歷數組中的項目並對其進行處理-這是JavaScript中非常常見的模式。 這裡:

    - -
      -
    1. 迭代器i從0開始(變量i = 0)。
    2. -
    3. 它被告知運行,直到它不再小於cats數組的長度為止。 這很重要,退出條件顯示了循環仍將運行的條件。 因此,在這種情況下,儘管i <cats.length仍然為true,循環仍將運行。
    4. -
    5. 在循環內部,我們將當前循環項(cats [i]是cats [無論 i 當時是什麼])與一個逗號和一個空格連接到info變量的末尾。 所以: -
        -
      1. 在第一次運行中,i = 0,因此cats [0] +','將連接到info(“ Bill,”)上。
      2. -
      3. 在第二次運行中,i = 1,因此cats [1] +','將連接到info(“ Jeff,”)上
      4. -
      5. 等等。 每次循環運行後,將1加到i(i ++),然後該過程將再次開始。
      6. -
      -
    6. -
    7. 當 i 等於cats.length時,循環將停止,瀏覽器將繼續循環下方的下一段代碼。
    8. -
    - -
    -

    備註: We have made the exit condition i < cats.length, not i <= cats.length, because computers count from 0, not 1 — we are starting i at 0, and going up to i = 4 (the index of the last array item). cats.length returns 5, as there are 5 items in the array, but we don't want to get up to i = 5, as that would return undefined for the last item (there is no array item with an index of 5). So therefore we want to go up to 1 less than cats.length (i <), not the same as cats.length (i <=).

    -
    +這顯示了一個循環,該循環用於遍歷數組中的項目並對其進行處理-這是 JavaScript 中非常常見的模式。 這裡: -
    -

    備註: A common mistake with exit conditions is making them use "equal to" (===) rather than say "less than or equal to" (<=). If we wanted to run our loop up to i = 5, the exit condition would need to be i <= cats.length. If we set it to i === cats.length, the loop would not run at all because i is not equal to 5 on the first loop iteration, so it would stop immediately.

    -
    +1. 迭代器 i 從 0 開始(變量 i = 0)。 +2. 它被告知運行,直到它不再小於 cats 數組的長度為止。 這很重要,退出條件顯示了循環仍將運行的條件。 因此,在這種情況下,儘管 i \ **備註:** We have made the exit condition `i < cats.length`, not `i <= cats.length`, because computers count from 0, not 1 — we are starting `i` at `0`, and going up to `i = 4` (the index of the last array item). `cats.length` returns 5, as there are 5 items in the array, but we don't want to get up to `i = 5`, as that would return `undefined` for the last item (there is no array item with an index of 5). So therefore we want to go up to 1 less than `cats.length` (`i <`), not the same as `cats.length` (`i <=`). + +> **備註:** A common mistake with exit conditions is making them use "equal to" (`===`) rather than say "less than or equal to" (`<=`). If we wanted to run our loop up to `i = 5`, the exit condition would need to be `i <= cats.length`. If we set it to `i === cats.length`, the loop would not run at all because `i` is not equal to `5` on the first loop iteration, so it would stop immediately. -

    我們剩下的一個小問題是最終輸出語句的格式不太正確:

    +我們剩下的一個小問題是最終輸出語句的格式不太正確: -
    -

    My cats are called Bill, Jeff, Pete, Biggles, Jasmin,

    -
    +> My cats are called Bill, Jeff, Pete, Biggles, Jasmin, -

    理想情況下,我們希望在最終循環迭代中更改串聯,以使句子的末尾沒有逗號。 好吧,沒問題-我們可以很高興地在for循環中插入一個條件來處理這種特殊情況:

    +理想情況下,我們希望在最終循環迭代中更改串聯,以使句子的末尾沒有逗號。 好吧,沒問題-我們可以很高興地在 for 循環中插入一個條件來處理這種特殊情況: -
    for (var i = 0; i < cats.length; i++) {
    +```js
    +for (var i = 0; i < cats.length; i++) {
       if (i === cats.length - 1) {
         info += 'and ' + cats[i] + '.';
       } else {
         info += cats[i] + ', ';
       }
    -}
    +} +``` -
    -

    備註: You can find this example code on GitHub too (also see it running live).

    -
    +> **備註:** You can find this [example code on GitHub](https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/loops/basic-for-improved.html) too (also [see it running live](http://mdn.github.io/learning-area/javascript/building-blocks/loops/basic-for-improved.html)). -
    -

    警告: With for — as with all loops — you must make sure that the initializer is iterated so that it eventually reaches the exit condition. If not, the loop will go on forever, and either the browser will force it to stop, or it will crash. This is called an infinite loop.

    -
    +> **警告:** With for — as with all loops — you must make sure that the initializer is iterated so that it eventually reaches the exit condition. If not, the loop will go on forever, and either the browser will force it to stop, or it will crash. This is called an **infinite loop**. -

    中斷退出循環

    +## 中斷退出循環 -

    如果要在所有迭代完成之前退出循環,可以使用break語句。 在查看switch語句時,我們已經在上一篇文章中遇到了這一問題—當在switch語句中遇到與輸入表達式匹配的case時,break語句立即退出switch語句並移至其後的代碼上。

    +如果要在所有迭代完成之前退出循環,可以使用 break 語句。 在查看 switch 語句時,我們已經在上一篇文章中遇到了這一問題—當在 switch 語句中遇到與輸入表達式匹配的 case 時,break 語句立即退出 switch 語句並移至其後的代碼上。 -

    循環也是如此,-break語句將立即退出循環,並使瀏覽器繼續執行緊隨其後的任何代碼。

    +循環也是如此,-break 語句將立即退出循環,並使瀏覽器繼續執行緊隨其後的任何代碼。 -

    假設我們要搜索一系列聯繫人和電話號碼,然後僅返回我們要查找的號碼? 首先,提供一些簡單的HTML-文本 {{htmlelement("input")}} 允許我們輸入要搜索的名稱,{{htmlelement("button")}} 元素以提交搜索,以及 {{htmlelement ("p")}} 元素以在以下位置顯示結果:

    +假設我們要搜索一系列聯繫人和電話號碼,然後僅返回我們要查找的號碼? 首先,提供一些簡單的 HTML-文本 {{htmlelement("input")}} 允許我們輸入要搜索的名稱,{{htmlelement("button")}} 元素以提交搜索,以及 {{htmlelement ("p")}} 元素以在以下位置顯示結果: -
    <label for="search">Search by contact name: </label>
    -<input id="search" type="text">
    -<button>Search</button>
    +```html
    +
    +
    +
     
    -<p></p>
    +

    +``` -

    Now on to the JavaScript:

    +Now on to the JavaScript: -
    var contacts = ['Chris:2232322', 'Sarah:3453456', 'Bill:7654322', 'Mary:9998769', 'Dianne:9384975'];
    +```js
    +var contacts = ['Chris:2232322', 'Sarah:3453456', 'Bill:7654322', 'Mary:9998769', 'Dianne:9384975'];
     var para = document.querySelector('p');
     var input = document.querySelector('input');
     var btn = document.querySelector('button');
    @@ -299,7 +297,7 @@ btn.addEventListener('click', function() {
       var searchName = input.value;
       input.value = '';
       input.focus();
    -  for (var i = 0; i < contacts.length; i++) {
    +  for (var i = 0; i < contacts.length; i++) {
         var splitContact = contacts[i].split(':');
         if (splitContact[0] === searchName) {
           para.textContent = splitContact[0] + '\'s number is ' + splitContact[1] + '.';
    @@ -308,27 +306,29 @@ btn.addEventListener('click', function() {
           para.textContent = 'Contact not found.';
         }
       }
    -});
    +}); +``` -