前言
最近與部門老大一起面試了許多前端求職者,其中想換個學習氛圍較好的人佔多數,但良好的學習氛圍也是需要一點點營造出來的?。
為此我們組建了我們團隊內部的“現代 JavaScript 突擊隊”,第一期學習內容為《現代 JavaScript 教程》系列,幫助小組成員系統地進行學習鞏固,並讓大家養成系統性學習和輸出學習總結的學習方式。
本文作為我輸出的第一部分學習總結,希望作為一份自測清單,幫助大家鞏固知識,溫故知新。
這裡也下面分享我們學習小組的“押金制度”和“押金記錄表”?
接下來開始分享自測清單的內容。
一、Hello World!
1. 指令碼引入方式
JavaScript 指令碼引入方式有兩種:
-
<script>
標籤插入指令碼; -
<script>
標籤src
設定指令碼地址。
2. script 標籤屬性
<script>
標籤有以下常用屬性:
2.1 src
src
:指定外部指令碼的URI, 如果設定了 src
特性,script 標籤內容將會被忽略;
<script src="example-url.js"></script>
2.2 type
type
:指定引用指令碼的語言,屬性值為 MIME 型別,包括text/javascript
, text/ecmascript
, application/javascript
, 和application/ecmascript
。如果沒有定義這個屬性,指令碼會被視作JavaScript。
ES6 新增了屬性值 module
,程式碼會被當做 JavaScript 模組。
<script type="text/javascript"></script>
2.3 async
async
規定一旦指令碼可用,則會非同步執行。
注意:async 屬性僅適用於外部指令碼(只有在使用 src 屬性時)。
有多種執行外部指令碼的方法:
如果 async="async"
:指令碼相對於頁面的其餘部分非同步地執行(當頁面繼續進行解析時,指令碼將被執行);
如果不使用 async
且 defer="defer"
:指令碼將在頁面完成解析時執行;
如果既不使用 async
也不使用 defer
:在瀏覽器繼續解析頁面之前,立即讀取並執行指令碼;
<script async="async"></script>
2.4 defer
defer
屬性規定是否對指令碼執行進行延遲,直到頁面載入為止。
如果您的指令碼不會改變文件的內容,可將 defer 屬性加入到 <script>
標籤中,以便加快處理文件的速度。因為瀏覽器知道它將能夠安全地讀取文件的剩餘部分而不用執行指令碼,它將推遲對指令碼的解釋,直到文件已經顯示給使用者為止。
<script defer="defer"></script>
詳細介紹可以閱讀《MDN <script>
章節 》。
二、程式碼結構
1. 語句
語句是執行行為(action)的語法結構和命令。如: alert('Hello, world!')
這樣可以用來顯示訊息的語句。
2. 分號
存在分行符時,多數情況下可以省略分號。但不全是,比如:
alert(3 +
1
+ 2);
建議新人最好不要省略分號。
3. 註釋
單行註釋以兩個正斜槓字元 //
開始。
// 註釋文字
console.log("leo");
多行註釋以一個正斜槓和星號開始 “/*”
並以一個星號和正斜杆結束 “*/”
。
/*
這是多行註釋。
第二行註釋。
*/
console.log("leo");
三、現代模式,"use strict"
1. 作用
JavaScript 的嚴格模式是使用受限制的 JavaScript 的一種方式,從而隱式地退出“草率模式”。
"use strict"
指令將瀏覽器引擎轉換為“現代”模式,改變一些內建特性的行為。
2. 使用
通過在指令碼檔案/函式開頭新增 "use strict";
宣告,即可啟用嚴格模式。
全域性開啟嚴格模式:
// index.js
"use strict";
const v = "Hi! I'm a strict mode script!";
函式內開啟嚴格模式:
// index.js
function strict() {
'use strict';
function nested() {
return "And so am I!";
}
return "Hi! I'm a strict mode function! " + nested();
}
3. 注意點
-
"use strict"
需要定義在指令碼最頂部(函式內除外),否則嚴格模式可能無法啟用。 - 一旦進入了嚴格模式,就無法關閉嚴格模式。
4. 體驗
啟用 "use strict"
後,為未定義元素賦值將丟擲異常:
"use strict";
leo = 17; // Uncaught ReferenceError: leo is not defined
啟用 "use strict"
後,試圖刪除不可刪除的屬性時會丟擲異常:
"use strict";
delete Object.prototype; // Uncaught TypeError: Cannot delete property 'prototype' of function Object() { [native code] }
詳細介紹可以閱讀《MDN 嚴格模式章節 》。
四、變數
1. 介紹
變數是資料的“命名儲存”。
2. 使用
目前定義變數可以使用三種關鍵字:var / let / const。三者區別可以閱讀《let 和 const 命令》 。
let name = "leo";
let name = "leo", age, addr;
let name = "leo", age = 27, addr = "fujian";
3. 命名建議
變數命名有 2 個限制:
- 變數名稱必須僅包含字母,數字,符號
$
和_
。 - 首字元必須非數字。
變數命名還有一些建議:
- 常量一般用全大寫,如
const PI = 3.141592
; - 使用易讀的命名,比如
userName
或者shoppingCart
。
4. 注意點
- JavaScript 變數名稱區分大小寫,如變數
leo
與Leo
是不同的; - JavaScript 變數名稱允許非英文字母,但不推薦,如
let 平安 = "leo"
; - 避免使用
a
、b
、c
這種縮寫。
五、資料型別
JavaScript 是一種弱型別或者說動態語言。這意味著你不用提前宣告變數的型別,在程式執行過程中,型別會被自動確定。這也意味著你可以使用同一個變數儲存不同型別的資料:
var foo = 42; // foo is a Number now
foo = "bar"; // foo is a String now
foo = true; // foo is a Boolean now
詳細介紹可以閱讀《MDN JavaScript 資料型別和資料結構 》。
1. 八大資料型別
前七種為基本資料型別,也稱為原始型別(值本身無法被改變),而 object
為複雜資料型別。
八大資料型別分別是:
-
number
用於任何型別的數字:整數或浮點數,在 ±2 範圍內的整數。 -
bigint
用於任意長度的整數。 -
string
用於字串:一個字串可以包含一個或多個字元,所以沒有單獨的單字元型別。 -
boolean
用於true
和false
。 -
null
用於未知的值 —— 只有一個null
值的獨立型別。 -
undefined
用於未定義的值 —— 只有一個undefined
值的獨立型別。 -
symbol
用於唯一的識別符號。 -
object
用於更復雜的資料結構。
每個型別後面會詳細介紹。
2. 檢測資料型別
通過 typeof
運算子檢查:
- 兩種形式:
typeof x
或者typeof(x)
。 - 以字串的形式返回型別名稱,例如
"string"
。 -
typeof null
會返回"object"
—— 這是 JavaScript 程式語言的一個錯誤,實際上它並不是一個object
。
typeof "leo" // "string"
typeof undefined // "undefined"
typeof 0 // "number"
typeof NaN // "number"
typeof 10n // "bigint"
typeof true // "boolean"
typeof Symbol("id") // "symbol"
typeof [1,2,3,4] // "object"
typeof Math // "object" (1) Math 是一個提供數學運算的內建 object。
typeof null // "object" (2) JavaScript 語言的一個錯誤,null 不是一個 object。null 有自己的型別,它是一個特殊值。
typeof alert // "function" (3) alert 在 JavaScript 語言中是一個函式。
六、型別轉換
JavaScript 變數可以轉換為新變數或其他資料型別:
- 通過使用 JavaScript 函式
- 通過 JavaScript 自身自動轉換
1. 字串轉換
通過全域性方法 String()
將其他型別資料(任何型別的數字,字母,布林值,物件)轉換為 String 型別:
String(123); // "123"
// Number方法toString()/toExponential()/toFixed()/toPrecision() 也有同樣效果。
String(false); // "false"
// Boolean方法 toString() 也有同樣效果。
String(new Date()); // "Sun Jun 07 2020 21:44:20 GMT+0800 (中國標準時間)"
// Date方法 toString() 也有同樣效果。
String(leo);
2. 數值轉換
通過以下幾種方式能將其他型別資料轉換為 Number 型別:
- 一元運算子
+
const age = +"22"; // 22
-
Number
方法
const age = Number("22"); // 22
Number.parseFloat("22"); // 22
Number.parseInt("22"); // 22
- 其他方式轉 Number 型別
// 布林值
Number(false) // 返回 0
Number(true) // 返回 1
// 日期
const date = new Date();
Number(date); // 返回 1591537858154
date.getTime(); // 返回 1591537858154,效果一致。
// 自動轉換
5 + null // 返回 5 null 轉換為 0
"5" + null // 返回"5null" null 轉換為 "null"
"5" + 1 // 返回 "51" 1 轉換為 "1"
"5" - 1 // 返回 4 "5" 轉換為 5
3. 布林值轉換
轉換規則如下:
- 直觀上為“空”的值(如
0
、空字串、null
、undefined
和NaN
)將變為false
。 - 其他值變成
true
。
Boolean(1); // true
Boolean(0); // false
Boolean("hello"); // true
Boolean(""); // false
Boolean("0"); // true
Boolean(" "); // 空白, 也是 true (任何非空字串是 true)
4. 小結
七、運算子
1、運算子概念
常見運算子如加法 +
、減法 -
、乘法 *
和除法 /
,舉一個例子,來介紹一些概念:
let sum = 1 + 2;
let age = +18;
其中:
- 加法運算
1 + 2
中,1
和2
為 2 個運算元,左運算元1
和右運算元2
,即運算元就是運算子作用的物件。 -
1 + 2
運算式中包含 2 個運算元,因此也稱該運算式中的加號+
為 二元運算子。 - 在
+18
中的加號+
對應只有一個運算元,則它是 一元運算子 。
2、+ 號運算子
let msg = "hello " + "leo"; // "hello leo"
let total = 10 + 20; // 30
let text1 = "1" + "2"; // "12"
let text2 = "1" + 2; // "12"
let text3 = 1 + "2"; // "12"
let text4 = 1 + 2 + "3"; // "33"
let num = +text1; // 12 轉換為 Number 型別
3、運算子優先順序
運算子的優先順序決定了表示式中運算執行的先後順序,優先順序高的運算子最先被執行。
下面的表將所有運算子按照優先順序的不同從高(20)到低(1)排列。
優先順序 | 運算型別 | 關聯性 | 運算子 | ||
---|---|---|---|---|---|
20 | 圓括號 |
n/a(不相關) | ( … ) |
||
19 | 成員訪問 |
從左到右 | … . … |
||
需計算的成員訪問 |
從左到右 | … [ … ] |
|||
new (帶引數列表) |
n/a | new … ( … ) |
|||
函式呼叫 | 從左到右 | … ( … ) |
|||
可選鏈(Optional chaining) | 從左到右 | ?. |
|||
18 | new (無引數列表) | 從右到左 | new … |
||
17 | 後置遞增(運算子在後) | n/a | |||
… ++ | |||||
後置遞減(運算子在後) | … -- |
||||
16 | 邏輯非 | 從右到左 | ! … |
||
按位非 | ~ … |
||||
一元加法 | + … |
||||
一元減法 | - … |
||||
前置遞增 | ++ … |
||||
前置遞減 | -- … |
||||
typeof | typeof … |
||||
void | void … |
||||
delete | delete … |
||||
await | await … |
||||
15 | 冪 | 從右到左 | … ** … |
||
14 | 乘法 | 從左到右 | |||
… * … | |||||
除法 | … / … |
||||
取模 | … % … |
||||
13 | 加法 | 從左到右 | |||
… + … | |||||
減法 | … - … |
||||
12 | 按位左移 | 從左到右 | … << … |
||
按位右移 | … >> … |
||||
無符號右移 | … >>> … |
||||
11 | 小於 | 從左到右 | … < … |
||
小於等於 | … <= … |
||||
大於 | … > … |
||||
大於等於 | … >= … |
||||
in | … in … |
||||
instanceof | … instanceof … |
||||
10 | 等號 | 從左到右 | |||
… == … | |||||
非等號 | … != … |
||||
全等號 | … === … |
||||
非全等號 | … !== … |
||||
9 | 按位與 | 從左到右 | … & … |
||
8 | 按位異或 | 從左到右 | … ^ … |
||
7 | 按位或 | 從左到右 | `… | …` | |
6 | 邏輯與 | 從左到右 | … && … |
||
5 | 邏輯或 | 從左到右 | `… | …` | |
4 | 條件運算子 | 從右到左 | … ? … : … |
||
3 | 賦值 | 從右到左 | … = … |
||
… += … |
|||||
… -= … |
|||||
… *= … |
|||||
… /= … |
|||||
… %= … |
|||||
… <<= … |
|||||
… >>= … |
|||||
… >>>= … |
|||||
… &= … |
|||||
… ^= … |
|||||
`… | = …` | ||||
2 | yield | 從右到左 | yield … |
||
yield* | yield* … |
||||
1 | 展開運算子 | n/a |
... … |
||
0 | 逗號 | 從左到右 | … , … |
3 > 2 && 2 > 1
// return true
3 > 2 > 1
// 返回 false,因為 3 > 2 是 true,並且 true > 1 is false
// 加括號可以更清楚:(3 > 2) > 1
八、值的比較
1. 常見比較
在 JS 中的值的比較與數學很型別:
- 大於/小於/大於等於/小於等於:
a>b
/a<b
/a>=b
/a<=b
; - 判斷相等:
// 使用 ==,非嚴格等於,不關心值型別
// == 運算子會對比較的運算元做隱式型別轉換,再比較
'1' == 1; // true
// 使用 ===,嚴格相等,關心值型別
// 將數字值 -0 和 +0 視為相等,並認為 Number.NaN 不等於 NaN。
'1' === 1; // false
(圖片來自:《MDN JavaScript 中的相等性判斷》)
- 判斷不相等:
和判斷相等一樣,也有兩種: !=
/ !==
。
2. 相等性判斷(Object.is())
另外 ES6 新增 Object.is 方法判斷兩個值是否相同,語法如下:
Object.is(value1, value2);
以下任意項成立則兩個值相同:
使用示例:
Object.is('foo', 'foo'); // true
Object.is(window, window); // true
Object.is('foo', 'bar'); // false
Object.is([], []); // false
var foo = { a: 1 };
var bar = { a: 1 };
Object.is(foo, foo); // true
Object.is(foo, bar); // false
Object.is(null, null); // true
// 特例
Object.is(0, -0); // false
Object.is(0, +0); // true
Object.is(-0, -0); // true
Object.is(NaN, 0/0); // true
相容性 Polyfill 處理:
if (!Object.is) {
Object.is = function(x, y) {
// SameValue algorithm
if (x === y) { // Steps 1-5, 7-10
// Steps 6.b-6.e: +0 != -0
return x !== 0 || 1 / x === 1 / y;
} else {
// Step 6.a: NaN == NaN
return x !== x && y !== y;
}
};
}
3. null 與 undefined 比較
對於相等性判斷比較簡單:
null == undefined; // true
null === undefined; // false
對於其他比較,它們會先轉換位數字:null
轉換為 0
, undefied
轉換為 NaN
。
null > 0; // false 1
null >= 0; // true 2
null == 0; // false 3
null < 1; // true 4
需要注意:null == 0; // false
這裡是因為:undefined
和 null
在相等性檢查 ==
中不會進行任何的型別轉換,它們有自己獨立的比較規則,所以除了它們之間互等外,不會等於任何其他的值。
undefined > 0; // false 1
undefined > 1; // false 2
undefined == 0; // false 3
第 1、2 行都返回 false
是因為 undefined
在比較中被轉換為了 NaN
,而 NaN
是一個特殊的數值型值,它與任何值進行比較都會返回 false
。
第 3 行返回 false
是因為這是一個相等性檢查,而 undefined
只與 null
相等,不會與其他值相等。
九、alert / prompt / confirm
1. alert
顯示一個警告對話方塊,上面顯示有指定的文字內容以及一個“確定”按鈕。
注意:彈出模態框,並暫停指令碼,直到使用者點選“確定”按鈕。
// 語法
window.alert(message);
alert(message);
// 示例
alert('hello leo!');
message
是要顯示在對話方塊中的文字字串,如果傳入其他型別的值,會轉換成字串。
2. prompt
顯示一個對話方塊,對話方塊中包含一條文字資訊,用來提示使用者輸入文字。
注意:彈出模態框,並暫停指令碼,直到使用者點選“確定”按鈕。
當點選確定返回文字,點選取消或按下 Esc 鍵返回 null
。
語法如下:
let result = window.prompt(text, value);
-
result
用來儲存使用者輸入文字的字串,或者是 null。 -
text
用來提示使用者輸入文字的字串,如果沒有任何提示內容,該引數可以省略不寫。 -
value
文字輸入框中的預設值,該引數也可以省略不寫。不過在 Internet Explorer 7 和 8 中,省略該引數會導致輸入框中顯示預設值"undefined"。
3. confirm
Window.confirm()
方法顯示一個具有一個可選訊息和兩個按鈕(確定和取消)的模態對話方塊。
注意:彈出模態框,並暫停指令碼,直到使用者點選“確定”按鈕。
語法如下:
let result = window.confirm(message);
- message 是要在對話方塊中顯示的可選字串。
- result 是一個布林值,表示是選擇確定還是取消 (true表示OK)。
十、條件運算子:if 和 '?'
1. if 語句
當 if 語句當條件表示式,會將表示式轉換為布林值,當為 truthy 時執行裡面程式碼。
轉換規則如:
- 數字
0
、空字串""
、null
、undefined
和NaN
都會被轉換成false
。因為他們被稱為 “falsy” 值。 - 其他值被轉換為
true
,所以它們被稱為 “truthy”。
2. 三元運算子
條件(三元)運算子是 JavaScript 僅有的使用三個運算元的運算子。一個條件後面會跟一個問號(?),如果條件為 truthy ,則問號後面的表示式A將會執行;表示式A後面跟著一個冒號(:),如果條件為 falsy ,則冒號後面的表示式B將會執行。本運算子經常作為 [if](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else)
語句的簡捷形式來使用。
語法:
condition ? exprIfTrue : exprIfFalse
- condition
計算結果用作條件的表示式。
- exprIfTrue
如果表示式 condition 的計算結果是 truthy(它和 true 相等或者可以轉換成 true ),那麼表示式 exprIfTrue 將會被求值。
- exprIfFalse
如果表示式 condition 的計算結果是 falsy(它可以轉換成 false ),那麼表示式 exprIfFalse 將會被執行。
示例:
let getUser = function(name){
return name === 'leo' ? 'hello leo!' : 'unknow user';
}
// 可以簡寫如下:
let getUser = name => name === 'leo' ? 'hello leo!' : 'unknow user';
getUser('leo'); // "hello leo!"
getUser('pingan'); // "unknow user"
十一、邏輯運算子
詳細可以閱讀《MDN 邏輯運算子》 。
1. 運算子介紹
邏輯運算子如下表所示 (其中_expr_
可能是任何一種型別, 不一定是布林值):
運算子 | 語法 | 說明 | ||||
---|---|---|---|---|---|---|
邏輯與,AND(&& ) |
_expr1_ && _expr2_ |
若 expr**1** 可轉換為 true ,則返回 expr**2** ;否則,返回 expr**1** 。 |
||||
邏輯或,OR(` | `) | `_expr1_ | _expr2_` | 若 expr**1** 可轉換為 true ,則返回 expr**1** ;否則,返回 expr**2** 。 |
||
邏輯非,NOT(! ) |
!_expr_ |
若 expr 可轉換為 true ,則返回 false ;否則,返回 true 。 |
如果一個值可以被轉換為 true
,那麼這個值就是所謂的 truthy,如果可以被轉換為 false
,那麼這個值就是所謂的 falsy。
會被轉換為 false
的表示式有:
-
null
; -
NaN
; -
0
; - 空字串(
""
or''
or`
`); -
undefined
。
儘管 &&
和 ||
運算子能夠使用非布林值的運算元, 但它們依然可以被看作是布林操作符,因為它們的返回值總是能夠被轉換為布林值。如果要顯式地將它們的返回值(或者表示式)轉換為布林值,請使用雙重非運算子(即!!
)或者Boolean建構函式。
JavaScript 裡有三個邏輯運算子:||
(或),&&
(與),!
(非)。
2. 運算子示例
- 邏輯與(&&)
所有條件都為 true 才返回 true,否則為 false。
a1 = true && true // t && t 返回 true
a2 = true && false // t && f 返回 false
a3 = false && true // f && t 返回 false
a4 = false && (3 == 4) // f && f 返回 false
a5 = "Cat" && "Dog" // t && t 返回 "Dog"
a6 = false && "Cat" // f && t 返回 false
a7 = "Cat" && false // t && f 返回 false
a8 = '' && false // f && f 返回 ""
a9 = false && '' // f && f 返回 false
- 邏輯或( || )
所有條件有一個為 true 則返回 true,否則為 false。
o1 = true || true // t || t 返回 true
o2 = false || true // f || t 返回 true
o3 = true || false // t || f 返回 true
o4 = false || (3 == 4) // f || f 返回 false
o5 = "Cat" || "Dog" // t || t 返回 "Cat"
o6 = false || "Cat" // f || t 返回 "Cat"
o7 = "Cat" || false // t || f 返回 "Cat"
o8 = '' || false // f || f 返回 false
o9 = false || '' // f || f 返回 ""
- 邏輯非( ! )
n1 = !true // !t 返回 false
n2 = !false // !f 返回 true
n3 = !'' // !f 返回 true
n4 = !'Cat' // !t 返回 false
- 雙重非運( !! )
n1 = !!true // !!truthy 返回 true
n2 = !!{} // !!truthy 返回 true: 任何 物件都是 truthy 的…
n3 = !!(new Boolean(false)) // …甚至 .valueOf() 返回 false 的布林值物件也是!
n4 = !!false // !!falsy 返回 false
n5 = !!"" // !!falsy 返回 false
n6 = !!Boolean(false) // !!falsy 返回 false
3. 布林值轉換規則
- 將 && 轉換為 ||
condi1 && confi2
// 轉換為
!(!condi1 || !condi2)
- 將 || 轉換為 &&
condi1 || condi2
// 轉換為
!(!condi1 && !condi2)
4. 短路取值
由於邏輯表示式的運算順序是從左到右,也可以用以下規則進行"短路"計算:
-
(some falsy expression) && (_expr)_
短路計算的結果為假。 -
(some truthy expression) || _(expr)_
短路計算的結果為真。
短路意味著上述表示式中的expr部分不會被執行,因此expr的任何副作用都不會生效(舉個例子,如果expr是一次函式呼叫,這次呼叫就不會發生)。造成這種現象的原因是,整個表示式的值在第一個運算元被計算後已經確定了。看一個例子:
function A(){ console.log('called A'); return false; }
function B(){ console.log('called B'); return true; }
console.log( A() && B() );
// logs "called A" due to the function call,
// then logs false (which is the resulting value of the operator)
console.log( B() || A() );
// logs "called B" due to the function call,
// then logs true (which is the resulting value of the operator)
5. 注意
與運算 &&
的優先順序比或運算 ||
要高。
所以程式碼 a && b || c && d
完全跟 &&
表示式加了括號一樣:(a && b) || (c && d)
。
十二、迴圈:while 和 for
1. while 迴圈
詳細可以閱讀《MDN while》 。
while 語句可以在某個條件表示式為真的前提下,迴圈執行指定的一段程式碼,直到那個表示式不為真時結束迴圈。
如:
var n = 0;
var x = 0;
while (n < 3) {
n++;
x += n;
}
當迴圈體為單行時,可以不寫大括號:
let i = 3;
while(i) console.log(i --);
2. do...while 迴圈
詳細可以閱讀《MDN do...while》 。do...while
語句建立一個執行指定語句的迴圈,直到condition
值為 false。在執行statement
後檢測condition
,所以指定的statement
至少執行一次。
如:
var result = '';
var i = 0;
do {
i += 1;
result += i + ' ';
} while (i < 5);
3. for 迴圈
詳細可以閱讀《MDN for》 。for
語句用於建立一個迴圈,它包含了三個可選的表示式,這三個表示式被包圍在圓括號之中,使用分號分隔,後跟一個用於在迴圈中執行的語句(通常是一個塊語句)。
語法如:
for (begin; condition; step) {
// ……迴圈體……
}
示例:
for (let i = 0; i < 3; i++) {
console.log(i);
}
描述:
begin | i = 0 |
進入迴圈時執行一次。 |
---|---|---|
condition | i < 3 |
在每次迴圈迭代之前檢查,如果為 false,停止迴圈。 |
body(迴圈體) | alert(i) |
條件為真時,重複執行。 |
step | i++ |
在每次迴圈體迭代後執行。 |
4. 可選的 for 表示式
for
語句頭部圓括號中的所有三個表示式都是可選的。
- 不指定表示式中初始化塊
var i = 0;
for (; i < 3; i++) {
console.log(i);
}
- 不指定表示式中條件塊,這就必須要求在迴圈體中結束迴圈,否則會出現死迴圈
for (var i = 0;; i++) {
console.log(i);
if (i > 3) break;
}
- 不指定所有表示式,也需要在迴圈體中指定結束迴圈的條件
var i = 0;
for (;;) {
if (i > 3) break;
console.log(i);
i++;
}
5. break 語句
詳細可以閱讀《MDN break》 。
break 語句中止當前迴圈,switch
語句或label
語句,並把程式控制流轉到緊接著被中止語句後面的語句。
在 while 語句中:
function testBreak(x) {
var i = 0;
while (i < 6) {
if (i == 3) {
break;
}
i += 1;
}
return i * x;
}
另外,也可以為程式碼塊做標記,並在 break 中指定要跳過的程式碼塊語句的 label:
outer_block:{
inner_block:{
console.log ('1');
break outer_block; // breaks out of both inner_block and outer_block
console.log (':-('); // skipped
}
console.log ('2'); // skipped
}
需要注意的是:break 語句需要內嵌在它所應用的標籤或程式碼塊中,否則報錯:
block_1:{
console.log ('1');
break block_2; // SyntaxError: label not found
}
block_2:{
console.log ('2');
}
6. continue 語句
continue 宣告終止當前迴圈或標記迴圈的當前迭代中的語句執行,並在下一次迭代時繼續執行迴圈。
與 break
語句的區別在於, continue 並不會終止迴圈的迭代,而是:
注意:continue 也必須在對應迴圈內部,否則報錯。
i = 0;
n = 0;
while (i < 5) {
i++;
if (i === 3) {
continue;
}
n += i;
}
帶 label:
var i = 0,
j = 8;
checkiandj: while (i < 4) {
console.log("i: " + i);
i += 1;
checkj: while (j > 4) {
console.log("j: "+ j);
j -= 1;
if ((j % 2) == 0)
continue checkj;
console.log(j + " is odd.");
}
console.log("i = " + i);
console.log("j = " + j);
}
7. 注意
禁止 break/continue
在 ‘?’ 的右邊:
(i > 5) ? console.log(i) : continue; // continue 不允許在這個位置
這樣會提示語法錯誤。
請注意非表示式的語法結構不能與三元運算子 ?
一起使用。特別是 break/continue
這樣的指令是不允許這樣使用的。
8. 總結
三種迴圈:
-
while
—— 每次迭代之前都要檢查條件。 -
do..while
—— 每次迭代後都要檢查條件。 -
for (;;)
—— 每次迭代之前都要檢查條件,可以使用其他設定。
通常使用 while(true)
來構造“無限”迴圈。這樣的迴圈和其他迴圈一樣,都可以通過 break
指令來終止。
如果我們不想在當前迭代中做任何事,並且想要轉移至下一次迭代,那麼可以使用 continue
指令。break/continue
支援迴圈前的標籤。標籤是 break/continue
跳出巢狀迴圈以轉到外部的唯一方法。
十三、"switch" 語句
switch
語句用來將表示式的值與 case 語句匹配,並執行與情況對應的語句。switch
語句可以替代多個 if 判斷,為多個分支選擇的情況提供一個更具描述性的方式。
1. 語法
switch
語句至少包含一個 case
程式碼塊和一個可選的 default
程式碼塊:
switch(expression) {
case 'value1':
// do something ...
[break]
default:
// ...
[break]
}
當 expression
表示式的值與 value1
匹配時,則執行其中程式碼塊。
如果沒有 case
子句匹配,則會選擇 default
子句執行,若連 default
子句都沒有,則直接執行到 switch
結束。
2. 使用 case 分組
所謂 case 分組,就是與多個 case 分支共享同一段程式碼,如下面例子中 case 1
和 case 2
:
let a = 2;
switch (a) {
case 1: // (*) 下面這兩個 case 被分在一組
case 2:
console.log('case is 1 or 2!');
break;
case 3:
console.log('case is 3!');
break;
default:
console.log('The result is default.');
}
// 'case is 1 or 2!'
3. 注意點
expression
表示式的值與case
值的比較是嚴格相等:
function f(n){
let a ;
switch(n){
case 1:
a = 'number';
break;
case '1':
a = 'string';
break;
default:
a = 'default';
break;
}
console.log(a)
}
f(1); // number
f('1'); // string
-
如果沒有
break
,程式將不經過任何檢查就會繼續執行下一個**case**
:
let a = 2 + 2;
switch (a) {
case 3:
console.log( 'Too small' );
case 4:
console.log( 'Exactly!' );
case 5:
console.log( 'Too big' );
default:
console.log( "I don't know such values" );
}
// Exactly!
// Too big
// I don't know such values
-
**default**
放在**case**
之上不影響匹配:
function f(n){
switch (n) {
case 2:
console.log(2);
break;
default:
console.log('default')
break;
case 1:
console.log('1');
break;
}
}
f(1); // 1
f(2); // 2
f(3); // default
switch
語句中存在let
/const
重複宣告問題:
// 以下定義會報錯
function f(n){
switch(n){
case 1:
let msg = 'hello';
console.log(1);
break;
case 2:
let msg = 'leo';
break;
default:
console.log('default');
break;
}
}
// Error: Uncaught SyntaxError: Identifier 'msg' has already been declared
這是由於兩個 let
處於同一個塊級作用域,所以它們被認為是同一變數名的重複宣告。
解決方式,只需要將 case
語句包裝在括號內即可解決:
function f(n){
switch(n){
case 1:{ // added brackets
let msg = 'hello';
console.log(msg);
break;
}
case 2: {
let msg = 'leo';
console.log(msg);
break;
}
default:
console.log('default');
break;
}
}
十四、函式
函式可以讓一段程式碼被多次呼叫,避免重複程式碼。
如之前學習到的一些內建函式: alert(msg)
/ prompt(msg, default)
/ confirm(quesyion)
等。
1. 函式定義
定義函式有兩種方式:函式宣告和函式表示式。
1.1 函式宣告
如定義一個簡單 getUser
函式:
function getUser(name){
return 'hello ' + name;
}
getUser('leo"); // 函式呼叫
通過函式宣告來定義函式時,需要由以下幾部分組成:
- 函式名稱 -
getUser
; - 函式引數列表 -
name
; - 函式的 JS 執行語句 -
return 'hello ' + name
。
1.2 函式表示式
類似宣告變數,還是以 getUser
為例:
let getUser = function(name){
return 'hello ' + name;
}
另外,函式表示式也可以提供函式名,並用於函式內部指代函式本身:
let fun = function f(n){
return n < 3 ? 1 : n * f(n - 1);
}
fun(3); // 3
fun(5); // 60
2. 函式呼叫
當定義一個函式後,它並不會自動執行,而是需要使用函式名稱進行呼叫,如上面例子:
fun(3); // 3
只要注意:
使用 函式表示式 定義函式時,呼叫函式的方法必須寫在定義之後,否則報錯:
console.log(fun()); // Uncaught ReferenceError: fun is not defined
let fun = function(){return 1};
而使用 函式宣告 則不會出現該問題:
console.log(fun()); // 1
function fun(){return 1};
原因就是:函式提升僅適用於函式宣告,而不適用於函式表示式。
3. 函式中的變數
在函式中,可以使用區域性變數和外部變數。
3.1 區域性變數
函式中宣告的變數只能在該函式內可見。
let fun = function(){
let name = 'leo';
}
fun();
console.log(name); // Uncaught ReferenceError: name is not defined
3.2 全域性變數
函式內可以使用外部變數,並且可以修改外部變數的值。
let name = 'leo';
let fun = function(){
let text = 'Hello, ' + name;
console.log(text);
}
fun(); // Hello, leo
當函式內也有與外部變數名稱相同的變數,會忽略外部變數:
let name = 'leo';
let fun = function(){
let name = 'pingan8787';
let text = 'Hello, ' + name;
console.log(text);
}
fun(); // Hello, pingan8787
4. 函式引數
從ECMAScript 6開始,有兩個新的型別的引數:預設引數,剩餘引數。
4.1 預設引數
若函式沒有傳入引數,則引數預設值為undefined
,通常設定引數預設值是這樣做的:
// ES6 之前,沒有設定預設值
function f(a, b){
b = b ? b : 1;
return a * b;
}
f(2,3); // 6
f(2); // 2
// ES6,設定預設值
function f(a, b = 1){
return a * b;
}
f(2,3); // 6
f(2); // 2
4.2 剩餘引數
可以將引數中不確定數量的參數列示成陣列,如下:
function f (a, ...b){
console.log(a, b);
}
f(1,2,3,4); // a => 1 b => [2, 3, 4]
既然講到引數,那就不能少了 arguments 物件。
4.3 arguments 物件
函式的實際引數會被儲存在一個類似陣列的arguments物件中。在函式內,我們可以使用 arguments 物件獲取函式的所有引數:
let fun = function(){
console.log(arguments);
console.log(arguments.length);
}
fun('leo');
// Arguments ["leo", callee: ƒ, Symbol(Symbol.iterator): ƒ]
// 1
以一個實際示例介紹,實現將任意數量引數連線成一個字串,並輸出的函式:
let argumentConcat = function(separator){
let result = '', i;
for(i = 1; i < arguments.length; i ++){
result += arguments[i] + separator;
}
return result;
}
argumentConcat(',', 'leo', 'pingan'); //"leo,pingan,"
5. 函式返回值
在函式任意位置,指定 return
指令來停止函式的執行,並返回函式指定的返回值。
let sum = function(a, b){
return a + b;
};
let res = sum(1, 2);
console.log(res); // 3
預設空值的 return
或沒有 return
的函式返回值為 undefined
。
十五、函式表示式
函式表示式是一種函式定義方式,在前面章節中已經介紹到了,這個部分將著重介紹 函式表示式 和 函式宣告 的區別:
1. 語法差異
// 函式表示式
let fun = function(){};
// 函式宣告
function fun(){}
2. 建立時機差異
函式表示式會在程式碼執行到達時被建立,並且僅從那一刻可用。
而函式宣告被定義之前,它就可以被呼叫。
// 函式表示式
fun(); // Uncaught ReferenceError: fun is not defined
let fun = function(){console.log('leo')};
// 函式宣告
fun(); // "leo"
function fun(){console.log('leo')};
3. 使用建議
建議優先考慮函式宣告語法,它能夠為組織程式碼提供更多靈活性,因為我們可以在宣告函式前呼叫該函式。
十六、箭頭函式
本章節簡單介紹箭頭函式基礎知識,後面章節會完整介紹。
函式箭頭表示式是ES6新增的函式表示式的語法,也叫胖箭頭函式,變化:更簡潔的函式和this
。
1. 程式碼更簡潔
// 有1個引數
let f = v => v;
// 等同於
let f = function (v){return v};
// 有多個引數
let f = (v, i) => {return v + i};
// 等同於
let f = function (v, i){return v + i};
// 沒引數
let f = () => 1;
// 等同於
let f = function (){return 1};
let arr = [1,2,3,4];
arr.map(ele => ele + 1); // [2, 3, 4, 5]
2. 注意點
- 箭頭函式不存在
this
; - 箭頭函式不能當做建構函式,即不能用
new
例項化; - 箭頭函式不存在
arguments
物件,即不能使用,可以使用rest
引數代替; - 箭頭函式不能使用
yield
命令,即不能用作Generator函式。
一個簡單的例子:
function Person(){
this.age = 0;
setInterval(() => {
this.age++;
}, 1000);
}
var p = new Person(); // 定時器一直在執行 p的值一直變化
總結
本文作為《初中級前端 JavaScript 自測清單》第一部分,介紹的內容以常用基礎知識為主,並在學習資料中,將知識點結合實際開發中遇到的場景進行展開介紹。希望能幫助大家自測自己的 JavaScript 水平並查缺補漏,溫故知新。