JavaScript 基礎知識 1
#JavaScript 基礎知識
##Hello, world!
“script” 標籤
JavaScript 程式可以在 <script> 標籤的幫助下插入到 HTML 文件的任何地方。
<script>
alert('Hello, world!');
</script>
<script> 標籤中包裹了 JavaScript 程式碼,當瀏覽器遇到 <script> 標籤,程式碼會自動執行。
外部指令碼
指令碼檔案可以通過 src 特性(attribute)新增到 HTML 檔案中。
<script src="/path/to/script.js"></script>
這裡,/path/to/script.js 是指令碼檔案從網站根目錄開始的絕對路徑。當然也可以提供當前頁面的相對路徑。例如,src ="script.js" 表示當前資料夾中的 "script.js" 檔案。
也可以提供一個完整的 URL 地址,例如:
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
要附加多個指令碼,請使用多個標籤:
<script src="/js/script1.js"></script>
<script src="/js/script2.js"></script>
注意
如果設定了 src 特性,script 標籤內容將會被忽略。
一個單獨的 <script> 標籤不能同時有 src 特性和內部包裹的程式碼。
<script src="file.js">
alert(1); // 此內容會被忽略,因為設定了 src
</script>
為了讓上面的例子工作,我們可以將它分成兩個 <script> 標籤。
<script src="file.js"></script>
<script>
alert(1);
</script>
程式碼結構
我們將要學習的第一個內容就是構建程式碼塊。
語句
語句是執行行為(action)的語法結構和命令。
語句之間可以使用分號進行分割。
例如,我們將 “Hello World” 這條資訊一分為二:
alert('Hello'); alert('World');
註釋
單行註釋以兩個正斜槓字元 // 開始。
多行註釋以一個正斜槓和星號開始 “/*” 並以一個星號和正斜槓結束 “*/”。
使用熱鍵!
在大多數的編輯器中,一行程式碼可以使用 Ctrl+/ 熱鍵進行單行註釋,諸如 Ctrl+Shift+/ 的熱鍵可以進行多行註釋(選擇程式碼,然後按下熱鍵)。對於 Mac 電腦,應使用 Cmd 而不是 Ctrl,使用 Option 而不是 Shift。
不支援註釋巢狀!
不要在 /*...*/ 內巢狀另一個 /*...*/。
現代模式,"use strict"
“use strict”
這個指令看上去像一個字串 "use strict"; 或者 'use strict'; 。當它處於指令碼檔案的頂部時,則整個指令碼檔案都將以“現代”模式進行工作。
變數
變數
變數 是資料的“命名儲存”。
在 JavaScript 中建立一個變數,我們需要用到 let 關鍵字。
下面的語句建立(也可以稱為 宣告 或者 定義)了一個名稱為 “message” 的變數:
let message;
message = 'Hello!';
let message = 'Hello!';
也可以在一行中宣告多個變數:
let user = 'John', age = 25, message = 'Hello';
當變數值改變的時候,之前的資料就被從變數中刪除了:
還可以宣告兩個變數,然後將其中一個變數的資料拷貝到另一個變數。
let hello = 'Hello world!';
let message;
// 將字串 'Hello world' 從變數 hello 拷貝到 message
message = hello;
// 現在兩個變數儲存著相同的資料
alert(hello); // Hello world!
alert(message); // Hello world!
宣告兩次會觸發 error
一個變數應該只被宣告一次。
對同一個變數進行重複宣告會觸發 error:
因此,我們對同一個變數應該只宣告一次,之後在不使用 let 的情況下對其進行引用。
變數命名
JavaScript 的變數命名有兩個限制:
變數名稱必須僅包含字母,數字,符號 $ 和 _。
首字元必須非數字。
如果命名包括多個單詞,通常採用駝峰式命名法(camelCase)
常量
宣告一個常數(不變)變數,可以使用 const 而非 let:
const myBirthday = '18.04.1982';
使用 const 宣告的變數稱為“常量”。它們不能被修改,如果你嘗試修改就會發現報錯:
#資料型別
在 JavaScript 中有 8 種基本的資料型別(譯註:7 種原始型別和 1 種引用型別)。
Number 型別
number 型別代表整數和浮點數。
數字可以有很多操作,比如,乘法 *、除法 /、加法 +、減法 - 等等。
除了常規的數字,還包括所謂的“特殊數值(“special numeric values”)”也屬於這種型別:Infinity、-Infinity 和 NaN。
Infinity 代表數學概念中的 無窮大 ∞。是一個比任何數字都大的特殊值。
NaN 代表一個計算錯誤。它是一個不正確的或者一個未定義的數學操作所得到的結果
NaN 是粘性的。任何對 NaN 的進一步操作都會返回 NaN:
alert( "not a number" / 2 + 5 ); // NaN
BigInt 型別
可以通過將 n 附加到整數字段的末尾來建立 BigInt 值。
// 尾部的 "n" 表示這是一個 BigInt 型別
const bigInt = 1234567890123456789012345678901234567890n;
String 型別
JavaScript 中的字串必須被括在引號裡。
在 JavaScript 中,有三種包含字串的方式。
雙引號:"Hello".
單引號:'Hello'.
反引號:`Hello`.
雙引號和單引號都是“簡單”引用,在 JavaScript 中兩者幾乎沒有什麼差別。
反引號是 功能擴充套件 引號。它們允許我們通過將變數和表示式包裝在 ${…} 中,來將它們嵌入到字串中。例如:
let name = "John";
// 嵌入一個變數
alert( `Hello, ${name}!` ); // Hello, John!
// 嵌入一個表示式
alert( `the result is ${1 + 2}` ); // the result is 3
Boolean 型別(邏輯型別)
boolean 型別僅包含兩個值:true 和 false。
“null” 值
特殊的 null 值不屬於上述任何一種型別。
它構成了一個獨立的型別,只包含 null 值:
let age = null;
“undefined” 值
特殊值 undefined 和 null 一樣自成型別。
undefined 的含義是 未被賦值。
如果一個變數已被宣告,但未被賦值,那麼它的值就是 undefined:
let age;
alert(age); // 彈出 "undefined"
object 型別和 symbol 型別
object 型別是一個特殊的型別。symbol 型別用於建立物件的唯一識別符號。
typeof 運算子
typeof 運算子返回引數的型別。
它支援兩種語法形式:
作為運算子:typeof x。
函式形式:typeof(x)。
總結
JavaScript 中有八種基本的資料型別(譯註:前七種為基本資料型別,也稱為原始型別,而 object 為複雜資料型別)。
number 用於任何型別的數字:整數或浮點數,在 ±(253-1) 範圍內的整數。
bigint 用於任意長度的整數。
string 用於字串:一個字串可以包含 0 個或多個字元,所以沒有單獨的單字元型別。
boolean 用於 true 和 false。
null 用於未知的值 —— 只有一個 null 值的獨立型別。
undefined 用於未定義的值 —— 只有一個 undefined 值的獨立型別。
symbol 用於唯一的識別符號。
object 用於更復雜的資料結構。
我們可以通過 typeof 運算子檢視儲存在變數中的資料型別。
兩種形式:typeof x 或者 typeof(x)。
以字串的形式返回型別名稱,例如 "string"。
typeof null 會返回 "object" —— 這是 JavaScript 程式語言的一個錯誤,實際上它並不是一個 object。
#互動:alert、prompt 和 confirm
alert
alert("Hello");
prompt
prompt 函式接收兩個引數:
result = prompt(title, [default]);
瀏覽器會顯示一個帶有文字訊息的模態視窗,還有 input 框和確定/取消按鈕。
title
顯示給使用者的文字
default
可選的第二個引數,指定 input 框的初始值。
語法中的方括號 [...]
上述語法中 default 周圍的方括號表示該引數是可選的,不是必需的。
confirm
語法:
result = confirm(question);
confirm 函式顯示一個帶有 question 以及確定和取消兩個按鈕的模態視窗。
點選確定返回 true,點選取消返回 false。
例如:
let isBoss = confirm("Are you the boss?");
alert( isBoss ); // 如果“確定”按鈕被按下,則顯示 true
總結
我們學習了與使用者互動的 3 個瀏覽器的特定函式:
alert
顯示資訊。
prompt
顯示資訊要求使用者輸入文字。點選確定返回文字,點選取消或按下 Esc 鍵返回 null。
confirm
顯示資訊等待使用者點選確定或取消。點選確定返回 true,點選取消或按下 Esc 鍵返回 false。
這些方法都是模態的:它們暫停指令碼的執行,並且不允許使用者與該頁面的其餘部分進行互動,直到視窗被解除。
上述所有方法共有兩個限制:
模態視窗的確切位置由瀏覽器決定。通常在頁面中心。
視窗的確切外觀也取決於瀏覽器。我們不能修改它。
#型別轉換
總結
有三種常用的型別轉換:轉換為 string 型別、轉換為 number 型別和轉換為 boolean 型別。
字串轉換 —— 轉換髮生在輸出內容的時候,也可以通過 String(value) 進行顯式轉換。原始型別值的 string 型別轉換通常是很明顯的。
數字型轉換 —— 轉換髮生在進行算術操作時,也可以通過 Number(value) 進行顯式轉換。
數字型轉換遵循以下規則:
值 變成……
undefined NaN
null 0
true / false 1 / 0
string “按原樣讀取”字串,兩端的空白會被忽略。空字串變成 0。轉換出錯則輸出 NaN。
布林型轉換 —— 轉換髮生在進行邏輯操作時,也可以通過 Boolean(value) 進行顯式轉換。
布林型轉換遵循以下規則:
值 變成……
0, null, undefined, NaN, "" false
其他值 true
上述的大多數規則都容易理解和記憶。人們通常會犯錯誤的值得注意的例子有以下幾個:
對 undefined 進行數字型轉換時,輸出結果為 NaN,而非 0。
對 "0" 和只有空格的字串(比如:" ")進行布林型轉換時,輸出結果為 true。
#基礎運算子,數學
術語:“一元運算子”,“二元運算子”,“運算元”
運算元 —— 運算子應用的物件。比如說乘法運算 5 * 2,有兩個運算元:左運算元 5 和右運算元 2。
如果一個運算子對應的只有一個運算元,那麼它是 一元運算子。
如果一個運算子擁有兩個運算元,那麼它是 二元運算子。減號還存在二元運算子形式:
數學
支援以下數學運算:
加法 +,
減法 -,
乘法 *,
除法 /,
取餘 %,
求冪 **.
前四個都很簡單,而 % 和 ** 則需要說一說。
取餘 %
取餘運算子是 %,儘管它看起來很像百分數,但實際並無關聯。
a % b 的結果是 a 整除 b 的 餘數)。
求冪 **
求冪運算 a ** b 是 a 乘以自身 b 次。
用二元運算子 + 連線字串
通常,加號 + 用於求和。
但是如果加號 + 被應用於字串,它將合併(連線)各個字串:
一個更復雜的例子
alert(2 + 2 + '1' ); // "41",不是 "221"
在這裡,運算子是按順序工作。
第一個 + 將兩個數字相加,所以返回 4,然後下一個 + 將字串 1 加入其中,所以就是 4 + '1' = 41。
二元 + 是唯一一個以這種方式支援字串的運算子。其他算術運算子只對數字起作用,並且總是將其運算元轉換為數字。
下面是減法和除法運算的示例:
alert( 6 - '2' ); // 4,將 '2' 轉換為數字
alert( '6' / '2' ); // 3,將兩個運算元都轉換為數字
數字轉化,一元運算子 +
加號 + 有兩種形式。一種是上面我們剛剛討論的二元運算子,還有一種是一元運算子。
一元運算子加號,或者說,加號 + 應用於單個值,對數字沒有任何作用。但是如果運算元不是數字,加號 + 則會將其轉化為數字。
例如:
// 對數字無效
let x = 1;
alert( +x ); // 1
let y = -2;
alert( +y ); // -2
// 轉化非數字
alert( +true ); // 1
alert( +"" ); // 0
運算子優先順序
表示式 1 + 2 * 2 中,乘法先於加法計算。這就是一個優先順序問題。乘法比加法擁有 更高的優先順序。
圓括號擁有最高優先順序,所以如果我們對現有的運算順序不滿意,我們可以使用圓括號來修改運算順序,就像這樣:(1 + 2) * 2。
在 JavaScript 中有眾多運算子。每個運算子都有對應的優先順序數字。數字越大,越先執行。如果優先順序相同,則按照由左至右的順序執行。
原地修改
我們經常需要對一個變數做運算,並將新的結果儲存在同一個變數中。
可以使用運算子 += 和 *= 來縮寫這種表示。
let n = 2;
n += 5; // 現在 n = 7(等同於 n = n + 5)
n *= 2; // 現在 n = 14(等同於 n = n * 2)
alert( n ); // 14
所有算術和位運算子都有簡短的“修改並賦值”運算子:/= 和 -= 等。
這類運算子的優先順序與普通賦值運算子的優先順序相同,所以它們在大多數其他運算之後執行:
let n = 2;
n *= 3 + 5;
alert( n ); // 16 (右邊部分先被計算,等同於 n *= 8)
自增/自減
自增 ++ 將變數與 1 相加:
let counter = 2;
counter++; // 和 counter = counter + 1 效果一樣,但是更簡潔
alert( counter ); // 3
自減 -- 將變數與 1 相減:
let counter = 2;
counter--; // 和 counter = counter - 1 效果一樣,但是更簡潔
alert( counter ); // 1
重要:
自增/自減只能應用於變數。試一下,將其應用於數值(比如 5++)則會報錯。
運算子 ++ 和 -- 可以置於變數前,也可以置於變數後。
當運算子置於變數後,被稱為“後置形式”:counter++。
當運算子置於變數前,被稱為“前置形式”:++counter。
兩者都做同一件事:將變數 counter 與 1 相加。
那麼它們有區別嗎?有,但只有當我們使用 ++/-- 的返回值時才能看到區別。
如果自增/自減的值不會被使用,那麼兩者形式沒有區別:
let counter = 0;
counter++;
++counter;
alert( counter ); // 2,以上兩行作用相同
如果我們想要對變數進行自增操作,並且 需要立刻使用自增後的值,那麼我們需要使用前置形式:
let counter = 0;
alert( ++counter ); // 1
如果我們想要將一個數加一,但是我們想使用其自增之前的值,那麼我們需要使用後置形式:
let counter = 0;
alert( counter++ ); // 0
++/-- 運算子同樣可以在表示式內部使用。它們的優先順序比絕大部分的算數運算子要高。
位運算子
下面是位運算子:
按位與 ( & )
按位或 ( | )
按位異或 ( ^ )
按位非 ( ~ )
左移 ( << )
右移 ( >> )
無符號右移 ( >>> )
逗號運算子
逗號運算子 , 是最少見最不常使用的運算子之一。
舉個例子:
let a = (1 + 2, 3 + 4);
alert( a ); // 7(3 + 4 的結果)
逗號運算子的優先順序非常低
請注意逗號運算子的優先順序非常低,比 = 還要低,因此上面你的例子中圓括號非常重要。
如果沒有圓括號:a = 1 + 2, 3 + 4 會先執行 +,將數值相加得到 a = 3, 7,然後賦值運算子 = 執行 a = 3,然後逗號之後的數值 7 不會再執行,它被忽略掉了。相當於 (a = 1 + 2), 3 + 4。
下面這些表示式的結果是什麼?
"" + 1 + 0 = "10" // (1)
"" - 1 + 0 = -1 // (2)
true + false = 1
6 / "3" = 2
"2" * "3" = 6
4 + 5 + "px" = "9px"
"$" + 4 + 5 = "$45"
"4" - 2 = 2
"4px" - 2 = NaN
7 / 0 = Infinity
" -9 " + 5 = " -9 5" // (3)
" -9 " - 5 = -14 // (4)
null + 1 = 1 // (5)
undefined + 1 = NaN // (6)
" \t \n" - 2 = -2 // (7)
有字串的加法 "" + 1,首先會將數字 1 轉換為一個字串:"" + 1 = "1",然後我們得到 "1" + 0,再次應用同樣的規則得到最終的結果。
減法 -(像大多數數學運算一樣)只能用於數字,它會使空字串 "" 轉換為 0。
帶字串的加法會將數字 5 加到字串之後。
減法始終將字串轉換為數字,因此它會使 " -9 " 轉換為數字 -9(忽略了字串首尾的空格)。
null 經過數字轉換之後會變為 0。
undefined 經過數字轉換之後會變為 NaN。
字串轉換為數字時,會忽略字串的首尾處的空格字元。在這裡,整個字串由空格字元組成,包括 \t、\n 以及它們之間的“常規”空格。因此,類似於空字串,所以會變為 0。
#值的比較
在 JavaScript 中,它們的編寫方式如下:
大於 / 小於:a > b,a < b。
大於等於 / 小於等於:a >= b,a <= b。
檢查兩個值的相等:a == b,請注意雙等號 == 表示相等性檢查,而單等號 a = b 表示賦值。
檢查兩個值不相等。不相等在數學中的符號是 ≠,但在 JavaScript 中寫成 a != b。
比較結果為 Boolean 型別
所有比較運算子均返回布林值:
true —— 表示“yes(是)”,“correct(正確)”或“the truth(真)”。
false —— 表示“no(否)”,“wrong(錯誤)”或“not the truth(非真)”。
字串比較
在比較字串的大小時,JavaScript 會使用“字典(dictionary)”或“詞典(lexicographical)”順序進行判定。
換言之,字串是按字元(母)逐個進行比較的。
非真正的字典順序,而是 Unicode 編碼順序
在上面的演算法中,比較大小的邏輯與字典或電話簿中的排序很像,但也不完全相同。
比如說,字串比較對字母大小寫是敏感的。大寫的 "A" 並不等於小寫的 "a"。哪一個更大呢?實際上小寫的 "a" 更大。這是因為在 JavaScript 使用的內部編碼表中(Unicode),小寫字母的字元索引值更大。我們會在 字串 這章討論更多關於字串的細節。
不同型別間的比較
當對不同型別的值進行比較時,JavaScript 會首先將其轉化為數字(number)再判定大小。
例如:
alert( '2' > 1 ); // true,字串 '2' 會被轉化為數字 2
alert( '01' == 1 ); // true,字串 '01' 會被轉化為數字 1
對於布林型別值,true 會被轉化為 1、false 轉化為 0。
嚴格相等
普通的相等性檢查 == 存在一個問題,它不能區分出 0 和 false:
alert( 0 == false ); // true
也同樣無法區分空字串和 false:
alert( '' == false ); // true
這是因為在比較不同型別的值時,處於相等判斷符號 == 兩側的值會先被轉化為數字。空字串和 false 也是如此,轉化後它們都為數字 0。
嚴格相等運算子 === 在進行比較時不會做任何的型別轉換。
換句話說,如果 a 和 b 屬於不同的資料型別,那麼 a === b 不會做任何的型別轉換而立刻返回 false。
讓我們試試:
alert( 0 === false ); // false,因為被比較值的資料型別不同
同樣的,與“不相等”符號 != 類似,“嚴格不相等”表示為 !==。
對 null 和 undefined 進行比較
當使用 null 或 undefined 與其他值進行比較時,其返回結果常常出乎你的意料。
當使用嚴格相等 === 比較二者時
它們不相等,因為它們屬於不同的型別。
alert( null === undefined ); // false
當使用非嚴格相等 == 比較二者時
JavaScript 存在一個特殊的規則,會判定它們相等。它們倆就像“一對戀人”,僅僅等於對方而不等於其他任何的值(只在非嚴格相等下成立)。
alert( null == undefined ); // true
當使用數學式或其他比較方法 < > <= >= 時:
null/undefined 會被轉化為數字:null 被轉化為 0,undefined 被轉化為 NaN。
奇怪的結果:null vs 0
通過比較 null 和 0 可得:
alert( null > 0 ); // (1) false
alert( null == 0 ); // (2) false
alert( null >= 0 ); // (3) true
是的,上面的結果完全打破了你對數學的認識。在最後一行程式碼顯示“null 大於等於 0”的情況下,前兩行程式碼中一定會有一個是正確的,然而事實表明它們的結果都是 false。
為什麼會出現這種反常結果,這是因為相等性檢查 == 和普通比較符 > < >= <= 的程式碼邏輯是相互獨立的。進行值的比較時,null 會被轉化為數字,因此它被轉化為了 0。這就是為什麼(3)中 null >= 0 返回值是 true,(1)中 null > 0 返回值是 false。
另一方面,undefined 和 null 在相等性檢查 == 中不會進行任何的型別轉換,它們有自己獨立的比較規則,所以除了它們之間互等外,不會等於任何其他的值。這就解釋了為什麼(2)中 null == 0 會返回 false。
特立獨行的 undefined
undefined 不應該被與其他值進行比較:
alert( undefined > 0 ); // false (1)
alert( undefined < 0 ); // false (2)
alert( undefined == 0 ); // false (3)
為何它看起來如此厭惡 0?返回值都是 false!
原因如下:
(1) 和 (2) 都返回 false 是因為 undefined 在比較中被轉換為了 NaN,而 NaN 是一個特殊的數值型值,它與任何值進行比較都會返回 false。
(3) 返回 false 是因為這是一個相等性檢查,而 undefined 只與 null 相等,不會與其他值相等。
以下表達式的執行結果是?
5 > 4 → true
"apple" > "pineapple" → false
"2" > "12" → true
undefined == null → true
undefined === null → false
null == "\n0\n" → false
null === +"\n0\n" → false
結果的原因:
數字間比較大小,顯然得 true。
按詞典順序比較,得 false。"a" 比 "p" 小。
與第 2 題同理,首位字元 "2" 大於 "1"。
null 只與 undefined 互等。
嚴格相等模式下,型別不同得 false。
與第 4 題同理,null 只與 undefined 相等。
不同型別嚴格不相等。