BACK
ES 是 ECMAScript 的簡稱,ECMAScript 是腳本語言的規範。而我們所使用的 JavaScript 是 ECMAScript 的一種實現。 【尚硅谷】ES6教程 - 涵盖 ES6~ES11 給進入 Vue.js 前的 ES6 必備知識 超级实用的 ES6 特性 【詳細 MDN 文件】
ECMAScript ES
是 ECMAScript
的簡稱,ECMAScript
是腳本語言的規範。而我們所使用的 JavaScript
是 ECMAScript
的一種實現。簡言之:ECMA做出規範,各瀏覽器依照規範做出實現,因而不同瀏覽器會有兼容性不同的情況。 ES6
是 ES
的經典版本,是前端工程師崗位的高頻需求,是前端開發工程師求職的必備技能。現階段前端行業發展迅猛,前端技術也在高速迭代, ES6-ES11 規范增加了很多 JavaScript
新特性。 ES
新特性已經成為前端技術發展的趨勢,語法簡潔,功能豐富,部分特性還有性能提升,前端開發三大框架 Vue
、React
、Angular
都用到了大量的新特性代碼,框架的升級也在向著新特性語法靠攏。 var
、 let
與 const
var
、 let
與 const
在 Javascript 都是用來宣告變數的語法,最大的差別是他們的scope(變數有效範圍)的不同。切分var
作用範圍的最小單位為 function
,而 let
與 const
的作用範圍是 block
也就是俗稱的大括號:{ }
來切分。const
所宣告的變數還有另一個特性是無法被重新賦值 (re-assign)。let
特性var 可以重複聲明,但 let 不能重複聲明。 演示: 1
2
3
4
5
var testA = "AAA" ;
var testA = "BBB" ; // 不會報錯
let testB = "AAA" ;
let testB = "BBB" ; // 會報錯
let 為塊級作用域 塊級作用域:變數只在代碼塊裡面有效({ ... }
、if else
、while
、for
) 在 ES5 中,作用域有:全局、函數、eval(嚴格模式下) 演示: 1
2
3
4
5
6
7
8
9
{
var b = "BBB" ;
}
console . log ( b ); // "BBB",因為 var 非塊級作用域,所以聲明時會往外層(全局window)添加這個屬性
{
let a = "AAA" ;
}
console . log ( a ); // 會報錯 a is not defined
不存在變數提升 變數提升:代碼執行前會先進行變數搜集,var
聲明的變量在搜集時會先定義一個 undefined
的初始值。 演示: 1
2
3
4
5
6
7
// 在 a 用 var 聲明前輸出
console . log ( a ); // 不會報錯,會輸出 undefined
var a = "AAA" ;
// 在 b 用 let 聲明前輸出
console . log ( b ); // 會報錯,Cannot access 'b' before initialization
let b = "BBB" ;
不影響作用域鏈 演示: 1
2
3
4
5
6
7
{
let school = "尚硅谷" ;
function fn () {
console . log ( school );
}
fn (); // 輸出 "尚硅谷",在 fn 內沒有 school,會往外層尋找
}
let 經典範例實踐 代碼: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
< html >
< body >
< div class = "container" >
< h2 class = "page-header" >
點擊切換顏色
</ h2 >
< div class = "item" ></ div >
< div class = "item" ></ div >
< div class = "item" ></ div >
</ div >
< script >
// 獲取 div 元素對象
let items = document . getElementsByClassName ( "item" );
// 遍歷並綁定事件
for ( var i = 0 ; i < items . length ; i ++ ) {
item [ i ]. onclick = function () {
// 修改當前元素的背景顏色
// 最佳寫法應為:
this . style . background = "pink" ;
// 常見錯誤寫法:
// items[i].style.background = "pink";
//
// 原因:
// i 使用 var 來聲明,var 聲明的變數非塊級作用域,
// 因此 i 被聲明在全局(window),此處取 i 會發現 window.i 已經等於 3
// 所以 items[3].style 會找不到。
//
// 修正方式:
// 將 i 改用 let 來聲明,讓 i 只存在於 for 迴圈中。
}
}
</ script >
</ body >
</ html >
const 特性 一定要賦初始值,且聲明後值不能被修改。 一般常數使用大寫(淺規則)。 也是塊級作用域 演示: 1
2
3
4
5
{
const PLAYER = "UZI" ;
}
console . log ( PLAYER ); // 會報錯,PLAYER is not defined
對於 Array 和 Object 的元素修改,不算對常數的修改,不會報錯。 演示: 1
2
3
4
{
const TEAM = [ "UZI" , "MXLG" , "Ming" , "Letme" ];
TEAM . push ( "Meiko" ); // 不會報錯,因為變數所指向的地址沒有改變
}
函式的參數默認值 1
2
3
4
function printText ( text ) {
text = text || "default" ;
console . log ( text );
}
1
2
3
function printText ( text = "default" ) {
console . log ( text );
}
二進制與八進制字面量 ES6 支持二進制與八進制的字面量,通過在數字前面添加 0o
或者 0O
即可將其轉換為八進制值 、添加 0b
或者 0B
即可將其轉換為二進制值 。 1
2
3
4
5
6
7
let oValue = "0o10" ;
console . log ( oValue );
// >>> 8
let bValue = 0b10 ;
console . log ( bValue );
// >>> 2
ES Module 與 import 、 export Javascript 自從 ES6 開始新增了模組系統(ES Module),我們可以將每個 Javascript 的檔案當作是一個獨立的模組來看待,在 A 檔案匯出(export)在 B 檔案匯入(import)。 a.js 1
2
3
4
5
6
7
export const aString = "This is A String" ;
export function aFunction () {
console . log ( "A Function test" )
}
export const aObject = { a : 1 };
b.js 1
2
3
4
5
6
7
8
9
10
import { aString , aFunction , aObject } from "./a.js" ;
console . log ( aString );
// >>> "This is A String"
console . log ( aObject );
// >>> { a: 1 }
aFounction ();
// >>> "A Function test"
c.js 1
2
3
export default function () {
console . log ( "Hello 008 JS!!!" );
}
d.js 1
2
3
4
import greeting from "./c.js" ;
greeting ();
// >>> "Hello 008 JS!!!"
箭頭函數與 this
從 ES6 開始新增了一種叫做 「箭頭函式表示式」 (Arrow Function expression) 的函式表達式。快速看一下,如何將一般的函式轉換成箭頭函式的寫法: 1
2
3
const plus = function ( numA , numB ) {
return numA + numB ;
};
首先我們把參數往前提,然後把關鍵字 function
刪掉改成箭頭符號 =>
: 1
2
3
const plus = ( numA , numB ) => {
return numA + numB ;
};
如果這個函式只是想要回傳某個運算結果的時候,可以將 return
以及大括號 { }
省略: 1
const plus = ( numA , numB ) => numA + numB ;
而只有一個參數的時候,參數前面的小括號( )
則可以省略: 1
2
3
4
const saySomething = msg => console . log ( msg );
saySomething ( "Hello!" );
// >>> "Hello!"
另外需要注意的是,在箭頭函式使用 this
時,這時 this
會指向箭頭函式外面的 this
,這個規則與原本 function
所宣告的函式不同,而且箭頭函式無法透過 bind()
強制指定裡面的 this
。 字串模板 (Template literals) 以往我們在組合 JavaScript 的變數與 HTML 模板的時候,大多會透過「字串結合」 +
的模式,或透過陣列來新增字串,最後再用 [].join("")
的方式串接起來。但自 ES6 起,我們可以透過字串模板的語法,將變數、運算式等插入至我們的網頁模板當中,像這樣: 1
2
// 用「`...`」取代單/雙引號
`string text ${ expression } string text`
這樣我們就可以將這個 expression
所代表的運算式或數值置入到字串裡頭了。 解構賦值 (Destructuring assignment) ES6 提供了解構賦值的語法,可以將陣列或者物件裡面的資料解開變成獨立的變數: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const user = {
id : 42 ,
displayName : "jdoe" ,
fullName : {
firstName : "John" ,
lastName : "Doe"
}
};
const { id , displayName , fullName } = user ;
console . log ( id );
// >>> 42
console . log ( displayName );
// >>> "jdoe"
console . log ( fullName );
// >>> { firstName: "John", lastName: "Doe" }
1
2
3
4
5
6
7
8
9
const number = [ 1 , 2 , 3 , 4 , 5 ];
const [ x , y ] = number ;
console . log ( x );
// >>> 1
console . log ( y );
// >>> 2
...
展開運算子 (Spread Operator) / 其餘運算子 (Rest Operator)雖然 ES6 提供的展開運算子與其餘運算子的語法都是 ...
,不過它們兩者所代表的涵意還是不太一樣。 展開運算子 1
2
3
4
5
const frameworks = [ "Vue.js" , "Angular" , "React" ];
const arr = [ "Awesome" , ... frameworks ];
console . log ( arr );
// >>> ["Awesome", "Vue.js", "Angular", "React"]
其餘運算子 延續前面的例子,我們可以透過 「其餘運算子」 將剩下的部分拆解出來: 1
2
3
4
5
6
7
8
9
10
11
12
13
console . log ( arr );
// >>> ["Awesome", "Vue.js", "Angular", "React"]
const [ a , b , ... others ] = arr ;
console . log ( a );
// >>> "Awesome"
console . log ( b );
// >>> "Vue.js"
console . log ( others );
// >>> "Angular", "React"
像這樣,我們可以搭配解構賦值的語法,將 arr
陣列拆解處來,並將剩餘的元素透過 ...others
分離。 當然,使用在物件上也是可以的: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 其餘 Properties
const { x , y , ... z } = { x : 1 , y : 2 , a : 3 , b : 4 };
console . log ( x );
// >>> 1
console . log ( y );
// >>> 2
console . log ( z );
// >>> { a: 3, b: 4 }
// 展開 Properties
const obj = { x , y , ... z };
console . log ( obj );
// >>> { x: 1, y: 2, a: 3, b: 4 }
要注意的是,其餘運算子 所分離的部分只是陣列或物件的「淺拷貝」 ,若在多層物件使用時要特別小心。 Promise
物件為了解決過去同步與非同步的問題,ES6 提供了 Promise
物件: 1
2
3
4
const myPromiseFunc = new Promise (( resolve , reject ) => {
resolve ( someValue ); // 完成
// reject("failure reason"); // 拒絕
});
當 Promise
的任務被完成的時候,我們就可以呼叫 resolve()
,然後將取得的資料傳遞出去。 或是說想要拒絕這個 Promise
,那麼就裡面呼叫 reject()
來拒絕他。 1
2
3
4
5
6
7
8
9
function myAsyncFunction ( url ) {
return new Promise (( resolve , reject ) => {
// resolve() or reject()
});
}
// 透過 .then() 來取代過去的 callback hell
myAsyncFunction (...)
. then (() => { ... });
async
與 await
在後來,從 Promise
物件又延伸出 async
與 await
兩個新特性,其實本質上是更簡便的語法糖。 假設我們有兩個非同步任務要處理,並且我們希望在 asyncFunc1
執行完成之後才去執行 asyncFunc2
: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function asyncFunc1 ( url ) {
return new Promise (( resolve , reject ) => {
// resolve() or reject()
});
}
function asyncFunc2 ( url ) {
return new Promise (( resolve , reject ) => {
// resolve() or reject()
});
}
const asyncCall = async () => {
const result1 = await asyncFunc1 ();
const result2 = await asyncFunc2 ();
};
像這樣,透過 async
與 await
我們就可以擺脫過去一層層 callback
的惡夢,程式碼也更加簡潔。
簡寫屬性 1
2
3
4
5
6
function createCoord ( x , y ) {
return {
x : x ,
y : y
}
}
1
2
3
4
5
6
function createCoord ( x , y ) {
return {
x ,
y
}
}
方法屬性 1
2
3
4
5
const math = {
add : function ( a , b ) { return a + b ; },
sub : function ( a , b ) { return a - b ; },
multiply : function ( a , b ) { return a * b ; }
}
1
2
3
4
5
const math = {
add ( a , b ) { return a + b ; },
sub ( a , b ) { return a - b ; },
multiply ( a , b ) { return a * b ; }
}
陣列方法 ES6 引入了許多有用的陣列方法,例如: find():查找陣列中的成員,返回 null 表示沒找到 findIndex():查找陣列成員的索引 some():檢查某個斷言是否至少一個成員在陣列中 includes:陣列是否包含某項目 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const array = [{ id : 1 , checked : true }, { id : 2 }];
arr . find ( item => item . id === 2 )
// >>> { id: 2 }
arr . findIndex ( item => item . id === 2 )
// >>> 1
arr . some ( item => item . checked )
// >>> true
const numberArray = [ 1 , 2 , 3 , 4 ];
numberArray . includes ( 2 );
// >>> true
ES6 的 class ES6 支持 class
語法,但不是新的對象繼承模型,只是原型鍊的語法糖。 函式中使用 static
關鍵字定義構造函式的方法與屬性: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Student {
constructor () {
console . log ( "I'm a student." );
}
study () {
console . log ( "study!" );
}
static read () {
console . log ( "Reading Now." );
}
}
console . log ( typeof Student );
// >>> Function
let stu = new Student ();
// >>> "I'm a student."
stu . study ();
// >>> "study!"
stu . read ();
// >>> "Reading Now."
class
的繼承(extends
)extends
允許一個子類繼承父類,需要注意的是,子類的 constructor 函式中需要執行 supre() 函式。當然你也可以在子類方法中調用父類的方法,如 supre.parentMethodName()
。 class
的聲明不會提升 hoisting
,如果你要使用某個 class
,那你必須在使用之前定義他,否則會拋出 reference error
的錯誤。在 class
中定義函式不需要使用 function
關鍵字。 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Phone {
constructor () {
console . log ( "I'm a phone." );
}
}
class MI extends Phone {
constructor () {
supre ();
console . log ( "I'm a phone designed by xiaomi." );
}
}
let mi8 = new MI ();
// >>> "I'm a phone."
// >>> "I'm a phone designed by xiaomi."
class
的 super
方法【詳細 MDN 文件】
語法 1
2
super ([ arguments ]); // calls the parent constructor.
super . functionOnParent ([ arguments ]);
當使用建構子 ,super
關鍵字必須出現 在 this
關鍵字之前使用,super
關鍵字也可以使用在呼叫函式與父對象。 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let parent = {
foo () {
console . log ( "Hello from the Parent" );
}
}
let child = {
foo () {
super . foo ();
console . log ( "Hello from the Child" );
}
}
Object . setPrototypeOf ( child , parent );
child . foo ();
// >>> Hello from the Parent
// >>> Hello from the Child
非同步處理工具 - Generator
(生成器函式) 【詳細 MDN 文件】
語法 1
2
3
4
5
6
7
8
function * gen () {
yield 1 ;
yield 2 ;
yield 3 ;
}
let g = gen ();
// "Generator { }"
方法 範例:一個無限迭代器 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function * idMaker () {
let index = 0 ;
while ( true )
yield index ++ ;
}
let gen = idMaker (); // "Generator { }"
console . log ( gen . next (). value );
// >>> 0
console . log ( gen . next (). value );
// >>> 1
console . log ( gen . next (). value );
// >>> 2
// ...
生成器對象 Generator.prototype.next():返回 yield 表達式生成的值。 Generator.prototype.close():關閉生成器,因此執行該函式後調用next()方法時將會拋出 StopIteration
錯誤。 Generator.prototype.send():用於將值發送到生成器。該值由yield 表達式返回,並且返回下一個yield 表達式生成的值。 Generator.prototype.throw():向生成器拋出錯誤。 Licensed under CC BY-NC-SA 4.0