aboutsummaryrefslogtreecommitdiff
path: root/files/zh-tw/web/javascript/a_re-introduction_to_javascript
diff options
context:
space:
mode:
authorPeter Bengtsson <mail@peterbe.com>2020-12-08 14:43:23 -0500
committerPeter Bengtsson <mail@peterbe.com>2020-12-08 14:43:23 -0500
commit218934fa2ed1c702a6d3923d2aa2cc6b43c48684 (patch)
treea9ef8ac1e1b8fe4207b6d64d3841bfb8990b6fd0 /files/zh-tw/web/javascript/a_re-introduction_to_javascript
parent074785cea106179cb3305637055ab0a009ca74f2 (diff)
downloadtranslated-content-218934fa2ed1c702a6d3923d2aa2cc6b43c48684.tar.gz
translated-content-218934fa2ed1c702a6d3923d2aa2cc6b43c48684.tar.bz2
translated-content-218934fa2ed1c702a6d3923d2aa2cc6b43c48684.zip
initial commit
Diffstat (limited to 'files/zh-tw/web/javascript/a_re-introduction_to_javascript')
-rw-r--r--files/zh-tw/web/javascript/a_re-introduction_to_javascript/index.html910
1 files changed, 910 insertions, 0 deletions
diff --git a/files/zh-tw/web/javascript/a_re-introduction_to_javascript/index.html b/files/zh-tw/web/javascript/a_re-introduction_to_javascript/index.html
new file mode 100644
index 0000000000..97ff027312
--- /dev/null
+++ b/files/zh-tw/web/javascript/a_re-introduction_to_javascript/index.html
@@ -0,0 +1,910 @@
+---
+title: 重新介紹 JavaScript
+slug: Web/JavaScript/A_re-introduction_to_JavaScript
+tags:
+ - JavaScript
+ - 待翻譯
+ - 所有類別
+translation_of: Web/JavaScript/A_re-introduction_to_JavaScript
+---
+<p>{{jsSidebar}}</p>
+
+<h2 id="介紹">介紹</h2>
+
+<p>為何需要重新介紹?因為 <a href="/zh_tw/JavaScript" title="zh_tw/JavaScript">JavaScript</a> 堪稱是<a class="external" href="http://javascript.crockford.com/javascript.html">全世界最被人誤解的程式語言</a>。儘管 JavaScript 再怎麼的被嘲諷為小兒科,在它誤導人的簡潔下隱藏著強大的語言功能。2005 年是個許多知名 JavaScript 應用程式推出的年度,在在證明:更加瞭解這項科技對任何網頁開發者來說皆是重要的技能。</p>
+
+<p>先從該語言的歷史說起。1995 年,Brendan Eich,一位 Netscape (網景)的工程師,創造了 JavaScript。1996 年初,JavaScript 隨著 Netscape 2 首次推出。它原本要被命名為 LiveScript,結果因為行銷策略為了強調昇陽的 Java 程式語言的普遍性,而不幸的被改名 — 即便兩者之間沒有太大的關係。從此之後,這便成為了混淆的元兇。</p>
+
+<p>Microsoft 在幾個月後隨著 IE 3 推出了跟該語言大致上相容的 JScript。Netscape 在 1997 年將該語言送交 <a class="external" href="http://www.ecma-international.org/">ECMA International</a>,一個歐洲標準化組織,而在 1997 年的時候產生了初版的 <a href="/zh_tw/ECMAScript" title="zh_tw/ECMAScript">ECMAScript</a>。該標準在 1999 年的時候以 <a class="external" href="http://www.ecma-international.org/publications/standards/Ecma-262.htm">ECMAScript 第三版</a>的形式推出了更新,從此之後大致上都相當穩定,不過近期有在研發第四版。</p>
+
+<p>這個穩定性對開發者來說是相當好的事情,因為它讓不少實作 (implementation) 有時間慢慢趕上。我會把重點放在第三版的語法。為了避免混淆,我會繼續使用 JavaScript 這個名稱。</p>
+
+<p>與其他程式語言大大不同的是,JavaScript 沒有任何輸入或輸出的觀念。它是被設計成在一個宿主 (host) 環境下執行的腳本 (script) 語言,所以任何與外界通訊的方式,都是宿主環境的責任。瀏覽器是最常見的宿主環境,不過有些程式也有 JavaScript 解釋器,如 Adobe Acrobat、Photoshop、以及 Yahoo! Widget Engine 等等。</p>
+
+<h3 id=".E6.A6.82.E8.A6.81" name=".E6.A6.82.E8.A6.81">概要</h3>
+
+<p>先從任何語言最基本的方面講起:型態 (type)。JavaScript 程式可以改變「值」(value),而這些值各自有其歸屬的型態。JavaScript 的型態有:</p>
+
+<ul>
+ <li><a href="/zh_tw/Core_JavaScript_1.5_Reference/Global_Objects/Number" title="zh_tw/Core_JavaScript_1.5_Reference/Global_Objects/Number">Number</a> (數字)</li>
+ <li><a href="/zh_tw/Core_JavaScript_1.5_Reference/Global_Objects/String" title="zh_tw/Core_JavaScript_1.5_Reference/Global_Objects/String">String</a> (字串)</li>
+ <li><a href="/zh_tw/Core_JavaScript_1.5_Reference/Global_Objects/Boolean" title="zh_tw/Core_JavaScript_1.5_Reference/Global_Objects/Boolean">Boolean</a> (布林值)</li>
+ <li><a href="/zh_tw/Core_JavaScript_1.5_Reference/Global_Objects/Function" title="zh_tw/Core_JavaScript_1.5_Reference/Global_Objects/Function">Function</a> (函式)</li>
+ <li><a href="/zh_tw/Core_JavaScript_1.5_Reference/Global_Objects/Object" title="zh_tw/Core_JavaScript_1.5_Reference/Global_Objects/Object">Object</a> (物件)</li>
+</ul>
+
+<p>...以及稍微怪一點的 undefined (未定義)和 null (空)。還有,算是一種特殊物件的 <a href="/zh_tw/Core_JavaScript_1.5_Reference/Global_Objects/Array" title="zh_tw/Core_JavaScript_1.5_Reference/Global_Objects/Array">Array</a> (陣列)。還有,額外的特殊物件 <a href="/zh_tw/Core_JavaScript_1.5_Reference/Global_Objects/Date" title="zh_tw/Core_JavaScript_1.5_Reference/Global_Objects/Date">Date</a> (日期)以及 <a href="/zh_tw/Core_JavaScript_1.5_Reference/Global_Objects/RegExp" title="zh_tw/Core_JavaScript_1.5_Reference/Global_Objects/RegExp">Regular Expression</a>。另外,如果真的要達到技術上的準確性,連函式也只算是一種特殊的物件。所以型態分佈表看起來應該像這樣:</p>
+
+<ul>
+ <li>Number (數字)</li>
+ <li>String (字串)</li>
+ <li>Boolean (布林)</li>
+ <li>Object (物件)
+ <ul>
+ <li>Function (函式)</li>
+ <li>Array (陣列)</li>
+ <li>Date (日期)</li>
+ <li>RegExp</li>
+ </ul>
+ </li>
+ <li>Null (空)</li>
+ <li>Undefined (未定義)</li>
+</ul>
+
+<p>其實也有內建的 <a href="/zh_tw/Core_JavaScript_1.5_Reference/Global_Objects/Error" title="zh_tw/Core_JavaScript_1.5_Reference/Global_Objects/Error">Error</a> (錯誤)類型,不過,先把重點放在上面的分佈表比較容易。</p>
+
+<h3 id=".E6.95.B8.E5.AD.97" name=".E6.95.B8.E5.AD.97">數字</h3>
+
+<p>根據規格,JavaScript 數字算是「雙精確度 64 位元格式 IEEE 754 值」("double-precision 64-bit format IEEE 754 values")。這能造成一些有趣的後果。JavaScript 沒有所謂的整數,所以你在做算術的時候得小心一點,尤其是假如你習慣了 C 或 Java 的數學。小心類似下的的事情:</p>
+
+<pre class="brush: js">0.1 + 0.2 = 0.30000000000000004
+</pre>
+
+<p>JavaScript 支援標準的<a href="/zh_tw/Core_JavaScript_1.5_Reference/Operators/Arithmetic_Operators" title="zh_tw/Core_JavaScript_1.5_Reference/Operators/Arithmetic_Operators">數字運算子</a>,包含加法、減法、取餘數、等算術。另外還有一個之前忘記提的內建物件,<a href="/zh_tw/Core_JavaScript_1.5_Reference/Global_Objects/Math" title="zh_tw/Core_JavaScript_1.5_Reference/Global_Objects/Math">Math</a> (數學),用以處理較為進階的數學函數和常數:</p>
+
+<pre class="brush: js">Math.sin(3.5);
+d = Math.PI * r * r;
+</pre>
+
+<p>你可以用內建的 <code><a href="/zh_tw/Core_JavaScript_1.5_Reference/Global_Functions/parseInt" title="zh_tw/Core_JavaScript_1.5_Reference/Global_Functions/parseInt">parseInt()</a></code> 函式將字串轉成整數。這個函式有個可選用的第二個參數(在此建議你一定要指定),用以指定進位數。</p>
+
+<pre class="brush: js">&gt; parseInt("123", 10)
+123
+&gt; parseInt("010", 10)
+10
+</pre>
+
+<p>如果你不指定進位數,就有可能得到意想不到的結果:</p>
+
+<pre class="brush: js">&gt; parseInt("010")
+8
+</pre>
+
+<p>這是因為 <code>parseInt</code> 函數把字串當成八進位的數字,因為開頭有個 0。</p>
+
+<p>如果要把二進位的數字轉成整數,只要把進位數改掉就行了:</p>
+
+<pre class="brush: js">&gt; parseInt("11", 2)
+3
+</pre>
+
+<p>有個特殊的數字,叫做 <code><a href="/zh_tw/Core_JavaScript_1.5_Reference/Global_Properties/NaN" title="zh_tw/Core_JavaScript_1.5_Reference/Global_Properties/NaN">NaN</a></code> (「Not a Number」,「非數字」的簡稱),假如遞進去的字串不是數字,則會回傳 <code><a href="/zh_tw/Core_JavaScript_1.5_Reference/Global_Properties/NaN" title="zh_tw/Core_JavaScript_1.5_Reference/Global_Properties/NaN">NaN</a></code> :</p>
+
+<pre class="brush: js">&gt; parseInt("hello", 10)
+NaN
+</pre>
+
+<p><code>NaN</code> 很毒:將其進行任何數學運算,結果仍會是 <code>NaN</code>:</p>
+
+<pre class="brush: js">&gt; NaN + 5
+NaN
+</pre>
+
+<p>你可以用內建的 <code><a href="/zh_tw/Core_JavaScript_1.5_Reference/Global_Functions/isNaN" title="zh_tw/Core_JavaScript_1.5_Reference/Global_Functions/isNaN">isNaN()</a></code> 函式來判斷一個值是否為 <code>NaN</code>:</p>
+
+<pre class="brush: js">&gt; isNaN(NaN)
+true
+</pre>
+
+<p>JavaScript 也有特殊的值 <code><a href="/zh_tw/Core_JavaScript_1.5_Reference/Global_Properties/Infinity" title="zh_tw/Core_JavaScript_1.5_Reference/Global_Properties/Infinity">Infinity</a></code> (無限)以及 <code>-Infinity</code> (負無限):</p>
+
+<pre class="brush: js">&gt; 1 / 0
+Infinity
+&gt; -1 / 0
+-Infinity
+</pre>
+
+<h3 id=".E5.AD.97.E4.B8.B2" name=".E5.AD.97.E4.B8.B2">字串</h3>
+
+<p>JavaScript 的字串是一序列的字元。更精確的說,是一序列的 <a href="/zh_tw/Core_JavaScript_1.5_Guide/Unicode" title="zh_tw/Core_JavaScript_1.5_Guide/Unicode">Unicode 字元</a>,每個字元皆以一個 16 位元的數字作為代表。這讓任何人不需要為國際化感到擔心。</p>
+
+<p>如果你要代表一個單一字元,用長度為 1 的字串即可。</p>
+
+<p>要得知一個字串的長度,請存取該字串的 <code><a href="/zh_tw/Core_JavaScript_1.5_Reference/Global_Objects/String/length" title="zh_tw/Core_JavaScript_1.5_Reference/Global_Objects/String/length">length</a></code>(長度)屬性:</p>
+
+<pre class="brush: js">&gt; "hello".length
+5
+</pre>
+
+<p>剛剛可是與 JavaScript 物件的第一次接觸呢!字串也是物件喔。字串甚至也有<a href="/zh_tw/Core_JavaScript_1.5_Reference/Global_Objects/String#Methods" title="zh_tw/Core_JavaScript_1.5_Reference/Global_Objects/String#Methods">方法 (method)</a>:</p>
+
+<pre class="brush: js">&gt; "hello".charAt(0) //位置 0 的字元
+h
+&gt; "hello, world".replace("hello", "goodbye") //把 hello 換成 goodbye
+goodbye, world
+&gt; "hello".toUpperCase() //轉成大寫
+HELLO
+</pre>
+
+<h3 id=".E5.85.B6.E4.BB.96.E9.A1.9E.E5.9E.8B" name=".E5.85.B6.E4.BB.96.E9.A1.9E.E5.9E.8B">其他類型</h3>
+
+<p>JavaScript 對下列兩者是有分別的:<code>null</code> (空),屬於「object」型態的一種物件,用以明言表示無數值,以及 <code>undefined</code> (無定義),屬於「undefined」類型的一種物件,用以表示未初始化的值,也就是說,根本還沒指定數值。雖然姑且先不論變數,但在 JavaScript 你可以宣告一個變數但不指定其值。如果你這麼做的話,那該變數的型態便是 <code>undefined</code>。</p>
+
+<p>JavaScript 有布林 (boolean) 型態,可能的值有 <code>true</code> (真)與 <code>false</code> (假)且兩者皆為關鍵字。根據下列規則,任何值都可以被轉換成布林值:</p>
+
+<ol>
+ <li><code>false</code>、<code>0</code>、空字串 (<code>""</code>)、<code>NaN</code>、<code>null</code>、以及 <code>undefined</code> 都會成為 <code>false</code></li>
+ <li>所有其他的值都會成為 <code>true</code></li>
+</ol>
+
+<p>你可以用 <code>Boolean()</code> 函式特別進行轉換:</p>
+
+<pre class="brush: js">&gt; Boolean("")
+false
+&gt; Boolean(234)
+true
+</pre>
+
+<p>不過很少需要這樣,因為假如 JavaScript 遇到需要接收布林值的時候,如 <code>if</code> 陳述式(見下),便會無聲無息的進行布林轉換。有鑑於此,常常會有「真值」("true values") 與「假值」("false values") 等說法,意思是指值在布林轉換過程中會被轉成 <code>true</code> 或是 <code>false</code>。這種值也有被稱為「真的」("truthy") 或「假的」("falsy")。</p>
+
+<p>JavaScript 支援布林運算,如 <code>&amp;&amp;</code> (邏輯「<em>與</em>」,英稱 <em>and</em>)、<code>||</code> (邏輯「<em>或</em>」,英稱 <em>or</em>)、以及 <code>!</code> (邏輯「<em>非</em>」,英稱 <em>not</em>),請見下。</p>
+
+<h3 id=".E8.AE.8A.E6.95.B8" name=".E8.AE.8A.E6.95.B8">變數</h3>
+
+<p>在 JavaScript,要宣告新變數,使用的是 <code><a href="/zh_tw/Core_JavaScript_1.5_Reference/Statements/var" title="zh_tw/Core_JavaScript_1.5_Reference/Statements/var">var</a></code> 關鍵字:</p>
+
+<pre class="brush: js">var a;
+var name = "simon";
+</pre>
+
+<p>如果你宣告一個變數但不指定任何值,其型態便為 <code>undefined</code> (未定義)。 <span class="comment">應該注意關於 JS 的區塊不會構成新變數作用域</span></p>
+
+<h3 id=".E9.81.8B.E7.AE.97.E5.AD.90" name=".E9.81.8B.E7.AE.97.E5.AD.90">運算子</h3>
+
+<p>JavaScript 的數字運算子有 <code>+</code>、<code>-</code>、<code>*</code>、<code>/</code>、以及 <code>%</code> - 最後一個是取餘數的運算子(英稱 <em>mod</em>)。用來指定值的運算子是 <code>=</code>,另外還有複合指定陳述式,如 <code>+=</code> 以及 <code>-=</code>。這些是用以延伸 <code>x = x <em>運算子</em> y</code>。</p>
+
+<pre class="brush: js">x += 5
+x = x + 5
+</pre>
+
+<p>你可以用 <code>++</code> 和 <code>--</code> 來分別增加或是減少數值。這些運算子可以放在變數的開頭或結尾。</p>
+
+<p><a href="/zh_tw/Core_JavaScript_1.5_Reference/Operators/String_Operators" title="zh_tw/Core_JavaScript_1.5_Reference/Operators/String_Operators"><code>+</code> 運算子</a>也能把字串連接 (concatenate) 起來:</p>
+
+<pre class="brush: js">&gt; "hello" + " world"
+hello world
+</pre>
+
+<p>如果你把一字串加到一個數字(或其他數值),會先把所有的東西轉成字串。這會讓你意想不到:</p>
+
+<pre class="brush: js">&gt; "3" + 4 + 5
+345
+&gt; 3 + 4 + "5"
+75
+</pre>
+
+<p>把一個空字串加到一個東西是個將其轉成字串的好方法之一。</p>
+
+<p>JavaScript 中進行<a href="/zh_tw/Core_JavaScript_1.5_Reference/Operators/Comparison_Operators" title="zh_tw/Core_JavaScript_1.5_Reference/Operators/Comparison_Operators">比較</a>可以用 <code>&lt;</code>、<code>&gt;</code>、<code>&lt;=</code>、以及 <code>&gt;=</code>。這些對字串和數字都有用。等值比較 (equality) 比較沒那麼直接。雙等號運算子(等於)會進行型態強制轉換,假如比較的資料型態不一樣,有時結果會相當有趣:</p>
+
+<pre class="brush: js">&gt; "dog" == "dog"
+true
+&gt; 1 == true
+true
+</pre>
+
+<p>要避免型態強制轉換,要用三等號運算子(絕對等於):</p>
+
+<pre class="brush: js">&gt; 1 === true
+false
+&gt; true === true
+true
+</pre>
+
+<p>另外還有 <code>!=</code> (不等於)以及 <code>!==</code> (絕對不等於)運算子。</p>
+
+<p>假如你需要用的話,JavaScript 也有<a href="/zh_tw/Core_JavaScript_1.5_Reference/Operators/Bitwise_Operators" title="zh_tw/Core_JavaScript_1.5_Reference/Operators/Bitwise_Operators">逐位元 (bitwise) 運算子</a>。</p>
+
+<h3 id=".E6.8E.A7.E5.88.B6.E7.B5.90.E6.A7.8B" name=".E6.8E.A7.E5.88.B6.E7.B5.90.E6.A7.8B">控制結構</h3>
+
+<p>JavaScript 跟其他同屬 C 家族的程式語言有類似的控制結構。條件陳述式是靠 <code>if</code> 以及 <code>else</code> 來表示。如果你喜歡還可以串起來:</p>
+
+<pre class="brush: js">var name = "貓咪";
+if (name == "狗狗") {
+ name += "!";
+} else if (name == "貓咪") {
+ name += "!!";
+} else {
+ name = "!" + name;
+}
+name == "貓咪!!"
+</pre>
+
+<p>JavaScript 有 <code>while</code> 迴圈以及 <code>do-while</code> 迴圈。前者適合做基本的迴圈,而後者是當你要迴圈至少執行一次就需要用到:</p>
+
+<pre class="brush: js">while (true) {
+ // 無限迴圈!
+}
+
+do {
+ var input = get_input();
+} while (inputIsNotValid(input))
+</pre>
+
+<p>JavaScript 的 <code>for</code> 迴圈跟 C 和 Java 的一樣:可以讓你用一行就提供控制的條件與資訊。</p>
+
+<pre class="brush: js">for (var i = 0; i &lt; 5; i++) {
+ // 會執行 5 次
+}
+</pre>
+
+<p><code>&amp;&amp;</code> 以及 <code>||</code> 運算子用的是「短路邏輯」(short-circuit logic),也就是說,第二個運算值是否會被執行靠的是第一個運算值。這用來在存取一個物件的屬性前檢查物件是否為空 (null) 非常有用:</p>
+
+<pre class="brush: js">var name = o &amp;&amp; o.getName();
+</pre>
+
+<p>或是用來設預設值:</p>
+
+<pre class="brush: js">var name = otherName || "預設";
+</pre>
+
+<p>JavaScript 也有三元運算子 (tertiary operator),可以用來寫單行的條件陳述式:</p>
+
+<pre class="brush: js">var allowed = (age &gt; 18) ? "是" : "否";
+</pre>
+
+<p>switch 陳述式可以根據一個數字或字串做不同決定:</p>
+
+<pre class="brush: js">switch(action) {
+ case '畫':
+ drawit(); //開始畫
+ break; //中斷
+ case '吃':
+ eatit(); //開始吃
+ break; //中斷
+ default: //預設
+ donothing(); //不做任何事
+}
+</pre>
+
+<p>如果你不加個 <code>break</code> (中斷)陳述式,執行的程式碼會往下「掉」一層。你很少會需要這樣 - 不過假如你真的要這樣,最好用個註解說明一下,這樣才方便除錯:</p>
+
+<pre class="brush: js">switch(a) {
+ case 1: //往下「掉」一層
+ case 2:
+ eatit(); //開始吃
+ break; //中斷
+ default: //預設
+ donothing(); //不做任何事
+}
+</pre>
+
+<p>default 子句 (clause) 是選擇性的,可有可無。如果你喜歡的話,你可以在 switch 部分或 case 部分放表達式 (expression);兩者之間的比較使用的會是 <code>===</code> 運算子:</p>
+
+<pre class="brush: js">switch(1 + 3){
+ case 2 + 2:
+ yay(); //耶!
+ break; //中斷
+ default: //預設
+ neverhappens(); //根本不會發生
+}
+</pre>
+
+<h3 id=".E7.89.A9.E4.BB.B6" name=".E7.89.A9.E4.BB.B6">物件</h3>
+
+<p>JavaScript 物件是一系列的「名稱對數值組合」(name-value pair)。有鑑於此,它們和下列的東西很相近:</p>
+
+<ul>
+ <li>Python 的 dictionary (字典)</li>
+ <li>Perl 和 Ruby 的 hash (雜湊)</li>
+ <li>C 和 C++ 的 hash table (雜湊表)</li>
+ <li>Java 的 HashMap (雜湊地圖)</li>
+ <li>PHP 的 associative array (聯合陣列)</li>
+</ul>
+
+<p>這種資料結構的普遍性證明了其多樣性。由於 JavaScript 的任何東西(核心類型以外)都是物件,任何 JavaScript 程式都自然而然的用到許多雜湊表查詢。還好這些查詢的速度都很快!</p>
+
+<p>「名稱」的部分是個 JavaScript 字串,而「數值」可以是任何 JavaScript 值--包括物件。這可以讓你隨心所欲的建構複雜的資料結構。</p>
+
+<p>建立空物件有種基本方法:</p>
+
+<pre class="brush: js">var obj = new Object();
+</pre>
+
+<p>以及:</p>
+
+<pre class="brush: js">var obj = {};
+</pre>
+
+<p>這兩者在意義上相等;後者叫做物件實體語法 (object literal),比較方便。早期並沒有物件實體語法,也就是為何許多程式碼用的還是舊的方法。</p>
+
+<p>一旦建立了,一個物件的屬性可以用兩種方法存取:</p>
+
+<pre class="brush: js">obj.name = "小明"
+var name = obj.name;
+</pre>
+
+<p>還有...</p>
+
+<pre class="brush: js">obj["name"] = "小明";
+var name = obj["name"];
+</pre>
+
+<p>這兩者也是在意義上相等。第二種方法的優點是屬性的名稱可以在執行的時候以字串提供,也就是說可以動態的變動。這也可以用來取得和設定名稱是保留關鍵字 (reserved keyword) 的屬性:</p>
+
+<pre class="brush: js">obj.for = "Simon"; //語法錯誤
+obj["for"] = "Simon"; //沒問題
+</pre>
+
+<p>物件實體語法可以一次把物件完全初始化:</p>
+
+<pre class="brush: js">var obj = {
+ name: "胡蘿蔔", //名稱
+ "for": "小華", //給誰
+ details: { //詳細資訊
+ color: "橘", //顏色
+ size: 12 //大小
+ }
+}
+</pre>
+
+<p>存取屬性也可以連在一起:</p>
+
+<pre class="brush: js">&gt; obj.details.color
+橘
+&gt; obj["details"]["size"]
+12
+</pre>
+
+<h3 id=".E9.99.A3.E5.88.97" name=".E9.99.A3.E5.88.97">陣列</h3>
+
+<p>JavaScript 的陣列其實是一種特殊的物件。它們的運作方式跟正常的物件很像(數字性的屬性只能透過 [] 語法進行存取),不過有個神奇的屬性,叫做「length」(長度)。這個屬性一定是陣列最高索引數加一。</p>
+
+<p>建立陣列的舊方法如下:</p>
+
+<pre class="brush: js">&gt; var a = new Array();
+&gt; a[0] = "狗";
+&gt; a[1] = "貓";
+&gt; a[2] = "雞";
+&gt; a.length
+3
+</pre>
+
+<p>比較方便的語法便是使用陣列實體語法:</p>
+
+<pre class="brush: js">&gt; var a = ["狗", "貓", "雞"];
+&gt; a.length
+3
+</pre>
+
+<p>在陣列實體語法結尾留個空逗點在各瀏覽器間結果參差不齊,所以最好不要這樣:</p>
+
+<pre class="brush: js">&gt; var a = ["狗", "貓", "雞", ]; //最好不要這麼做
+</pre>
+
+<p>注意--<code>array.length</code> 不一定是陣列的項目數。比方說:</p>
+
+<pre class="brush: js">&gt; var a = ["狗", "貓", "雞"];
+&gt; a[100] = "狐";
+&gt; a.length
+101
+</pre>
+
+<p>別忘了--陣列的 length 就是最高索引數加一。</p>
+
+<p>如果你查詢一個不存在的陣列索引,得到的就是 <code>undefined</code>:</p>
+
+<pre class="brush: js">&gt; typeof(a[90])
+undefined
+</pre>
+
+<p>利用上述,便可以像下列一樣在陣列上做迴圈:</p>
+
+<pre class="brush: js">for (var i = 0; i &lt; a.length; i++) {
+ //處理 a[i]
+}
+</pre>
+
+<p>這樣不是很有效率,因為每迴圈一次就會查詢一次 length 屬性。比較好的做法是:</p>
+
+<pre class="brush: js">for (var i = 0, len = a.length; i &lt; len; i++) {
+ //處理 a[i]
+}
+</pre>
+
+<p>一個更棒的寫法是:</p>
+
+<pre class="brush: js">for (var i = 0, item; item = a[i]; i++) {
+ //處理 item
+}
+</pre>
+
+<p>這裡設定了兩個變數。<code>for</code> 迴圈中間指定變數值的部分會被測試是否為「真的」(truthy)--如果成功了,迴圈便會繼續。由於 <code>i</code> 每次都會加一,陣列內的每個項目會被照順序指定到變數 item。當偵測到「假的」(falsy) 項目時(如 <code>undefined</code>)迴圈便會停止。</p>
+
+<p>注意--這個小技巧只該用在你確定不會含有「假的」值的陣列(比如說一陣列的物件或 <a href="/zh_tw/DOM" title="zh_tw/DOM">DOM</a> 節點)。假如你在可能含有 0 的數字資料或可能含有空字串的字串資料上做迴圈,最好還是用 <code>i, j</code> 的方式。</p>
+
+<p>另外一個做迴圈的方法是用 <code><a href="/zh_tw/Core_JavaScript_1.5_Reference/Statements/for...in" title="zh_tw/Core_JavaScript_1.5_Reference/Statements/for...in">for...in</a></code> 迴圈。不過,假如有人用 <code>Array.prototype</code> 新增新的屬性,那些屬性也會被這種迴圈讀到:</p>
+
+<pre class="brush: js">for (var i in a) {
+ //處理 a[i]
+}
+</pre>
+
+<p>假如你要在陣列結尾加入項目,最安全的方法是這樣:</p>
+
+<pre class="brush: js">a[a.length] = item; //同 a.push(item);
+</pre>
+
+<p>由於 <code>a.length</code> 一定是最高索引數加一,你可以很確定你指定到的是陣列結尾的空間。</p>
+
+<p>陣列附有一些方法 (method):</p>
+
+<pre class="brush: js">a.toString(), a.toLocaleString(), a.concat(item, ..), a.join(sep),
+a.pop(), a.push(item, ..), a.reverse(), a.shift(), a.slice(start, end),
+a.sort(cmpfn), a.splice(start, delcount, [item]..), a.unshift([item]..)
+</pre>
+
+<ul>
+ <li><code>concat</code> 結合,會傳回加入了新項目的新陣列</li>
+ <li><code>pop</code> 會移除最後一個項目並將其傳回</li>
+ <li><code>push</code> 會在結尾加入一或多個項目(就像前面提的 <code>ar.length</code> 方法)</li>
+ <li><code>slice</code> 傳回副陣列</li>
+ <li><code>sort</code> 進行排序,可選擇性的接受「比較性函數」(comparison function)</li>
+ <li><code>splice</code> 讓你透過刪除一個區塊並以更多項目代替來修改陣列</li>
+ <li><code>unshift</code> 會在開頭加入一或多個項目</li>
+</ul>
+
+<h3 id=".E5.87.BD.E5.BC.8F" name=".E5.87.BD.E5.BC.8F">函式</h3>
+
+<p>如同物件,函式 (function) 是瞭解 JavaScript 的核心元件。最基本的函式再簡單不過了:</p>
+
+<pre class="brush: js">function add(x, y) {
+ var total = x + y;
+ return total;
+}
+</pre>
+
+<p>這示範了基本函式的一切。一個 JavaScript 函式可以接受零或多個有名 (named) 參數。函式內文 (body) 要有多少陳述式就有多少陳述式,且可以宣告對於函式而言本地 (local) 的變數。<code>return</code> 陳述式可以在任何時候傳回一值並終止函式。如果沒有 return 陳述式(或者光是 return,沒有值),JavaScript 便會傳回 <code>undefined</code>。</p>
+
+<p>有名參數比較像是做為參考,而非強制性的。你可以呼叫一個函式但不提供其要求的參數,引此傳入的便是 <code>undefined</code>。</p>
+
+<pre class="brush: js">&gt; add()
+NaN // undefined 不能進行加法
+</pre>
+
+<p>你也可以傳入超過函式要求的參數數目:</p>
+
+<pre class="brush: js">&gt; add(2, 3, 4)
+5 // 加了前兩數,不理 4
+</pre>
+
+<p>這或許有些可笑,但函式在內文內還可以存取一個叫做 <a href="/zh_tw/Core_JavaScript_1.5_Reference/Functions/arguments" title="zh_tw/Core_JavaScript_1.5_Reference/Functions/arguments"><code>arguments</code></a> 的變數,一個類似陣列的物件,內含所有遞給函式的值。改寫一下 add 函式便可以使其接受無限量的值:</p>
+
+<pre class="brush: js">function add() {
+ var sum = 0;
+ for (var i = 0, j = arguments.length; i &lt; j; i++) {
+ sum += arguments[i];
+ }
+ return sum;
+}
+
+&gt; add(2, 3, 4, 5)
+14
+</pre>
+
+<p>這樣並沒有比直接寫 <code>2 + 3 + 4 + 5</code> 來得有用。寫個算平均的函式吧:</p>
+
+<pre class="brush: js">function avg() {
+ var sum = 0;
+ for (var i = 0, j = arguments.length; i &lt; j; i++) {
+ sum += arguments[i];
+ }
+ return sum / arguments.length;
+}
+&gt; avg(2, 3, 4, 5)
+3.5
+</pre>
+
+<p>這樣滿有用的,不過又有新問題了。<code>avg()</code> 函式接受的是個逗號分隔的參數清單--不過如果你要取一陣列的平均值呢?你可以把函式重寫成這樣:</p>
+
+<pre class="brush: js">function avgArray(arr) {
+ var sum = 0;
+ for (var i = 0, j = arr.length; i &lt; j; i++) {
+ sum += arr[i];
+ }
+ return sum / arr.length;
+}
+&gt; avgArray([2, 3, 4, 5])
+3.5
+</pre>
+
+<p>但是最好是可以重複利用已經建立好的函式。幸運的是,JavaScript 可以讓你以一陣列的參數來呼叫一個函式。這靠的是使用任何函式物件的 <a href="/zh_tw/Core_JavaScript_1.5_Reference/Global_Objects/Function/apply" title="zh_tw/Core_JavaScript_1.5_Reference/Global_Objects/Function/apply"><code>apply()</code></a> 方法。</p>
+
+<pre class="brush: js">&gt; avg.apply(null, [2, 3, 4, 5])
+3.5
+</pre>
+
+<p><code>apply()</code> 的第二個參數便是做為一系列參數的陣列;第一個參數稍後才會討論。重點是,函式也是物件。</p>
+
+<p>JavaScript 可以讓你建立匿名 (anonymous) 函式。</p>
+
+<pre class="brush: js">var avg = function() {
+ var sum = 0;
+ for (var i = 0, j = arguments.length; i &lt; j; i++) {
+ sum += arguments[i];
+ }
+ return sum / arguments.length;
+}
+</pre>
+
+<p>這個語法上和 <code>function avg()</code> 形式相等。這是非常強大的功能,因為你可以藉此在平常該放表達式的地方塞入一個完整的函式定義。這可以讓你用在各種令人拍案叫絕的技巧上。下列可以把本地 (local) 參數「藏」起來--像 C 的 block scope 一樣:</p>
+
+<pre class="brush: js">&gt; var a = 1;
+&gt; var b = 2;
+&gt; (function() {
+ var b = 3;
+ a += b;
+})();
+&gt; a
+4
+&gt; b
+2
+</pre>
+
+<p>JavaScript 能讓你遞迴地呼叫函式。這在處理樹狀結構的時候特別有用,比如瀏覽器的 <a href="/zh_tw/DOM" title="zh_tw/DOM">DOM</a>。 </p>
+
+<pre class="brush: js">function countChars(elm) {
+ if (elm.nodeType == 3) { // TEXT_NODE
+ return elm.nodeValue.length;
+ }
+ var count = 0;
+ for (var i = 0, child; child = elm.childNodes[i]; i++) {
+ count += countChars(child);
+ }
+ return count;
+}
+</pre>
+
+<p>以上揭露了一個使用匿名函式的潛在問題:如果匿名函式沒有名稱,那要怎麼樣遞迴地自我呼叫?答案是使用 <code>arguments</code> 物件。該物件除了提供一系列的參數以外,還提供了一個叫做 <code>arguments.callee</code> 的屬性。這個屬性所指向的是目前的函式,因此可以用來做遞迴的呼叫:</p>
+
+<pre class="brush: js">var charsInBody = (function(elm) {
+ if (elm.nodeType == 3) { // TEXT_NODE
+ return elm.nodeValue.length;
+ }
+ var count = 0;
+ for (var i = 0, child; child = elm.childNodes[i]; i++) {
+ count += arguments.callee(child);
+ }
+ return count;
+})(document.body);
+</pre>
+
+<p>由於 <code>arguments.callee</code> 是目前的函式,而所有的函式都是物件,你可以因此用 <code>arguments.callee</code> 來在多次呼叫同個函式之間儲存資料。這個函式會記得它被呼叫過多少次:</p>
+
+<pre class="brush: js">function counter() {
+ if (!arguments.callee.count) {
+ arguments.callee.count = 0;
+ }
+ return arguments.callee.count++;
+}
+
+&gt; counter()
+0
+&gt; counter()
+1
+&gt; counter()
+2
+</pre>
+
+<h3 id=".E8.87.AA.E8.A8.82.E7.89.A9.E4.BB.B6" name=".E8.87.AA.E8.A8.82.E7.89.A9.E4.BB.B6">自訂物件</h3>
+
+<p>就典型的物件導向程式設計而言,物件是資料 (data) 以及運算該資料的方法 (method) 所構成的集合體 (collection)。我們以一個含有姓與名兩個欄位的「person」(人)物件來做為例子。在英文,一個人的姓名有兩種寫法:「名 姓」或「姓, 名」。利用之前探討的函式與物件,寫法如下:</p>
+
+<pre class="brush: js">function makePerson(first, last) {
+ return {
+ first: first,
+ last: last
+ }
+}
+function personFullName(person) {
+ return person.first + ' ' + person.last;
+}
+function personFullNameReversed(person) {
+ return person.last + ', ' + person.first
+}
+&gt; s = makePerson("Simon", "Willison");
+&gt; personFullName(s)
+Simon Willison
+&gt; personFullNameReversed(s)
+Willison, Simon
+</pre>
+
+<p>雖然這樣行得通,可是這樣很醜,在全域命名空間 (global namespace) 裡灑了一堆函式。我們需要把函式「附著」(attach) 到物件上。由於函式也是物件,這麼做並不難:</p>
+
+<pre class="brush: js">function makePerson(first, last) {
+ return {
+ first: first,
+ last: last,
+ fullName: function() {
+ return this.first + ' ' + this.last;
+ },
+ fullNameReversed: function() {
+ return this.last + ', ' + this.first;
+ }
+ }
+}
+&gt; s = makePerson("Simon", "Willison")
+&gt; s.fullName()
+Simon Willison
+&gt; s.fullNameReversed()
+Willison, Simon
+</pre>
+
+<p>這裡出現了之前沒有提過的 '<code><a href="/zh_tw/Core_JavaScript_1.5_Reference/Operators/Special_Operators/this_Operator" title="zh_tw/Core_JavaScript_1.5_Reference/Operators/Special_Operators/this_Operator">this</a></code>' 關鍵字。在一個函式內,「<code>this</code>」 指的是目前的物件。其真正的意義是經由你呼叫函數的方式來指定。如果你透過在物件上使用<a href="/zh_tw/Core_JavaScript_1.5_Reference/Operators/Member_Operators" title="zh_tw/Core_JavaScript_1.5_Reference/Operators/Member_Operators">點或中括號記號</a>來呼叫它,這物件就成為「<code>this</code>」。如果在呼叫中沒有使用點記號,「<code>this</code>」就會參考到全域物件. 這經常造成錯誤。舉例來說:</p>
+
+<pre class="brush: js">&gt; s = makePerson("Simon", "Willison")
+&gt; var fullName = s.fullName;
+&gt; fullName()
+undefined undefined
+</pre>
+
+<p>當我們呼叫<code>fullName()</code>,「<code>this</code>」被繫結到全域物件。 既然沒有叫做 <code>first</code> 或 <code>last</code> 的全域變數,我們取得的都是 <code>undefined</code> 。  </p>
+
+<p>我們可以利用「<code>this</code>」關鍵字的好處來改進我們的 <code>makePerson</code> 函式:</p>
+
+<pre class="brush: js">function Person(first, last) {
+ this.first = first;
+ this.last = last;
+ this.fullName = function() {
+ return this.first + ' ' + this.last;
+ }
+ this.fullNameReversed = function() {
+ return this.last + ', ' + this.first;
+ }
+}
+var s = new Person("Simon", "Willison");
+</pre>
+
+<p>我們引入了另一個關鍵字:「<code><a href="/zh_tw/Core_JavaScript_1.5_Reference/Operators/Special_Operators/new_Operator" title="zh_tw/Core_JavaScript_1.5_Reference/Operators/Special_Operators/new_Operator">new</a></code>」。 「<code>new」</code>與「<code>this</code>」有強烈的關係。 他的作用是產生一個全新的空物件,然後呼叫指定的函式,使用新物件的作為函式的「<code>this</code>」。 被「new」呼叫的函式叫做「建構子函數」(constructor functions) 。 </p>
+
+<p>此時我們可以發現,每次產生一個新的 Person 物件都會在其內部重新產生兩個新的 function 物件。如果這兩個 function 物件只有一份而讓大家共用不是更好嗎?所以</p>
+
+<pre class="brush: js">function personFullName() {
+ return this.first + ' ' + this.last;
+}
+function personFullNameReversed() {
+ return this.last + ', ' + this.first;
+}
+function Person(first, last) {
+ this.first = first;
+ this.last = last;
+ this.fullName = personFullName;
+ this.fullNameReversed = personFullNameReversed;
+}
+</pre>
+
+<p>好了,如此一來我們達到每一個 Person 物件都共用同一組 function 物件了。</p>
+
+<p>那能不能做的更好呢?</p>
+
+<pre class="brush: js">function Person(first, last) {
+ this.first = first;
+ this.last = last;
+}
+Person.prototype.fullName = function() {
+ return this.first + ' ' + this.last;
+}
+Person.prototype.fullNameReversed = function() {
+ return this.last + ', ' + this.first;
+}
+</pre>
+
+<p>Person.prototype 是一個由所有 Person 物件共享的物件。他將產生一個可供查看的關係鍊 (有個特殊的名字 <span style="color: #0000cd;">prototype chain</span>)。任何時候當我們想要使用某個不在 Person 中定義的 property 時,JavaScript 就會到 Person.prototype 裡頭尋找。因此, Person.prototype 就成為一個所有 Person 物件共用且可視的一個共享空間(物件)。這是一個提供強大工具,允許你可以在執行的任何一刻增加物件的相關函式。</p>
+
+<pre class="brush: js">&gt; s = new Person("Simon", "Willison");
+&gt; s.firstNameCaps();
+TypeError on line 1: s.firstNameCaps is not a function
+&gt; Person.prototype.firstNameCaps = function() {
+ return this.first.toUpperCase()
+}
+&gt; s.firstNameCaps()
+SIMON
+</pre>
+
+<p>另外,有趣的是,在 JavaScript 中,你不只能加入東西到你自創的物件,甚至可以加入到 JavaScript 原生的物件中!如下面舉例:</p>
+
+<pre class="brush: js">&gt; var s = "Simon";
+&gt; s.reversed()
+TypeError on line 1: s.reversed is not a function
+&gt; String.prototype.reversed = function() {
+ var r = "";
+ for (var i = this.length - 1; i &gt;= 0; i--) {
+ r += this[i];
+ }
+ return r;
+}
+&gt; s.reversed()
+nomiS
+</pre>
+
+<p>我們新加入 String 的 reversed 函式亦可反應在字串物件上!如下所示:</p>
+
+<pre class="brush: js">&gt; "This can now be reversed".reversed()
+desrever eb won nac sihT
+</pre>
+
+<p>承接以前提過的,prototype 會是 chain 的一部分,是以很直觀的,根就是 Object.prototype 了。在 Object.prototype 提供的眾多函式中包含一個 toString() - 他在當你創建一個字串物件時被呼叫。因此我們可以利用他來對我們的 Person 物件 debug:</p>
+
+<pre class="brush: js">&gt; var s = new Person("Simon", "Willison");
+&gt; s.toString()
+[object Object]
+&gt; Person.prototype.toString = function() {
+ return '&lt;Person: ' + this.fullName() + '&gt;';
+}
+&gt; s
+&lt;Person: Simon Willison&gt;
+</pre>
+
+<p>還記得前面提過的 apply() 嗎?當時我們在 avg.apply() 的第一個參數送進 null。現在我們回過頭來解釋 apply()。其實 apply() 的第一個參數會被當成 "this"。舉例來說,我們可以利用 apply() 實作一個 trivialNew() 使他的效果等同於平時使用的 new():</p>
+
+<pre class="brush: js">function trivialNew(constructor) {
+ var o = {}; // Create an object
+ constructor.apply(o, arguments);
+ return o;
+}
+</pre>
+
+<p>當我們這樣做時,並不會產生一個類似於 new() 的函式在 prototype 中。此外,apply() 有一個姊妹函式叫做 <span style="color: #0000cd;">call</span><span style="color: #000000;">,差異在於 call() 接受一個可被擴展的參數串列而非一個陣列。</span></p>
+
+<pre class="brush: js">function lastNameCaps() {
+ return this.last.toUpperCase();
+}
+var s = new Person("Simon", "Willison");
+lastNameCaps.call(s);
+// Is the same as:
+s.lastNameCaps = lastNameCaps;
+s.lastNameCaps();
+</pre>
+
+<h3 id=".E5.B7.A2.E7.8B.80.E5.87.BD.E5.BC.8F" name=".E5.B7.A2.E7.8B.80.E5.87.BD.E5.BC.8F">巢狀函式</h3>
+
+<p>JavaScript 函式宣告可以放在其他函式內。我們之前有在 <code>makePerson()</code> 函式見過這個。巢狀函式的一樣重要的功能是:它們可以存取其母函式 (parent function) 的領域 (scope) 內的變數:</p>
+
+<pre class="brush: js">function betterExampleNeeded() {
+ var a = 1;
+ function oneMoreThanA() {
+ return a + 1;
+ }
+ return oneMoreThanA();
+}
+</pre>
+
+<p>這對於寫更容易維護的程式碼來說很有用。假如某函式需要其他一兩個函式,而這一兩個函式在整個程式的其他部分都不需要用到,你便可以把這些所謂「工具函式」(utility function) 給巢狀性地包在需要它們的函式內,這個主要函式再從其他地方呼叫。這樣便能保持全域領域 (global scope) 的函式不會太多。不在全域領界內塞太多函式是件好事情。</p>
+
+<p>這也能反制全域變數 (global variable) 的誘惑。在撰寫複雜的程式碼的時候,常常會有想利用全域變數來在多個函式之間傳遞數值的這種誘惑--但這麼做,便會導致程式碼非常難以維護。巢狀函式可與其母函式一起共用變數,因此你可以用這個原理來在適當的時機將好幾個函式配在一起,而不用「汙染」全域命名空間 (global namespace)--這可以稱做「本地變數」 (local variable)。使用此技巧時應當小心,不過,此技巧相當有用。</p>
+
+<h3 id="Closures" name="Closures">閉包</h3>
+
+<p>接著我們介紹一種十分強大卻也常使人困惑的機制:閉包 (closure)。在解釋之前,我們先看看下面這段程式,猜猜執行的結果是什麼。</p>
+
+<pre class="brush: js">function makeAdder(a) {
+ return function(b) {
+ return a + b;
+ }
+}
+x = makeAdder(5);
+y = makeAdder(20);
+x(6)
+?
+y(7)
+?
+</pre>
+
+<p>答案揭曉,makeAdder 創造了一個 "adder" 函式,這個新的 "adder" 接受一個參數並將這個參數和 "adder" 被創造時的參數加在一起。</p>
+
+<p>這裡發生的事情很類似先前提過的「內部函式」(inner function):一個新的函式被創造在別的函式內部,並且可以接觸到外面函式的變數。但不同的地方在於,內部函式中一旦回到上層函式,其先前創造的內部函式中的本地變數就消滅了(因為作用域結束了)。但在閉包中,這些本地變數卻<strong>依然存在</strong>(有一種本地變數在離開作用域的那一刻被凍結的感覺!)--否則我們上面例子中的 adder 就無法運作了。說到這裡,你應該知道結果了。x(6) 是 11 而 y(7) 是 27。</p>
+
+<p>更進一步解釋,在 JavaScript 中,只要你開始執行一個函式,那麼就會出現一個「作用域」物件。作用域物件可以根據傳入的參數來做初始化的動作。這聽起來有點類似存放所有全域變數和全域函數的全域物件,但他們卻有幾點不同。首先,一個新的作用域物件是在任何該函式被執行時才產生,而全域物件直接的被使用,而必須進入該作用域才能操作。</p>
+
+<p>再論 makeAdder。當它被呼叫時,一個新的作用域物件被產生,並且帶著一個 property a 在其中(這是由呼叫 makeAdder 者傳入的)。隨後 makeAdder 回傳一個新創的函式。正常情況下,JavaScript 的垃圾回收機制理應回收這個作用域物件,但在我們的例子中,回傳的函式卻保留了一個可以再次訪問此作用域物件的參照。因此,這個作用域物件便沒被真正回收--必須等到未來沒有任何參照可以指向它,方能回收。</p>
+
+<p>多個作用域物件會組成作用域鏈 (scope chain),類似於 JavaScript 物件系統中的原型鏈。</p>
+
+<p>簡單地做個結論,閉包是作用域物件和一個函式的組合,反映了其被創造之時的狀態。閉包允許你保留狀態,而這是很常用的功能。</p>
+
+<h3 id="Memory_leaks" name="Memory_leaks">記憶體流失</h3>
+
+<p>雖然閉包很方便,但卻容易在 IE 中造成記憶體流失。</p>
+
+<p>JavaScript 是具有垃圾回收特性的語言,物件的生滅都是由執行環境(如瀏覽器)所控制。</p>
+
+<ol>
+ <li>記憶體配置:創造物件時 </li>
+ <li>記憶體釋放:當沒有參照可以指向該物件時</li>
+</ol>
+
+<p>當瀏覽器在執行的時候,需要維護來自<span style="color: #0000cd;"> DOM</span> 的大量物件。IE 使用自己的垃圾回收機制,而這個管理機制和 JavaScript 的不同--這就造成了記憶體流失。</p>
+
+<p>在我們這裡的例子中,記憶體流失源自於環狀的相互參照 (circular reference)-- JavaScript 物件及原生物件之間的相互參照。以下面的程式為例:</p>
+
+<pre class="brush: js">function leakMemory() {
+ var el = document.getElementById('el');
+ var o = { 'el': el };
+ el.o = o;
+}
+</pre>
+
+<p>此時便發生了記憶體流失。除非 IE 重新啟動,否則 el 和 o 使用的記憶體皆無法被釋放。這種例子容易因不小心而出現。</p>
+
+<p>其實記憶體流失很難引起注意,除非</p>
+
+<ol>
+ <li>程式需要長期執行</li>
+ <li>因記憶體流失而消耗大量的記憶體</li>
+ <li>迴圈中明顯地浪費記憶體</li>
+</ol>
+
+<p>閉包容易產生記憶體流失。如下舉例 : </p>
+
+<pre class="brush: js">function addHandler() {
+ var el = document.getElementById('el');
+ el.onclick = function() {
+ this.style.backgroundColor = 'red';
+ }
+}
+</pre>
+
+<p>在這段程式中,我們設定了一個 element,讓它可以在被點選時變紅。這裡形成了記憶體流失。你發現了嗎?因為指向 el 的參照不經意地被由匿名內部函式產生的閉包連結了。這就在 JavaScript 物件 (function) 和原生物件 (el) 之間產生了相互參照。</p>
+
+<p>在現實生活中充滿了類似的案例,但我們其實可以很容易地處理。看一個簡單的示範:</p>
+
+<pre class="brush: js">function addHandler() {
+ var el = document.getElementById('el');
+ el.onclick = function() {
+ this.style.backgroundColor = 'red';
+ }
+ el = null;
+}
+</pre>
+
+<p>如此一來我們解除了相互參照。</p>
+
+<p>此外,尚有一種巧妙的方式可以藉由增加一個新的閉包來消除相互參照:</p>
+
+<pre class="brush: js">function addHandler() {
+ var clickHandler = function() {
+ this.style.backgroundColor = 'red';
+ }
+ (function() {
+ var el = document.getElementById('el');
+ el.onclick = clickHandler;
+ })();
+}
+</pre>
+
+<p>在此例子中,隱藏了來自 clickHandler 產生的內容,因而消除了相互參照。</p>
+
+<p>避免閉包還有另外一種方法:利用 <code>window.onunload</code> 事件消除相互參照。許多事件函示庫都會自動這麼做。</p>
+
+<div class="originaldocinfo">
+<h2 id=".E5.8E.9F.E5.A7.8B.E6.96.87.E4.BB.B6.E8.B3.87.E8.A8.8A" name=".E5.8E.9F.E5.A7.8B.E6.96.87.E4.BB.B6.E8.B3.87.E8.A8.8A">原始文件資訊</h2>
+
+<ul>
+ <li>作者:<a class="external" href="http://simon.incutio.com/">Simon Willison</a></li>
+ <li>最後更新日期:2006 年 3 月 7 日</li>
+ <li>著作權:© 2006 Simon Willison,Creative Commons: Attribute-Sharealike 2.0 (創用:姓名標示-相同方式分享 2.0)授權。</li>
+ <li>更多資訊:其他關於這份教學的資訊(以及原作者所用的投影片)請前往原作者的網誌:<a class="external" href="http://simon.incutio.com/archive/2006/03/07/etech">Etech weblog</a>。</li>
+</ul>
+</div>
+
+<p> </p>