JavaScript 快速語法參考(全)
原文:JavaScript Quick Syntax Reference
協議:CC BY-NC-SA 4.0
一、使用 JavaScript
要開始試驗 JavaScript,您應該安裝一個支援這種語言的整合開發環境(IDE)。有很多不錯的選擇,比如 NetBeans、Eclipse、Visual Studio、括號。在本書中,我們將使用 NetBeans,它可以從 Netbeans.org 免費獲得。確保你下載了一個包含 HTML 5 支援的包,因為它也包含對 JavaScript 的支援。
或者,您可以使用簡單的文字編輯器(如記事本)進行開發,儘管這不如使用 IDE 方便。如果您選擇這樣做,只需建立一個副檔名為.html
的空文件,並在您選擇的編輯器中開啟它。
建立專案
安裝 NetBeans 後,繼續執行該程式。然後,您需要建立一個專案,該專案將管理 HTML 原始檔和網站的其他資源。轉到檔案➤新專案以顯示新專案視窗。從這裡,在左側框架中選擇 HTML 5 類別,然後在右側框架中選擇 HTML 5 應用專案。單擊 Next 按鈕,您可以配置專案的名稱和位置。完成後,單擊“完成”讓嚮導建立您的專案。
您現在已經建立了一個 HTML 5 專案。在“專案”皮膚(“➤專案”視窗)中,您可以看到該專案由一個名為“index.html”的檔案組成,該檔案位於站點的根資料夾中。該檔案包含一些基本的 HTML 5 標記,可以進一步簡化為如下所示的標記。
<!doctype html>
<html>
<head><title>JavaScript Test</title></head>
<body></body>
</html>
嵌入 JavaScript
將 JavaScript 插入 web 文件有兩種方法。第一種是將程式碼放在一個script
元素中。一個文件可以有多個這樣的元素,每個元素可以包含任意數量的 JavaScript 語句。
<script></script>
另一種更常見的方法是將程式碼包含在外部檔案中,然後使用script
元素的src
屬性連結到該檔案。這樣,幾個文件就可以使用相同的程式碼,而不必在每一頁上都重複程式碼。
<script src="mycode.js"></script>
按照慣例,.js
副檔名用於包含 JavaScript 程式碼的檔案。要將此名稱的新檔案新增到 NetBeans 專案中,請右鍵單擊“專案”皮膚中的站點根資料夾,然後選擇“新建➤ JavaScript 檔案”。在對話方塊中,將原始檔命名為“mycode.js”,單擊 Finish,該檔案將被新增到您的專案中併為您開啟。
出於實驗的目的,您可以使用第一種嵌入方法來內聯您的程式碼。然而,對於真實世界的應用,除了最簡單的指令碼之外,所有的指令碼都應該是外部的。這使得程式碼更容易閱讀和維護,因為它將 JavaScript 程式碼(頁面行為)與 HTML 標記(頁面內容)分開。由於瀏覽器快取了外部檔案,這也提高了網站的效能。
顯示文字
在學習一門新的程式語言時,第一個示例 JavaScript 程式碼將顯示一個“Hello World”文字字串,這是很常見的。這是透過在 web 文件的body
元素中新增下面一行來實現的。
<script>
document.write("Hello World");
</script>
這段程式碼語句使用了屬於document
物件的write
方法。該方法接受文字作為其引數,用雙引號分隔。這些概念將在後面的章節中進一步探討。
JavaScript 中的語句用分號分隔。如果語句後跟換行符,可以省略分號,因為這也被解釋為語句分隔符。
document.write("Hello World")
完整的 web 文件現在應該是這樣的。
<!doctype html>
<html>
<head><title>JavaScript Test</title></head>
<body>
<script>
document.write("Hello World");
</script>
</body>
</html>
若要檢視該網頁,請用 web 瀏覽器開啟 HTML 檔案。在 NetBeans 中,這是透過單擊執行➤執行專案(F6)或單擊工具欄上的綠色箭頭來完成的。您可以從“執行➤集專案瀏覽器”中選擇您的首選瀏覽器。在瀏覽器中檢視文件時,一旦頁面載入並顯示文字字串,就會執行指令碼。
檢視原始碼
當瀏覽器開啟時,您可以透過按 Ctrl + U 檢視組成頁面的原始碼。該快捷方式適用於所有主流瀏覽器,包括 Chrome、Firefox 和 Internet Explorer (IE)。原始碼視窗顯示 HTML 標記,以及未解析的 JavaScript 程式碼。
以這種方式檢視 web 頁面的原始碼提供了一個向其他 web 開發人員學習的好方法。每當你在一個網頁上發現一個有趣的功能——無論它是用 HTML、CSS、JavaScript 還是其他語言製作的——頁面原始碼通常會揭示它是如何建立的。
瀏覽器相容性
JavaScript 通常執行在客戶端,在瀏覽器內部,而不是伺服器端。因此,要執行程式碼,需要客戶端在支援 JavaScript 的瀏覽器中檢視文件。
因為 JavaScript 是最流行的客戶端指令碼語言,所以它幾乎可以在今天使用的所有瀏覽器上工作。但是,客戶端可能會選擇禁用 JavaScript,因此無法保證客戶端程式碼得到執行。即便如此,今天大多數網站都使用 JavaScript,而且許多網站都依賴它才能正常執行。
HTML 提供了noscript
元素,為不支援 JavaScript 或禁用了 JavaScript 的瀏覽器指定替代內容。
<noscript>
Please enable JavaScript for full functionality of this site.
</noscript>
控制檯視窗
大多數瀏覽器都有一個開發控制檯,允許您檢視 JavaScript 程式碼中的資訊,以便進行除錯。要將資訊列印到該控制檯,可以使用 console 物件的 log 方法。
<script>
console.log("Hello Console");
</script>
在 Chrome、Firefox 和 Internet Explorer 中,開啟控制檯的過程是相同的。右鍵單擊頁面並選擇 Inspect Element。這將開啟開發視窗,從中可以找到 Console 選項卡。在 Internet Explorer 中,您需要首先啟動開發視窗,然後重新整理頁面以檢視控制檯輸出。
評論
註釋用於向開發人員闡明程式碼,它們對程式碼的解析沒有影響。JavaScript 具有單行(//
)和多行(/**/
)註釋的標準符號,許多其他語言也使用這種符號。
<script>
// single-line comment
/* multi-line
comment */
</script>
和 HTML 一樣,空白字元——比如空格、製表符和註釋——在 JavaScript 中通常會被忽略。這讓你在如何格式化你的程式碼上有很大的自由度。您使用的格式是個人喜好的問題。選擇一種對你有意義的風格,併力求保持一致。
程式碼提示
如果您不確定一個特定的物件包含什麼,或者一個函式接受什麼引數,您可以利用一些 ide 中的程式碼提示,比如 NetBeans。透過按 Ctrl + Space 開啟程式碼提示視窗,並提供對您能夠在當前上下文中使用的任何程式碼實體的快速訪問。這是一個強大的特性,你應該學會好好利用它。
Footnotes 1
netbeans . org/downloads/
。
二、變數
變數是用於儲存資料(如數字或字串)的容器,因此它們可以在指令碼中多次使用。
宣告變數
要建立一個變數,可以使用關鍵字var
,後跟一個名稱,稱為識別符號。變數的一個常見命名約定是,除了第一個單詞,每個單詞最初都要大寫。
var myVar;
可以使用等號給變數賦值,這叫做賦值運算子(=
)。這被稱為分配或初始化變數。
myVar = 10;
宣告和賦值可以合併成一條語句。當一個變數被賦值時,它就被定義了。
var myVar = 10;
透過使用逗號運算子(,
),有一種在同一個語句中建立多個變數的簡便方法。
var myVar = 10, myVar2 = 20, myVar3;
一旦變數被宣告,就可以透過引用變數名來使用它。例如,透過將識別符號傳遞給document.write
方法,可以將變數的值列印到 web 文件中。
document.write(myVar); // "10"
請記住,變數識別符號區分大小寫,因此大寫和小寫字母有不同的含義。JavaScript 中的識別符號可以包括字母、美元符號($
)、下劃線(_
)和數字,但不能以數字開頭。它們也不能包含空格或特殊字元,並且不能是保留關鍵字。
var _myVar32; // allowed
var 32Var; // incorrect (starts with number)
var my Var; // incorrect (contains space)
var var@32; // incorrect (contains special character)
var var; // incorrect (reserved keyword)
動態打字
JavaScript 是一種動態型別語言。因此,不需要指定變數的資料型別,任何變數都可以包含任何資料型別。
var myType = "Hi"; // string type
myType = 1.5; // number type
此外,變數的值將根據需要自動轉換,這取決於使用它的上下文。
// Number type evaluated as string type
console.log(myType); // "1.5"
由於這些隱式型別轉換,知道變數的基礎型別並不總是必要的。儘管如此,瞭解 JavaScript 在後臺處理的資料型別還是很有用的。這六種型別如下:數字、布林、字串、物件、未定義和空。
數字型別
JavaScript 對於整數和浮點數只有一種型別。整數可以用十進位制(基數為 10)、八進位制(基數為 8)或十六進位制(基數為 16)來表示。整數值上的前導零表示它是八進位制的,前導 0x(或 0X)表示十六進位制的。十六進位制整數可以包括數字 0–9 和字母 A–F,而八進位制整數只能包括數字 0–7。下面的整數文字都代表同一個數,在十進位制記數法中是 10。
var dec = 10; // decimal notation
var oct = 012; // octal notation
var hex = 0xA; // hexadecimal notation
浮點數可以用十進位制或指數(科學)記數法來表示。指數記數法的用法是在十進位制指數後加上E
(或e
)。
var num = 1.23;
var exp = 3e2; // 3*10² = 300
請記住,JavaScript 中的所有數字都在後臺儲存為雙精度浮點數。
布林型別
bool 型別可以儲存布林值,該值只能為 true 或 false。這些值由關鍵字true
和false
指定。
var myBool = true;
布林值通常與條件語句和迴圈語句一起使用,這將在後面的章節中討論。
未定義的型別
JavaScript 有一個名為undefined
的值,用來表示沒有值。這是宣告的變數在沒有初始值的情況下得到的值。
var myUndefined;
console.log(myUndefined); // "undefined"
用於儲存該值的資料型別也被命名為undefined
。這可以透過使用typeof
運算子來顯示,它檢索一個型別的字串表示。
console.log(typeof myUndefined); // "undefined"
請記住,未定義的變數不同於未宣告的變數。任何試圖訪問未宣告變數的行為都將導致丟擲ReferenceError
異常,從而中止指令碼的執行。
console.log(myUndeclared); // throws a ReferenceError
零點型別
型別和值 null 表示沒有值的物件。與可能由語言級行為導致的 undefined 相反,null 值總是透過程式碼設定的。它通常用作指示異常或錯誤情況的函式返回值。
var myNull = null;
console.log(myNull); // "null"
雖然 null 是一種資料型別,但是typeof
運算子會將這種型別作為一個物件來計算。這被認為是語言規範中的一個錯誤。
console.log(typeof myNull); // "object"
在布林上下文中,null 和 undefined 都被評估為false
。以下示例使用 not 運算子(!
)將這些值強制轉換為布林型別。該運算子反轉布林結果,因此被兩次用於檢索原始值的布林表示。
console.log(!!null); // "false"
console.log(!!undefined); // "false"
相反,在數值上下文中,null 表現為 0,而 undefined 導致整個表示式計算為 NaN。
console.log(null * 5); // "0"
console.log(undefined * 5); // "NaN"
特殊數值
JavaScript 有三個特殊的數值:Infinity、-Infinity 和 NaN。這些值用於表示在計算過程中發生了一些異常。例如,以下計算得出這三個值。
console.log(1 / 0); // "Infinity"
console.log(-1 / 0); // "-Infinity"
console.log(0 / 0); // "NaN"
值 NaN 是非數字的縮寫,表示不可表示的數值。它是數學運算失敗時通常使用的返回值。例如,從-1 中取平方根得到 NaN。這個計算可以使用全域性 Math 物件的 sqrt 方法來執行。
var myNaN = Math.sqrt(-1);
console.log(myNaN); // "NaN"
console.log(typeof myNaN); // "number"
嘗試在數值上下文中計算非數值也會導致 NaN。
console.log("Hi" * 3); // "NaN"
NaN 有一個奇怪的特性,它與任何其他值都不相等,包括另一個 NaN 值。要確定一個值是否為 NaN,可以使用全域性 is NaN 函式。
console.log(NaN == NaN); // "false"
console.log(isNaN(myNaN)); // "true"
三、運算子
運算子是一個符號,它使指令碼執行特定的數學或邏輯操作。JavaScript 中的運算子可以分為五種型別:算術、賦值、比較、邏輯和按位運算子。
算術運算子
算術運算子包括四種基本算術運算,以及用於獲得除法餘數的模數運算子(%
)。
x = 3 + 2; // 5 - addition
x = 3 - 2; // 1 - subtraction
x = 3 * 2; // 6 - multiplication
x = 3 / 2; // 1.5 - division
x = 3 % 2; // 1 - modulus (division remainder)
賦值運算子
第二組是賦值運算子。最重要的是賦值運算子(=
)本身,它給變數賦值。
x = 0; // assignment
組合賦值運算子
賦值運算子和算術運算子的一個常見用途是對變數進行運算,然後將結果儲存回同一個變數中。使用組合賦值運算子可以縮短這些操作。
x += 5; // x = x+5;
x -= 5; // x = x-5;
x *= 5; // x = x*5;
x /= 5; // x = x/5;
x %= 5; // x = x%5;
遞增和遞減運算子
另一種常見的操作是將變數加 1 或減 1。這可以用增量(++
)和減量(--
)運算子來簡化。
x++; // x = x+1;
x--; // x = x-1;
這兩者都可以用在變數之前或之後。
x++; // post-increment
x--; // post-decrement
++x; // pre-increment
--x; // pre-decrement
無論使用哪個變數,變數的結果都是相同的。不同的是,後運算子在改變變數之前返回原始值,而前運算子先改變變數,然後返回值。
x = 5; y = x++; // y=5, x=6
x = 5; y = ++x; // y=6, x=6
比較運算子
比較運算子比較兩個值,並返回 true 或 false。它們主要用於指定條件,即計算結果為 true 或 false 的表示式。
x = (2 == 3); // false - equal to
x = (2 === 3); // false - identical
x = (2 !== 3); // true - not identical
x = (2 != 3); // true - not equal to
x = (2 > 3); // false - greater than
x = (2 < 3); // true - less than
x = (2 >= 3); // false - greater than or equal to
x = (2 <= 3); // true - less than or equal to
嚴格相等運算子===
和!==
用於比較型別和值。這些是必要的,因為常規的等於(==
)和不等於(!=
)運算子會在比較運算元之前自動執行型別轉換。
x = (1 == "1"); // true (same value)
x = (1 === "1"); // false (different types)
當不需要等於運算的型別轉換功能時,使用嚴格比較被認為是一種好的做法。
邏輯運算子
邏輯運算子通常與比較運算子一起使用。如果左右兩邊都為真,則邏輯 and ( &&
)計算為真,如果左右兩邊都為真,則邏輯 or ( ||
)為真。對一個布林結果取反,有一個邏輯非(!
)運算子。請注意,對於“邏輯與”和“邏輯或”,如果結果已經由左側確定,則不會計算右側。
x = (true && false); // false - logical and
x = (true || false); // true - logical or
x = !(true); // false - logical not
按位運算子
按位運算子可以處理組成整數的各個位。例如,右移位運算子(>>
)將除符號位之外的所有位向右移動,而零填充右移位(>>>
)將包括符號位在內的所有位向右移動。這兩個運算子對正數的計算是相同的。
x = 5 & 4; // 101 & 100 = 100 (4) - and
x = 5 | 4; // 101 | 100 = 101 (5) - or
x = 5 ^ 4; // 101 ^ 100 = 001 (1) - xor
x = 4 << 1; // 100 << 1 =1000 (8) - left shift
x = 4 >> 1; // 100 >> 1 = 010 (2) - right shift
x = 4 >>>1; // 100 >>> 1 = 010 (2) - zero-fill right shift
x = ∼4; // ∼00000100 = 11111011 (-5) - invert
按位運算子也有組合賦值運算子。
x=5; x &= 4; // 101 & 100 = 100 (4) - and
x=5; x |= 4; // 101 | 100 = 101 (5) - or
x=5; x ^= 4; // 101 ^ 100 = 001 (1) - xor
x=4; x <<= 1; // 100 << 1 =1000 (8) - left shift
x=4; x >>= 1; // 100 >> 1 = 010 (2) - right shift
x=4; x >>>=1; // 100 >>> 1 = 010 (2) - right shift
請記住,JavaScript 數字儲存為雙精度浮點數。但是,位運算需要對整數進行操作,因此在執行位運算時,數字會臨時轉換為 32 位有符號整數。
運算子優先順序
在 JavaScript 中,表示式通常從左到右計算。但是,當表示式包含多個運算子時,這些運算子的優先順序決定了它們的求值順序。下表顯示了優先順序順序,其中優先順序最低的運算子將首先被計算。同樣的順序也適用於許多其他語言,比如 PHP 和 Java。
| 在…之前 | 操作員 | 在…之前 | 操作員 | | --- | --- | --- | --- | | one | `() [] . x++ x--` | eight | `&` | | Two | `! ∼ ++x --x` | nine | `^` | | three | `* / %` | Ten | `|` | | four | `+ -` | Eleven | `&&` | | five | `<< >> >>>` | Twelve | `||` | | six | `< <= > >=` | Thirteen | `= op=` | | seven | `== != === !===` | Fourteen | `,` |舉個例子,乘法比加法更難繫結,因此將在下面的程式碼行中首先進行計算。
x = 4 + 3 * 2; // 10
這可以透過將表示式中首先被求值的部分用括號括起來來說明。從表中可以看出,括號在所有運算子中優先順序最低。
x = 4 + (3 * 2); // 10
四、陣列
陣列是用於儲存值集合的資料結構。JavaScript 陣列可以分為三類:數值型、關聯型和多維型。這些陣列之間的區別只是概念上的,因為 JavaScript 認為它們都是陣列物件。
數字陣列
數字陣列用數字索引儲存陣列中的每個元素。可以透過以下方式使用陣列建構函式建立空陣列。
var a = new Array(); // empty array
要將元素新增到陣列中,可以透過將元素的索引放在方括號中來一次引用一個元素。為元素賦值會自動為該元素建立空間,並增加陣列的容量。請注意,陣列索引從零開始。
a[0] = 1;
a[1] = 2;
a[2] = 3;
陣列的初始容量可以透過向陣列建構函式傳遞一個數值引數來指定。這可用於在預先知道陣列將容納的元素數量的情況下提高效能。
var b = new Array(3);
將多個引數或非數字引數傳遞給陣列建構函式會將這些值賦給陣列的第一個元素。
var c = new Array(1, 2, 3);
建立陣列的另一種方法是將元素值放在方括號中,即所謂的陣列文字。這是建立陣列的最短且最常用的方法。
var d = [1, 2, 3];
省略陣列中的值為建立空陣列提供了一種快捷方式。
var e = []; // empty array
透過引用方括號內所需元素的索引來訪問陣列的內容。
var f = [1, 2, 3];
document.write(f[0] + f[1] + f[2]); // "6"
如果引用的元素不存在,則返回未定義型別的物件。
document.write(f[3]); // "undefined"
注意,就像常規變數一樣,陣列中的元素可以儲存任何資料型別或其組合。
var mixed = [0, 3.14, "string", true];
關聯陣列
關聯陣列使用鍵字串而不是數字索引來標識元素。要建立一個陣列,首先宣告一個空陣列,然後將值賦給所需的鍵。
var g = new Array();
g["name"] = "Peter";
g["age"] = 25;
當訪問這些元素時,記住鍵名是很重要的,因為這些陣列不能用索引來訪問。
document.write(g["name"] + " is " + g["age"]); // "Peter is 25"
JavaScript 中的陣列是物件,它們的元素是物件屬性。因此,關聯陣列的元素也可以用點符號來引用。
var h = new Array();
h.name = "Peter";
h.age = 25;
document.write(h.name + " is " + h.age); // "Peter is 25"
數字元素不能以這種方式訪問,必須使用括號符號引用。
h[0] = 1;
可以在同一個陣列中混合數字元素和關聯元素,因為 JavaScript 不會區分它們。事實上,索引是作為鍵字串儲存在後臺的,也可以這樣引用。
h["0"] = 1;
多維陣列
透過將陣列作為元素新增到另一個陣列中,可以使陣列成為多維的。
var m = [ ["00","01"], ["10","11"] ];
多維陣列可以有任意多個維度,但是很少需要兩個以上的維度。對於每個額外的維度,新增另一組方括號。
document.write(m[1][1]); // "11"
與許多其他語言不同,JavaScript 中的多維陣列不需要所有子陣列的長度都相同。隨著陣列容量的自動調整,也可以稍後在指令碼中更改尺寸。
m[1][2] = "12";
陣列物件
array 物件提供對許多用於運算元組的成員的訪問。一個這樣的成員是 length 屬性,它檢索或設定陣列的當前容量。
var x = [1, 2, 3];
var len = x.length; // 3
x.length = 2; // deletes third element
document.write(x[2]); // "undefined"
IDE 中的程式碼提示提供了陣列物件可用的成員列表。舉個例子,pop 方法從陣列中移除最後一個元素,push 將一個或多個元素追加到陣列的末尾。
var y = [1, 2];
y.push(3); // add element to end of array
y.pop(); // remove last element
五、字串
字串由一系列用雙引號或單引號分隔的字元組成。使用哪種符號是個人喜好的問題。
var s1 = "Hello";
var s2 = ' World';
有兩個運算子可以對字串進行操作。對於組合字串,有一個加號(+
),在這個上下文中稱為連線運算子。它有一個伴隨的賦值運算子(+=
),將一個字串附加到一個字串變數的末尾。
var greeting = s1 + s2; // "Hello World"
s1 += s2; // "Hello World"
要在字串中換行,必須新增反斜槓。該字元對換行符進行轉義,換行符在 JavaScript 中通常表示語句的結束。反斜槓和換行符都從字串的值中刪除。
greeting = "Hello \
World";
跳脫字元
跳脫字元用於書寫特殊字元,如新行和製表符。這些字元前面總是有一個反斜槓“\
”。例如,要在單引號字串中插入單引號,該標記前面需要有反斜槓。
var s = 'It\'s'; // "It’s"
下表列出了 JavaScript 中可用的跳脫字元。
| 性格;角色;字母 | 意義 | 性格;角色;字母 | 意義 | | --- | --- | --- | --- | | `\n` | 新行 | `\f` | 換頁 | | `\t` | 橫表 | `\v` | 垂直標籤 | | `\'` | 單引號 | `\"` | 雙引號 | | `\b` | 退格鍵 | `\r` | 回車 | | `\\` | 反斜線符號 | | |除了這些跳脫字元之外,還有用於引用 Unicode 和 Latin-1 編碼字符集的符號。Unicode 字元表示為“\u
”,後跟一個 4 位十六進位制數。Latin-1 字元可以表示為以“\x
”開頭的三位八進位制數或兩位十六進位制數。如下圖所示,換行符用四種不同的方式表示。
var line = '\n'; // escape code
line = '\012'; // octal Latin-1
line = '\x0A'; // hexadecimal Latin-1
line = '\u000A'; // hexadecimal Unicode
字串和數字
在同時包含字串和數值的表示式中,串聯運算子會將數字轉換為字串。如果可能的話,其他數值運算子將嘗試將字串轉換為數字,否則將計算為 NaN。
"5" + 5; // "55"
"5" - 5; // 0
"a" - 5; // NaN
用字串表示的數值可以用parseInt
函式轉換成整數。
parseInt("5") + 5; // 10
類似地,parseFloat
可以用來將字串轉換成浮點數。對於這兩個函式,只返回字串中的第一個數字,否則,如果第一個字元不是數字,則該方法返回 NaN。
parseFloat("3.14"); // 3.14
parseFloat("Hi"); // NaN
或者,一元加法運算子(+
)可用於執行字串到數字的轉換,方法是將加法符號放在字串之前。
+"5" + 5; // 10
字串物件
JavaScript 中的所有字串都是字串物件。因此,它們提供了對執行常見字串操作時有用的屬性和方法的快速訪問。例如,字串中的字元數可以使用 length 屬性來確定。
var a = "Hello";
var len = a.length; // 5
當您鍵入點號來訪問 string 物件的成員時,IDE 會提供程式碼提示,為您提供可用成員的完整列表。例如,toLowerCase
方法將字串轉換成小寫字母。返回結果字串,而不更改原始字串。
var lower = a.toLowerCase(); // "hello"
JavaScript 將任何一段文字解釋為 string 物件的一個例項。因此,可以直接在字串常量上呼叫方法,就像在字串變數上一樣。
var upper = "abc".toUpperCase(); // "ABC";
六、條件語句
條件語句用於根據不同的條件執行不同的程式碼塊。
如果語句
只有當括號內的表示式被求值為 true 時,if
語句才會執行。在 JavaScript 中,這不必是布林表示式。它可以是任何表示式,在這種情況下,零、null、NaN、空字串和未定義的變數被計算為 false,而所有其他值為 true。
if (x < 1) {
document.write("x < 1");
}
為了測試其他條件,if
語句可以被任意數量的else if
子句擴充套件。只有當前面的條件為假時,才會測試每個附加條件。
else if (x > 1) {
document.write("x > 1");
}
對於處理所有其他情況,可以在末尾有一個else
子句,如果所有先前的條件都為假,則執行該子句。
else {
document.write("x == 1");
}
如果只需要有條件地執行一條語句,可以省去花括號。但是,始終包含它們被認為是一種好的做法,因為它們可以提高程式碼的可讀性。
if (x < 1)
document.write("x < 1");
else if (x > 1)
document.write("x > 1");
else
document.write("x == 1");
交換語句
switch
語句檢查表示式和一系列 case 標籤之間的相等性,然後將執行傳遞給匹配的 case。表示式可以是任何型別,並且將使用嚴格比較(===
)來匹配案例標籤。開關可以包含任意數量的 case 子句,並且可以以處理所有其他情況的預設標籤結束。
switch (x) {
case 0: document.write("x is 0"); break;
case 1: document.write("x is 1"); break;
default: document.write("x is not 0 or 1"); break;
}
注意,每個 case 標籤後的語句以關鍵字break
結束,以跳過開關的其餘部分。如果省略了 break,執行將一直進行到下一個案例,如果需要以相同的方式評估幾個案例,這將非常有用。
三元運算子
除了if
和switch
語句之外,還有三元運算子(?:
,它為單個if else
語句提供了快捷方式。這個運算子有三個表示式。如果第一個為真,則計算並返回第二個表示式;如果為假,則計算並返回第三個。
// Ternary operator expression
y = (x === 1) ? 1 : 2;
在 JavaScript 中,該運算子也可以用作獨立的程式碼語句,而不僅僅是表示式。
// Ternary operator statement
(x === 1) ? y = 1 : y = 2;
程式設計術語“表示式”指的是計算出一個值的程式碼,而語句是以分號或右花括號結束的程式碼段。
七、迴圈
迴圈語句用於多次執行一個程式碼塊。JavaScript 有四種迴圈:while
、do-while
、for
和for-in
。與條件if
語句一樣,如果程式碼塊中只有一條語句,可以省略這些迴圈的花括號。
While 迴圈
只有當條件為真時,while
迴圈才會遍歷程式碼塊,並且只要條件保持為真,迴圈就會繼續。
var i = 0;
while (i < 10) {
document.write(i++); // 0-9
}
這裡的迴圈將列印出數字 0 到 9。請記住,條件只在每次迭代開始時檢查。
Do-While 迴圈
除了檢查程式碼塊之後的條件之外,do-while
迴圈的工作方式與while
迴圈相同。因此,它將始終至少在程式碼塊中執行一次。注意,這個迴圈以分號結束。
var j = 0;
do {
document.write(j++); // 0-9
} while (j < 10);
For 迴圈
for
迴圈在程式碼塊中執行特定的次數。它使用三個引數。第一個初始化一個計數器,並且總是在迴圈之前執行一次。第二個引數儲存迴圈的條件,並在每次迭代之前進行檢查。第三個引數包含計數器的增量,在每次迭代結束時執行。
for (var k = 0; k < 10; k++) {
document.write(k); // 0-9
}
這個迴圈有幾種變化,因為任何一個引數都可以省略。例如,如果省略第一個和第三個引數,它的行為方式與while
迴圈相同。
var k;
for (; k < 10;) {
document.write(k++); // 0-9
}
第一個和第三個引數也可以使用逗號運算子(,
)拆分成幾個語句。
for (var k = 0, m = 0; k < 10; k++, m--) {
document.write(k+m); // 000... (10x)
}
屬性獲取陣列中元素的數量。與for
迴圈一起,它可以用來遍歷一個陣列。
var a = [1, 2, 3];
for (var i = 0; i < a.length; i++) {
document.write(a[i]); // "123"
}
如果不需要跟蹤迭代,for-in
迴圈提供了一個更短的遍歷陣列的語法。
For-in 迴圈
for-in
迴圈提供了一種簡單的方法來遍歷陣列中的元素或物件中的屬性。在每次迭代中,將下一個屬性的鍵或索引賦給變數,迴圈繼續迭代,直到遍歷完物件的所有成員。
var colors = ["red","green","blue"];
for (i in colors) {
document.write(colors[i] + " "); // "red green blue"
}
中斷並繼續
有兩個跳轉語句可以在迴圈內部使用:break
和continue
。break
關鍵字結束迴圈結構,而continue
跳過當前迭代的剩餘部分,並在下一次迭代的開始處繼續。
for (var i = 0; i < 10; i++)
{
if (i == 2) continue; // start next iteration
else if (i == 5) break; // end loop
document.write(i); // "0134"
}
要中斷當前迴圈之上的迴圈,必須首先標記外部迴圈,方法是在外部迴圈前新增一個名稱,後跟一個冒號。有了這個標籤,它現在可以用作break
語句的引數,告訴它從哪個迴圈中退出。這也適用於continue
關鍵字,以便跳到指定迴圈的下一次迭代。
myloop:
for (var i = 0; i < 10; i++)
{
var j = 0;
while (++j < 10)
{
break myloop; // end for loop
}
}
八、函式
函式是可重用的程式碼塊,只有在被呼叫時才會執行。它們允許開發人員將他們的指令碼分成更小的部分,更容易理解和重用。
定義函式
要建立一個函式,可以使用 function 關鍵字,後跟一個名稱、一組括號和一個程式碼塊。函式的命名慣例與變數相同——使用一個描述性的名稱,除了第一個單詞以外,每個單詞都要大寫。
function myFunc()
{
document.write("Hello World");
}
這個函式只是向 web 文件顯示一個文字字串。函式程式碼塊可以包含任何 JavaScript 程式碼,包括其他函式定義。
呼叫函式
一旦定義了一個函式,就可以在文件的任何地方呼叫它,只需鍵入它的名字,後面加上一組括號。函式名區分大小寫,所以字母的大小寫需要一致。
myFunc(); // "Hello World"
即使函式定義稍後出現在指令碼中,也可以呼叫函式。這是因為 JavaScript 中的宣告是在程式碼執行之前處理的。
foo(); // ok
function foo() {}
函式引數
函式名後面的括號用於向函式傳遞引數。為此,必須首先將相應的引數新增到函式的引數列表中。這些引數可以作為常規變數在函式中使用。
function sum(a, b) {
var sum = a + b;
console.log(sum);
}
一個函式可以被定義為接受任意數量的引數。呼叫該函式時,引數以逗號分隔列表的形式提供。在此示例中,該函式接受兩個數值引數,並顯示它們的總和。
sum(2, 3); // "5"
像變數一樣,函式引數也沒有在宣告中指定型別。因此,不會自動執行型別檢查,函式引數也不限於任何特定的資料型別。
可變引數列表
允許呼叫一個函式,其引數個數與定義的引數個數不同。如果呼叫函式時使用的引數較少,那麼剩餘的引數將被設定為未定義。
function say(message) {
console.log(message);
}
say(); // "undefined"
當呼叫一個函式的引數比它的定義中的多時,多餘的引數將沒有名字。可以透過類似陣列的 arguments 物件引用這些額外的引數,該物件包含傳遞給函式的所有引數。
function say() {
console.log(arguments[0]);
}
say("Hello"); // "Hello"
arguments 物件可用於建立 varadic 函式,這些函式能夠處理不同數量的引數。舉個例子,可以將任意數量的引數傳遞給下面的函式。該函式遍歷引數,並將它們組合成一個字串,輸出到控制檯。
function combine() {
var result = "";
for (var i = 0; i < arguments.length; i++) {
result += arguments[i];
}
console.log(result);
}
combine(1, 2, 3); // "123";
返回語句
Return 是一個跳轉語句,它使函式退出,並將指定的值返回到呼叫該函式的地方。舉例來說,下面的函式返回其兩個引數的和。這個函式又可以作為引數傳遞給另一個函式,在那裡它將計算出結果數。
function getSum(a, b) {
return a + b; // exit function and return value
}
console.log( getSum(1, 2) ); // "3"
return 語句也可以用來在到達 end 塊之前退出函式,而不返回任何特定的值。沒有返回值的函式將隱式返回 undefined。
function foo() {
return; // exit function
}
console.log( foo() ); // "undefined"
就像變數和引數一樣,返回值不進行型別檢查。函式需要被適當地文件化,以便函式的使用者知道他們的輸入和輸出應該是什麼。
引數傳遞
引數透過值傳遞給函式。對於基本型別,這意味著只有值的副本被傳遞給函式。因此,以任何方式更改引數都不會影響原始變數。
function set(y) {
y = 1;
}
var x = 0;
set(x); // copy of value passed
console.log(x); // "0"
當物件型別用作引數時,傳遞的是對該物件的引用。這允許函式對原始物件的屬性進行更改。
function addFruit(basket) {
basket[0] = "Apple";
}
var fruits = [];
addFruit(fruits); // copy of reference passed
console.log( fruits[0] ); // "Apple"
將新物件賦給引數不會影響函式外部的原始物件。這是因為賦值改變了引數的值,而不是物件的一個屬性值。
function makeFruit(basket) {
basket = [ "Apple" ];
}
var fruits = [];
makeFruit(fruits);
console.log( fruits[0] ); // "undefined"
函式表示式
JavaScript 中的函式是物件,特別是函式物件。因此,它們可以被賦給變數並傳遞給其他函式,就像任何其他物件一樣。當一個函式以這種方式使用時,它被稱為函式表示式,而不是函式宣告。可以使用正常的賦值語法(包括分號)將函式表示式賦給變數。
var say = function foo(message)
{
console.log("Hello " + message);
};
當呼叫一個函式物件時,引用的是變數名而不是函式名。
say("World"); // "Hello World"
可以在函式內部使用函式名來引用它自己,但是函式名在其他情況下是不必要的。因此,該名稱通常被省略。然後,函式表示式被稱為匿名函式。
var say = function(message)
{
console.log("Hello " + message);
};
函式表示式通常被用作回撥函式,或者被傳遞給其他函式,或者從其他函式返回。它們允許非常簡潔地編寫程式碼,因為函式可以內聯,而不必在其他地方定義它。為了說明這一點,下面的例子使用了window.setTimeout
函式,它採用另一個函式和一個數字作為引數。該數字指定在呼叫函式引數之前等待的毫秒數。
// Call anonymous function after one second
window.setTimeout(function() { console.log("Hello") }, 1000);
範圍和壽命
變數的作用域指的是可以使用該變數的程式碼區域。JavaScript 中的變數既可以全域性宣告,也可以區域性宣告。全域性變數是在任何函式之外宣告的,可以從文件中的任何地方訪問。另一方面,區域性變數是在函式內部宣告的,並且只能在該函式內部訪問。
var globalVar = 0; // global variable
function foo() {
var localVar = 0; // local variable
}
區域性變數的生存期是有限的。全域性變數將在指令碼執行期間保持分配狀態,而區域性變數將在其函式執行完畢後被銷燬。
console.log(globalVar); // "0"
foo();
console.log(localVar); // throws a ReferenceError
當作用域中的兩個變數同名時,就會出現名稱衝突。更多的內部作用域具有優先權,因此最裡面的作用域具有最高的優先權,而最外面的作用域具有最低的優先權。
var a = "global";
function foo() {
var a = "local"; // overshaddows global variable
console.log(a);
}
foo(); // "local"
console.log(a); // "global"
與許多其他語言不同,JavaScript 中的程式碼塊沒有自己的作用域。因此,在控制結構程式碼塊(如迴圈或條件語句)中定義的變數在程式碼塊結束時不會被銷燬。
if(true) {
var x = 10; // global variable
}
console.log(x); // "10"
還有一種建立變數的替代方法,即在不使用 var 關鍵字的情況下為未宣告的變數賦值。這將隱式地將變數宣告為全域性變數,即使它是在函式中宣告的。
function foo() {
a = 5; // global variable
}
foo();
console.log(a); // "5"
以這種方式錯誤地引入或覆蓋全域性變數是常見的錯誤來源。因此,建議始終使用 var 關鍵字顯式宣告變數。
var a = 10;
foo(); // replaces value of global variable
console.log(a); // "5"
像函式宣告一樣,顯式變數宣告也在指令碼執行之前進行處理。因此,變數可以在宣告之前在程式碼中引用。
console.log(a); // "undefined"
var a = "defined";
console.log(a); // "defined"
對於隱式宣告的變數來說,這種行為是不同的,因為在為變數賦值的程式碼執行之前,這些變數是不存在的。
console.log(b); // throws a ReferenceError
b = "defined"; // never executes
九、物件
物件是被稱為屬性的命名值的集合。屬性可以是儲存物件狀態的變數,也可以是定義物件功能的函式。物件的吸引力在於它們在提供功能的同時隱藏了它們的內部工作。你需要知道的只是一個物件能為你做什麼,而不是它是如何做的。
物件屬性
可以使用 new 指令以下面的顯式方式建立一個空物件。
var box = new Object();
使用點符號或陣列符號將物件的屬性賦給時,會自動建立這些屬性。
box.x = 2;
box["y"] = 3;
同樣,屬性可以用這兩種方式之一引用。
console.log(box.x); // "2"
console.log(box["y"]); // "3"
可以使用 delete 指令從物件中刪除屬性。這不能用常規變數來完成,只能用物件屬性來完成。
box.z = 1; // add property
delete box.z; // delete property
要檢查物件是否包含屬性,可以使用 in 運算子。然後,屬性名被指定為字串。
console.log("x" in box); // "true"
console.log("z" in box); // "false"
物件方法
函式可以以屬性的形式新增到物件中。這種功能被稱為方法。當引用函式宣告時,括號被省略。
box.getArea = myArea;
function myArea() { return this.x * this.y; }
這裡使用的this
關鍵字是對當前擁有該函式的物件的引用。如果在物件上下文之外呼叫函式,關鍵字將改為引用window
物件。為了防止這種脫離上下文的呼叫,最好使用函式表示式行內函數。這也防止了函式名不必要地弄亂了全域性名稱空間。
box.getArea = function() { return this.x * this.y; };
一旦繫結到物件,就可以以熟悉的方式呼叫該方法。this
關鍵字在這裡指的是 box 物件,它具有前面定義的屬性x
和y
。
console.log( box.getArea() ); // "6"
物件文字
建立物件的一種更簡單的方法是使用物件文字,它由一組花括號分隔。建立空物件時,括號是空的。
var box = {};
使用物件文字的優點是,可以在建立物件時設定屬性,方法是將它們包含在花括號中。屬性的每個名稱-值對由冒號分隔,每個屬性依次由逗號分隔。
var box = {
x: 2,
y: 3,
getArea: function() { return this.x * this.y; }
};
如果只需要一個物件例項,那麼物件文字就很有用。如果需要多個例項,可以使用函式建構函式。
建構函式
可以從建構函式中建立物件。透過允許從一組定義中建立一個物件的多個例項,它們提供了其他語言中的類所提供的功能。按照慣例,打算用作物件建構函式的函式以大寫字母開頭,以提醒它們的用途。
function Box(x, y) {
this.x = x;
this.y = y;
this.getArea = function() { return this.x * this.y; };
}
為了從這個建構函式中例項化一個或多個物件,用new
指令呼叫這個函式。就像任何其他函式一樣,建構函式可以接受引數,如下例所示。
var b1 = new Box(1, 2);
var b2 = new Box(3, 4);
每個物件例項都包含自己的一組屬性,這些屬性可以儲存與其他例項不同的值。
console.log(b1.x); // "1"
console.log(b2.x); // "3"
先前定義的建構函式包括在另一個函式中宣告的函式。這種巢狀函式可以訪問其父函式中定義的變數,因為它形成了一個包含外部函式範圍的所謂閉包。這種物件建立模式的一個優點是它提供了資訊隱藏。例如,下面的示例有一個方法,它使用一個區域性變數來記錄該方法被呼叫的次數。這個特性類似於基於類的語言中的私有屬性,因為區域性變數只在建構函式中可見。
function Counter(x, y) {
var count = 0;
this.printCount = function() { console.log(count++); };
}
var c = new Counter();
c.printCount(); // "0";
c.printCount(); // "1";
這種物件建立模式的一個小缺點是每個例項都有自己的printCount
方法,這會增加每個物件消耗的記憶體。避免這種情況的另一種模式是利用繼承將方法新增到物件的原型中。
遺產
一個物件可以從另一個物件繼承屬性。這為物件重用其他物件的程式碼提供了一種方式。專門化的物件通常稱為子物件,更一般的物件稱為父物件。
在 JavaScript 中,繼承是透過原型繼承模型實現的。在這個模型中,每個物件都有一個內部屬性,作為到另一個物件的連結,稱為它的原型。考慮下面的物件。
var myObj = new Object();
這個物件從內建的Object
建構函式繼承屬性。當請求一個物件不直接包含的屬性時,JavaScript 將自動搜尋繼承鏈,直到找到請求的屬性或到達鏈的末端。
// Add a property to myObj
myObj.x = 5;
// Call a method from Object
myObj.hasOwnProperty("x"); // true
鏈中的最後一個連結總是內建的Object
建構函式,它的原型連結又是 null,這標誌著鏈的結束。可以使用__proto__
屬性檢索對物件原型連結的引用。這不要與建構函式的 prototype 屬性混淆,它是函式的文字物件表示,當建立該函式的新例項時,它被分配給__proto__
。
myObj.__proto__ === Object.prototype; // true
myObj.__proto__.__proto__ === null; // true
透過擴充套件原型鏈來實現繼承。這可以在建構函式中完成,方法是將prototype
屬性設定為要繼承的物件。
function Parent() { this.a = "Parent"; }
function Child() { }
Child.prototype = new Parent();
var child = new Child();
console.log(child.a); // "Parent"
型別檢查
您可以透過比較物件的__proto__
連結和建構函式的prototype
屬性來手動確認物件的型別。
child.__proto__ === Child.prototype; //
true
JavaScript 還提供了instanceof
運算子。該運算子在原型鏈中導航,如果左側物件指向原型鏈中右側的建構函式,則返回 true。
child instanceof Child; // true
child instanceof Parent; // true
child instanceof Object; // true
物件建立
執行繼承的另一種方式是透過Object.create
方法。此方法提供了一種更簡單的實現繼承的方法,它允許一個物件直接從另一個物件繼承,而無需使用額外的建構函式。
var p = new Parent();
var c1 = Object.create(p); // inherit from p
方法的第二個可選引數允許您使用特殊的表示法初始化物件的屬性。
var c2 = Object.create(p, { name: { value: "Child 2" } } );
console.log(c2.name); // "Child 2"
物件的prototype
屬性可以用來動態地向原型新增屬性。這將導致連結到該原型的所有物件都繼承新的屬性。
Parent.prototype.x = "new property";
console.log(c2.x); // "new property"
如本章所示,JavaScript 在建立和使用物件時提供了很大的靈活性。選擇使用哪種方法往往歸結為個人喜好的問題。
十、文件物件模型
文件物件模型或 DOM 是一個程式設計介面,它描述了 web 文件的所有元素以及它們之間的相互關係。透過這個介面,JavaScript 程式碼可以與 web 文件進行互動。
DOM 節點
在 DOM 模型中,web 文件的內容以由節點組成的樹狀結構表示。透過了解這些節點是如何組織的,就有可能動態地更改文件的任何部分。考慮下面的 HTML 標記。
<p>My paragraph</p>
這個標記在 DOM 樹中建立了兩個節點:一個元素節點和一個文字節點。元素節點可以有子節點,這裡的文字節點是段落元素的子節點。相反,段落節點是文字節點的父節點。轉到下一個示例,有四個節點。
<div id="mydiv">
<!-- My comment -->
</div>
div 元素節點包含三個節點:兩個文字節點和一個註釋節點。額外的文字節點來自本例中註釋前後的空格(空格、製表符和換行符)。檢視網頁時,這四個節點都是不可見的。
每個元素可以有一個可選的 id 屬性,在本例中應用於 div 元素。因為這個屬性必須是惟一的,所以它提供了一種在 DOM 樹中選擇特定節點的便捷方式。注意,DOM 規範也認為屬性是節點;但是,在導航 DOM 樹時,屬性節點被視為元素節點的屬性。
選擇節點
document 物件表示 web 文件,並提供了幾種訪問 DOM 樹和檢索節點引用的方法。最常見的方法是使用getElementById
方法。此方法返回對具有指定唯一 id 的元素的引用。使用這個方法,可以檢索前面示例中的 div 元素節點。
var mydiv = document.getElementById("mydiv");
也可以用getElementsByTagName
方法透過標籤名選擇元素。此方法檢索該型別的所有元素的類似陣列的集合。
var mydivs = document.getElementsByTagName("div");
另一種不太常見的選擇元素的方式是透過class
屬性。由於多個元素可以共享同一個類名,因此該方法也返回一個集合。除 IE < 9 之外,所有現代瀏覽器都支援它。
var myclasses = document.getElementsByClassName("myclass");
getElementById
方法只能從文件物件中呼叫,但是另外兩個方法可以從 DOM 樹中的特定節點呼叫,以便只搜尋該節點的子節點。例如,下面的程式碼檢索 body 元素下的所有元素節點。
var myelements = document.body.getElementsByTagName("*");
除了 body 節點,document 物件還具有檢索對html
和head
節點的引用的屬性。
var htmlnode = document.documentElement;
var headnode = document.head;
var bodynode = document.body;
也可以使用querySelector
方法定位元素節點。該方法返回匹配指定 CSS 選擇器的第一個元素,在本例中是 class 屬性設定為myclass
的第一個元素。
var mynode = document.querySelector(".myclass");
類似地,querySelectorAll
方法檢索匹配給定 CSS 查詢的所有元素節點的集合。除了 IE < 9,所有現代瀏覽器都支援這兩種查詢方式。
var mynodes = document.querySelectorAll(".myclass");
如果找不到元素,則返回 null。這種行為在所有 DOM 方法中都是一致的。
遍歷 DOM 樹
一旦選擇了一個節點,就有許多屬性允許相對於該節點遍歷 DOM 樹。下面的列表可以用來說明。
<ul id="mylist">
<li>First</li>
<li>Second</li>
<li>Third</li>
</ul>
對這個無序列表節點的引用是透過它的 id 來檢索的。
var mylist = document.getElementById("mylist");
此節點下的元素節點可透過 children 集合獲得。第一個列表項也可以使用firstElementChild
屬性來檢索。
var first = mylist.children[0];
first = mylist.firstElementChild;
同樣,可以透過 children 集合或lastElementChild
屬性訪問最後一個節點。
var third = mylist.children[2];
third = mylist.lastElementChild;
可以使用previousElementSibling
和nextElementSibling
屬性將 DOM 樹左右導航到相鄰節點。在這種情況下,兄弟指的是共享同一個父節點的節點。
var second = first.nextElementSibling;
second = third.previousElementSibling;
這四個屬性——firstElementChild
、lastElementChild
、nextElementSibling
和previousElementSibling
——除了 IE < 9,所有現代瀏覽器都支援。對於完整的瀏覽器支援,可以使用以下屬性來代替:firstChild
、lastChild
、nextSibling
和previousSibling
。請記住,這些屬性也考慮文字和註釋節點,而不僅僅是元素節點。
parentNode
屬性引用父節點。與其他屬性一起,它們允許在所有四個方向上遍歷 DOM 樹:上、下、左、右。
mylist = first.parentNode;
子集合只包含元素節點。一個例外是在 9 之前的 IE 版本中,這個集合也包括註釋節點。如果需要所有節點型別,則使用childNodes
集合。該集合包含所有子節點,包括元素、文字和註釋節點。再一次,IE < 9 的行為與其他瀏覽器不同,在childNodes
集合中沒有包含純空白的文字節點。
mylist.childNodes[0]; // whitespace text node
mylist.childNodes[1]; // li node
mylist.childNodes[2]; // whitespace text node
對於元素節點,nodeName
和tagName
屬性都包含大寫字母的標籤名稱。它們可以用來測試元素節點的標記名。
if (mydiv.nodeName == "DIV")
console.log(mydiv.tagName); // "DIV"
雖然tagName
屬性專門用於元素節點,但是nodeName
屬性對於任何節點型別都是有用的。例如,註釋節點的計算結果為“#comment”,文字節點的計算結果為“#text”。
mylist.childNodes[0].nodeName; // #text
mylist.childNodes[1].nodeName; // LI
mylist.childNodes[2].nodeName; // #text
建立節點
DOM 中的節點可以動態地新增、刪除或更改。舉例來說,一個新的列表項將被新增到先前的列表中。第一步是分別使用createElement
和createTextNode
方法建立元素和文字節點。
var myitem = document.createElement('li');
var mytext = document.createTextNode("New list item");
然後,使用 list item 節點上的appendChild
方法,將文字節點新增到元素節點。
myitem.appendChild(mytext);
接下來,使用相同的方法將列表項節點新增到無序列表中。這將導致新列表項出現在頁面上。
mylist.appendChild(myitem);
appendChild
方法新增它的節點引數作為呼叫它的元素節點的最後一個子節點。為了在其他地方插入節點,使用了insertBefore
方法。此方法採用第二個節點引數,在該引數之前插入新節點。
mylist.insertBefore(myitem, mylist.children[0]);
一個節點不能同時出現在兩個地方,因此該操作會刪除之前新增到列表末尾的節點,而將其放在列表的開頭。要新增另一個類似的節點,可以首先使用cloneNode
方法複製元素。此方法採用一個布林引數,該引數指定元素的後代節點是否也將被複制。
var newitem = myitem.cloneNode(false);
接下來,這個節點被新增到列表的末尾,但是因為它是在沒有後代的情況下克隆的,所以它沒有文字節點,所以在文件中顯示為空列表元素。
mylist.appendChild(newitem);
如前所示,可以使用createTextNode
和appendChild
方法建立和連結文字節點。一個更短的替代方法是修改innerHTML
屬性,它代表元素的 HTML 內容。
newitem.innerHTML = "<b>Another</b> new list item";
該屬性允許自動建立元素和文字節點。另一個有用的類似屬性是textContent
。此屬性表示去除了任何 HTML 標記的元素內容。
newitem.textContent; // "Another new list item"
newitem.innerHTML; // "<b>Another</b> new list item"
刪除節點
可以使用removeChild
方法刪除一個節點。該方法返回對已移除節點的引用:在本例中,是列表中的最後一個子節點。請記住,在 JavaScript 中捕捉返回值是可選的。
var removedNode = mylist.removeChild(mylist.lastElementChild);
另一種刪除節點的方法是用不同的節點替換它。這是透過replaceChild
方法完成的,該方法也返回被替換的節點。下面的程式碼用以前移除的節點替換列表的第一個子節點。
mylist.replaceChild(removedNode, mylist.firstElementChild);
屬性節點
屬性節點可以透過其包含元素來訪問,而不是作為該元素的子節點。為了便於說明,這裡有一個帶有id
屬性的段落,以便於選擇。
<p id="myid">My paragraph</p>
它的元素節點以熟悉的方式選擇。
var mypara = document.getElementById("myid");
setAttribute
方法向被引用的元素新增一個屬性,或者替換它的值,如果它已經存在的話。它有兩個引數:屬性和值。
mypara.setAttribute("class","myclass");
為了檢索屬性的值,使用了getAttribute
方法。在檢索屬性之前,這裡使用hasAttribute
方法執行檢查以確保它存在。
if (mypara.hasAttribute("class"))
console.log(mypara.getAttribute("class")); // "myclass"
屬性自動與元素節點的屬性同步。這為設定和獲取屬性提供了一種不太冗長的方式。屬性和它們對應的屬性共享相同的名稱,除了 class 屬性的屬性名為className
。這是因為 class 是 JavaScript 中的保留關鍵字。
console.log(mypara.id); // "myid"
console.log(mypara.className); // "myclass"
使用style
屬性可以動態改變元素的 CSS 屬性。一種方法是直接修改屬性。這將覆蓋以前透過該屬性設定的任何內聯樣式。
mypara.setAttribute("style", "background-color: yellow;");
相反,要給元素新增新的樣式,可以改變style
物件的屬性。這些屬性與其對應的 CSS 屬性具有相同的名稱,只是刪除了所有連字元,並且第一個單詞後面的每個單詞都大寫。
mypara.style.backgroundColor = "yellow";
要恢復樣式更改,您只需透過將該屬性設定為空字串來清除它。
mypara.style.backgroundColor = "";
十一、事件
事件是在使用者、網頁和瀏覽器之間的互動中發生的事件。事件處理使指令碼能夠檢測這些事件並做出反應,從而使網頁變得具有互動性。
事件處理
處理事件有三個步驟。首先,您需要定位將接收事件的元素。事件總是發生在 DOM 樹中元素節點的上下文中。在此示例中,當單擊以下超連結時,將發生該事件。
<a href="
http://www.google.com
下一步是建立事件處理程式,這是事件發生時將執行的程式碼。該事件處理程式通常由一個函式組成,在這種情況下,該函式為使用者顯示一個帶有訊息的警告框。
function myEventHandler() {
alert("Event triggered");
}
最後,最後一步是為要處理的特定事件註冊事件處理程式。這裡選擇了 link 元素節點,事件處理程式以下面的方式註冊到它的onclick
事件中。
var mylink = document.getElementById("mylink");
mylink.onclick = myEventHandler;
當使用者點選這個連結時,呼叫事件處理程式,彈出警告框。一旦盒子被關閉,預設的事件處理程式就會接管,連結就會像平常一樣被跟蹤。若要移除事件處理程式,請將屬性設定為 null。
mylink.onclick = null;
這被稱為事件註冊的傳統模型。另一種更短的註冊方法是內聯模型,它使用事件屬性將事件處理程式直接附加到產生事件的 HTML 元素上。
<a href="
http://www.google.com
請注意,事件處理程式的括號包含在內聯模型中,而不是傳統模型中。使用這個模型的一個更簡單的方法是內聯這個函式。
<a href="
http://www.google.com
內聯事件處理程式可以包含多個語句。在下面的示例中,return false 語句被新增到事件處理程式中。這將阻止預設的瀏覽器操作發生,在這種情況下,這意味著該連結將不再被跟蹤。
<a href="
http://www.google.com
所有現代瀏覽器都支援傳統模型和內聯模型。傳統模型通常更可取,因為它允許透過程式碼新增、更改和刪除事件處理程式,並且它完全將 JavaScript 與 HTML 分開。
W3C 在 DOM level 2 規範中標準化了第三種註冊事件的模型。在這個 W3C 模型中,使用addEventListener
方法新增了一個事件處理程式。這個方法有三個引數:事件型別、事件處理程式和一個布林值,我們將在後面看到。
mylink.addEventListener("click", myEventHandler, false);
注意,在 W3C 模型中,事件的字首“on”被省略了,所以“onclick”就變成了“click”要刪除一個事件處理程式,removeEventListener
與同樣的三個引數一起使用。
mylink.removeEventListener("click", myEventHandler, false);
W3C 模型的主要優點是可以為同一個事件和同一個元素節點註冊多個事件處理程式。一個缺點是 IE<9 不支援,這使得它比其他兩種方法的跨瀏覽器相容性差。
事件物件
當事件被觸發時,瀏覽器向事件處理程式傳遞一個引數,將事件表示為一個物件。可以透過向事件處理程式新增引數來訪問該物件。
function myEventHandler(e) { }
這個物件是訪問事件資訊的 W3C 方法。IE<9 沒有傳遞事件物件引數,而是有一個全域性的window.event
物件,代表最後觸發的事件。為了跨瀏覽器的相容性,可以將下面一行新增到處理程式的開頭,以確保在所有瀏覽器中都能檢索到正確的事件物件。
function myEventHandler(e) {
if (!e) var e = window.event;
}
事件物件透過其屬性提供關於事件的附加資訊。不同的事件有不同的屬性,但是所有的事件物件都有type
屬性。該屬性儲存一個標識事件的字串,例如 onclick 事件的“click”。
console.log(e.type); // "click"
大多數事件也有一個目標,它是對觸發事件的元素節點的引用。在前面的例子中,這指的是錨元素。
console.log(e.target.tagName); // "A"
在 IE<9 上,事件物件有一個srcElement
屬性,而不是target
屬性。這裡可以看到跨瀏覽器相容的目標檢索方式。
var target = e.target || e.srcElement;
事件傳播
大多數 DOM 事件都有事件傳播,這意味著內部元素觸發的事件也會觸發外部元素。舉例來說,下面是一個巢狀在div
元素中的段落元素。
<div id="outer">Outer element
<p id="inner">Inner element</div>
</div>
下面的程式碼使用傳統模型為這兩個元素註冊 click 事件。
var inner = document.getElementById("inner");
inner.onclick = function() { alert("Inner"); }
var outer = document.getElementById("outer");
outer.onclick = function() { alert("Outer"); }
在內部元素上觸發一個事件後,它繼續觸發以巢狀順序附加到父元素的任何事件處理程式。因此,單擊內部元素將首先顯示“內部”訊息,然後是“外部”訊息。這種預設的事件順序稱為冒泡。
相反的事件順序稱為捕獲。IE<9 只有冒泡順序,但其他所有現代瀏覽器都是先捕獲再冒泡的方式處理事件處理程式。為了註冊捕獲階段的事件處理程式,addEventListener
的最後一個引數被設定為 true。在下面的示例中,當單擊段落元素時,數字將按順序列印。
outer.addEventListener("click", function() { console.log("1"); }, true);
inner.addEventListener("click", function() { console.log("2"); }, true);
inner.addEventListener("click", function() { console.log("3"); }, false);
outer.addEventListener("click", function() { console.log("4"); }, false);
當事件處理程式被觸發時,它有機會透過呼叫事件物件上的stopPropagation
方法來防止事件進一步傳播。在 IE < 9 中,事件的cancelBubble
屬性需要改為設定為 true。下面是一種跨瀏覽器相容的阻止事件傳播的方法。
function cancelEvent(e) {
if (!e) var e = window.event;
e.cancelBubble = true;
if (e.stopPropagation) e.stopPropagation();
}
DOM 事件
現代瀏覽器支援很多 DOM 事件。以下是您可能會遇到的最常見事件的列表。
| 事件名稱 | 描述 | | --- | --- | | `onClick` | 當使用者單擊一個元素時觸發。可以應用於任何可見的元素。 | | `onLoad` | 當頁面完成載入時在視窗上觸發。需要外部物件的元素,比如``、``、`