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 與 constvar 、 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