最近原創文章
《了不起的 Webpack HMR 學習指南(含源碼分析)》
前言
最近與部門老大一起面試了許多前端求職者,其中「想換個學習氛圍較好的人佔多數」,但良好的學習氛圍也是需要一點點營造出來的????。
爲此我們組建了我們團隊內部的“「現代 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);
以下任意項成立則兩個值相同:
兩個值都是
undefined
兩個值都是
null
兩個值都是
true
或者都是false
兩個值是由相同個數的字符按照相同的順序組成的字符串
兩個值指向同一個對象
兩個值都是數字並且
都是正零
+0
都是負零
-0
都是
NaN
都是除零和
NaN
外的其它同一個數字 使用示例:
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 並不會終止循環的迭代,而是:
在
while
循環中,控制流跳轉回條件判斷;在
for
循環中,控制流跳轉到更新語句。注意: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 水平並查缺補漏,溫故知新。