ES6新特性


ECMAScript 6 是ECMA於2015.06發布的版本,作為一個分界點,現在我們通常把這之后的版本統稱為ES6。ES6帶來了許多全新的語法,同時添加了類的概念,可以預見的是,JavaScript正朝着工程化語言邁進,我們並不知道這對於年輕的JavaScript來說是好還是壞,因為它最開始是做為一款輕量級的腳本語言而風靡全球的。

 

一  新的原始類型和變量聲明

  1,symbol

  在ES6之前,我們知道JavaScript支持6種數據類型:object,string,boolean,number,null,undefined。現在,ES6新增了一種原始數據類型:symbol,表示獨一無二的值,即每個symbol類型的值都不相同。這讓我想起了另一個特殊的值:NaN,想一想,他們是不是有一點類似呢!

1 var sy = Symbol('test'); 2 var sy1 = Symbol('test'); 3 console.log(tepeof sy);//'symbol'
4 sy == sy1;//false
5 var sy2 = new Symbol('test');//error : Symbol is not a constructor

  創建symbol數據類型的值時,需要給Symbol函數傳遞一個字符串,並且有一點特殊的是:不能使用new關鍵字調用它。另外,每個symbol類型值都是獨一無二的,即使傳遞的是相同的字符串。

  2,let和const

  ES6新增了兩個聲明變量的關鍵字:let和const。

  他們聲明的變量僅在let和const關鍵字所在的代碼塊內起作用,即在使用let和const的那一對大括號{}內起作用,也稱塊級作用域(ES6之前只有函數作用域和全局作用域)。

  let和const聲明變量不會在預編譯過程中有提升行為(在全局聲明也不會變成window的屬性),且同一變量不能重復聲明。所以要使用這類變量,只能在let和const關鍵字之后使用它們。

  let和const關鍵字還有一個特性:“暫時性死區”,即在使用了該關鍵字的塊級作用域中,其內部使用let和const關鍵字聲明的變量與外部作用域中的變量相互隔絕,互不影響。即使是同名變量。

1 var a = 1; 2 { 3     console.log(a);//error Cannot access 'a' before initialization
4     let a = 0; 5     console.log(a);//0
6 } 7 console.log(a);//1

  const用來聲明一個常量,聲明時必須賦值,且一旦聲明就不能改變。

  其實說const變量不能更改是不准確的,請看下面的例子:

1 const obj = { 2     name:'ren', 3     age:12
4 }; 5 obj = {};//error
6 obj.sex = male; 7 consol.log(obj);//{name:'ren',age:12;sex:'male'}

  const聲明的如果是一個原始值,那么上面的說法是准確的,如果const聲明的是一個引用值,那么更准確的說法應該是一個不能被重新賦值的變量。

  3,解構賦值 

  解構賦值是對賦值運算符的擴展。它是一種針對數組或者對象進行模式匹配,然后對其中的變量進行賦值。

let [a,b,c] = [1,2,3]; console.log(a,b,c);//1,2,3
************************** let [a,b,c] = [1,,3]; console.log(a,b,c);//1,undefined,3
************************** let [a,,b] = [1,2,3]; console.log(a,b);//1,3
************************** let [a,..b] = [1,2,3];//...是剩余運算符,表示賦值運算符右邊除第一個值外剩余的都賦值給b
console.log(a,b);//1,[2,3]

  事實上所有可枚舉(iterable)的對象都可以使用結構賦值,例如數組,字符串對象,以及ES6新增的Map和Set類型。

1 let arr = 'hello'; 2 let [a,b,c,d,e] = arr; 3 console.log(a,b,c,d,e);//'h','e','l','l','o'

  對象的解構賦值和數組類似,不過左邊的變量名需要使用對象的屬性名,並且用大括號{}而非中括號[]:

1 let obj = {name:'ren',age:12,sex:'male'}; 2 let {name,age,sex} = obj; 3 console.log(name,age,sex);//'ren' 12 'male'
4 let {name:myName,age:myAge,sex:mySex} = obj;//自定義變量名
5 console.log(myName,myAge,mySex);//'ren' 12 'male'

 

 

二  新的對象和方法

  1,Map和Set

  Map對象用於保存鍵值對,任何值JavaScript支持的值都可以作為一個鍵或者一個值。這聽起來和對象差不多啊?其實它們還是有區別的:

    a) object的鍵只能是字符串或ES6的symbol值,而Map可以是任何值。

    b) Map對象有一個size屬性,存儲了鍵值對的個數,而object對象沒有類似屬性。

1 let myMap = new Map([['name','ren'],['age',12]]); 2 console.log(myMap);//{'name'=>'ren','age'=>12}
3 myMap.set('sex','male'); 4 console.log(myMap);//{'name'=>'ren','age'=>12,'sex'=>'male'}
5 myMap.get('name');//'ren'
6 myMap.has('age');//true
7 myMap.delete('age');//true
8 myMap.has('age');//false
9 myMap.get('age');//undefined

  Map構造函數接收一個二維數組來創建一個Map對象。數組元素的第0位表示Map對象的key,第1位表示Map對象的value。

  Map對象使用set方法來新增數據,set方法接收兩個參數,第一個表示key,第二個表示value。使用get方法獲取數據,參數是對象的key。

  Map對象使用delete方法來刪除數據,接收一個參數,表示需要被刪除的key。

  Map對象使用has方法檢測是否已經具有某個屬性,返回boolean值。

  Set對象和Map對象類似,但它是用來存儲一組唯一值的,而不是鍵值對。類似數組,但它的每個元素都是唯一的。

1 let mySet = new Set([1,2,3]); 2 console.log(mySet);//{1,2,3}
3 mySet.add(4); 4 console.log(mySet);//{1,2,3,4}
5 mySet.delete(1);//true
6 mySet.has(1);//false

  利用Set對象唯一性的特點,可以輕松實現數組的去重:

1 let arr = [1,1,2,3,4,4]; 2 let mySet = new Set(arr); 3 let newArr = Array.from(mySet); 4 console.log(newArr);//[1,2,3,4]

  2,對象新特性

  創建對象的字面量方式可以更加簡潔。直接使用變量名作為屬性,函數體作為方法,最終變量值變成屬性值,函數名變成方法名。

 1 let name = 'ren';  2 let age = 12;  3 let myself = {  4  name,  5  age,  6  say(){  7         console.log(this.name);  8  }  9 }; 10 console.log(myself);//{name:'ren',age:12,say:fn}
11 myself.say();//'ren'

  對象的拓展運算符(...)三點。用於拷貝目標對象所有可遍歷的屬性到當前對象。

1 let obj = {name:'ren',age:12}; 2 let person = {...obj}; 3 console.log(person);//{name:'ren',age:12}
4 obj == person;//false
5 let another = {sex:'male'}; 6 let someone = {...person,...another};//合並對象 7 console.log(someone);//{name:'ren',age:12,sex:'male'}

  ES6對象新增了兩個方法,assign和is。

  assign用於淺拷貝源對象可枚舉屬性到目標對象。

1 let source = {a:{ b: 1},b: 2}; 2 let target = {c: 3}; 3 Object.assign(target, source); 4 console.log(target);//{c: 3, a: {b:1}, b: 2}
5 source.a.b = 2; 6 console.log(target.a.b);//2

  如果有同名屬性,那么目標對象的屬性值會被源對象的屬性值覆蓋。所以數組的表現就有一點特別了:

1 Object.assign([1,2,3],[11,22,33,44]);//[11,22,33,44]

  數組的index就是屬性名,當使用assign方法時,從第0位開始,目標數組的值便開始被源數組的值覆蓋了。

  is方法和(===)功能基本類似,用於判斷兩個值是否絕對相等。

1 Object.is(1,1);//true
2 Object.is(1,true);//false
3 Object.is([],[]);//false
4 Object.is(+0,-0);//false
5 Object.is(NaN,NaN);//true

  他們僅有的兩點區別是,is方法可以區分+0還是-0,還有就是它認為NaN是相等的。

  3,字符串新方法

  includes()判斷字符串是否包含參數字符串,返回boolean值。如果想要知道參數字符串出現的位置,還是需要indexOf或lastIndexOf方法。

  startsWith()/endsWith(),判斷字符串是否以參數字符串開頭或結尾。返回boolean值。這兩個方法可以有第二個參數,一個數字,表示開始查找的位置。

1 let str = 'blue,red,orange,white'; 2 str.includes('blue');//true
3 str.startsWith('blue');//true
4 str.endsWith('blue');//false

  repeat()方法按指定次數返回一個新的字符串。如果次數是大於0的小數則向下取整,0到-1之間的小數則向上取整,其他負數將拋出錯誤。

1 console.log('hello'.repeat(2));//'hellohello'
2 console.log('hello'.repeat(1.9));//'hello'
3 console.log('hello'.repeat(-0.9));//''
4 console.log('hello'.repeat(-1.9));//error

  padStart()/padEnd(),用參數字符串按給定長度從前面或后面補全字符串,返回新字符串。

1 let arr = 'hell'; 2 console.log(arr.padEnd(5,'o'));//'hello'
3 console.log(arr.padEnd(6,'o'));//'helloo'
4 console.log(arr.padEnd(6));//'hell ',如果沒有指定將用空格代替
5 console.log(arr.padStart(5,'o'));//'ohell'

  另外,如果字符串加上補全的字符串超出了給定的長度,那么,超出的部分將被截去。

  4,數組的新方法

  of()是ES6新增的用於創建數組的方法。of把傳入的參數當做數組元素,形成新的數組。

1 let arr = Array.of(1,'2',[3],{}); 2 console.log(arr);//[1,'2',[3],{}]

  from()方法可以將可迭代對象轉換為新的數組。函數可接受3個參數:第一個表示將被轉換的可迭代對象,第二個是回調函數,將對每個數組元素應用該回調函數,然后返回新的值到新數組,第三個是回到函數內this的指向。后兩個參數是可選的。

1 let obj = { 2     double(n) { 3         return n * 2; 4  } 5 } 6 let arr = [1, 2, 3]; 7 console.log(Array.from(arr, function (n){ 8     return this.double(n); 9 }, obj)); // [2, 4, 6]

  find()和findIndex(),查找數組中符合條件的元素值或索引,方法不會修改原數組。

  接受一個回調函數作為參數,函數可以接受四個參數,分別是當前遍歷到的元素,當前遍歷到的索引,數組本身以及函數內this的指向。方法會把回調函數作用於每一個遍歷到的元素,如果遍歷到某一個元素可以使回調函數返回true,那么find方法會立即返回該元素,findIndex方法會返回該元素的索引。並終止繼續遍歷。

  如果有多個符合條件的,也將只返回第一個。如果遍歷完整個數組也無法是回調函數返回true,那么find方法將返回undefined,findIndex方法將返回-1。

1 let arr = [1,2,3,4,5];
2 console.log(arr.find((ele) => {
3     return ele === 1;
4 }));//1
5 console.log(arr.findIndex((ele) => {
6     return ele > 4;
7 }));//4

 

  fill()/copyWithin(),替換數組中部分元素,會修改原數組。

1 let arr = [1,2,3,4,5]; 2 console.log(arr.fill(0,0,3));//[0,0,0,4,5]
3 //參數1表示目標值,參數2,3表示替換的始末位置,左閉右開區間。
4 console.log(arr.copyWithin(0,2,4));//[0,4,0,4,5]
5 //參數1表示修改的起始位置,參數2,3表示用來替換的數據的始末位置,左閉右開區間。

  fill()用指定的值替換,copyWithin()使用數組中原有的某一部分值替換。

  includes()用於檢測數組是否包含某個值,可以指定開始位置。

1 let arr = [1,2,3,4,5]; 2 console.log(arr.includes(2));//true
3 console.log(arr.includes(1,1));//false

  

三  函數

  1,參數默認值

  ES6首次添加了參數默認值。我們再也不用在函數內部編寫容錯代碼了。

1 function add(a=1,b=2){ 2     return a + b; 3 } 4 add();//3
5 add(2);//4
6 add(3,4);//7

  和參數默認值一起,ES6還帶來了不定參。它的功能和使用arguments差不多。

1 function add(...num){ 2     return num.reduce(function(result,value){ 3         return result + value; 4  }); 5 } 6 add(1,2,3,4);//10

  下面介紹的箭頭函數沒有arguments屬性,如果箭頭函數內要實現不定參,上述方式就是一個不錯的選擇了。

  2,箭頭函數

  箭頭函數實現了一種更加簡潔的書寫方式,並且也解決了關鍵字聲明方式的一些麻煩事兒。箭頭函數內部沒有arguments,也沒有prototype屬性,所以不能用new關鍵字調用箭頭函數。

  箭頭函數的書寫方式:參數 => 函數體。

1 let add = (a,b) => { 2     return a+b; 3 } 4 let print = () => { 5     console.log('hi'); 6 } 7 let fn = a => a * a; 8 //當只有一個參數時,括號可以省略,函數體只有單行return語句時,大括號也可以省略,強烈建議不要省略它們,是真的難以閱讀

  當函數需要直接返回對象時,你必須使用小括號把對象包裹起來。否則將拋出錯誤。

1 const fn = () =>{name:'ren',age:12}; 2 // SyntaxError
3 *******************************************
4 const fn = () =>({name:'ren',age:12});

  箭頭函數和普通函數最大的區別在於其內部this永遠指向其父級AO對象的this。

  普通函數在預編譯環節會在AO對象上添加this屬性,保存一個對象(請參照《JavaScript之深入對象(二)》)。每個普通函數在執行時都有一個特定的this對象,而箭頭函數執行時並不直接擁有this屬性,如果你在箭頭函數中使用this,將根據函數作用域鏈,直接引用父級AO對象上this綁定的對象。普通函數的AO對象只有在函數執行時才產生,換言之,普通函數的this是由函數執行時的環境決定。而箭頭函數的特別之處在於,當函數被定義時,就引用了其父級AO對象的this,即箭頭函數的this由定義時的環境決定。

  根據箭頭函數的特點,不難推測:如果定義對象的方法直接使用箭頭函數,那么函數內的this將直接指向window。

1  var age = 123; 2  let obj = { 3      age:456, 4      say:() => { 5          console.log(this.age); 6  } 7  }; 8 obj.say();//123
9 //對象是沒有執行期上下文的(AO對象),定義對象的方法實際上是在全局作用域下,即window

   如果你一定要在箭頭函數中讓this指向當前對象,其實也還是有辦法的(但是沒必要這么麻煩啊,直接使用普通函數不是更好嗎?):

 1  var age = 123;  2  let obj = {  3      age:456,  4      say:function(){  5          var fn = () => {  6          console.log(this.age);  7  }  8          return fn();  9  } 10  }; 11 obj.say();//456

  我們來分析一下這是怎么做到的:首先,我們使用obj調用say方法時,say內創建了AO對象,並且該AO對象的this屬性指向了obj(這里不明白的請回去復習一下我的《JavaScript之深入函數/對象》),然后,say內部又聲明了一個箭頭函數。我們說箭頭函數在聲明時就要強行引用父級AO的this屬性,那么現在該箭頭函數的父級AO是誰呢?當然就是say的AO啦,所以這里箭頭函數的this直接就綁定了obj,最后箭頭函數在執行時拿到的this,實際上就是say方法的AO.this,即obj本身。

  上面是在對象中使用箭頭函數,如果那讓你難於理解,那么請看下面這種方式:在普通函數中使用箭頭函數。

1 var obj = {name:'ren'}; 2 function test(){ 3     var fn = () => { 4         console.log(this); 5  }; 6  fn(); 7 } 8 test();//window
9 test.call(obj);//{name:'ren'}

  test函數在全局執行時,其this指向window,這時也產生了箭頭函數的定義,於是箭頭函數內的this也被指向了window,所以最終打印出window對象。

  當我們手動改變test函數執行時this的指向時,箭頭函數定義所綁定的this實際上也被我們修改了。所以最終打印出obj。

 

四  class(類)  

  class 作為對象的模板被引入ES6,你可以通過 class 關鍵字定義類。class 的本質依然是一個函數。

  1,創建類

 1 class Ex {//關鍵字聲明方式
 2  constructor(name){  3         this.name = name;  4         this.say = () => {  5             console.log(this.name);  6  }  7  }  8  methods(){  9         console.log('hello ' + this.name); 10  } 11     static a = 123; 12     static m = () => { 13         console.log(this.a); 14  }; 15 } 16 //let ex = class{} 字面量方式
17 var example = new Ex('ren'); 18 example.say();//'ren'
19 Ex.m();//123
20 example.methods();//'hello ren'
  constructor是創建類必須的方法,當使用new調用類創建實例時,將自動執行該方法,該方法和構造函數類似,默認返回this對象。實例的方法和屬性都定義在constructor內部。相當於構造函數的this方式。
  類保留了prototype屬性,類中的方法不需要使用function關鍵字,並且方法之間不需要逗號隔開。類中定義的方法實際上還是保存在類的prototype屬性上
  使用static關鍵字定義類的靜態屬性和方法。類中不能定義共有屬性,要想定義實例的共有屬性還是需要使用prototype屬性:Ex.prototype.屬性名 = 屬性值。

  創建實例依然使用new關鍵字。

  2,類的繼承
  類的繼承通過extends關鍵字實現。
 1 class Person {  2  constructor (name,age){  3         this.name = name;  4         this.age = age;  5  }  6  say(){  7         console.log(this.name + ':' + this.age);  8  }  9 } 10 class Student extends Person{ 11  constructor (name,age,sex){ 12  super(name,age); 13         this.sex = sex; 14  } 15 } 16 var student = new Student('ren',12,'male'); 17 student.name;//'ren'
18 student.sex;//'male'
19 student.say();//'ren:12'

  子類繼承自父類,不會隱式的創建自己的this對象,而是通過super()引用父類的this。這個過程和在子構造函數內使用父構造函數call(this)很像,但他們有本質的區別。另外,ES6規定,super()必須在子類的this之前執行。所以一般我們把super()放在子類constructor方法的第一行,這樣准沒錯!

 

五  模塊導入和導出

  1,導入

  ES6使用關鍵字 import 導入模塊(文件),有兩種常用的方式:

1 import ‘模塊名稱’ from ‘路徑’;
2 import  ‘路徑’;

  通過 import...from 的方式引入模塊,模塊名稱實際上相當於定義一個變量,用來接收即將導入的模塊。

  路徑可以有很多方式,既可以是絕對路徑,也可以是相對路徑,甚至只是一個簡單的模塊名稱,更甚至連文件后綴都不需要。當你使用該命令時,系統會自動從配置文件中指定的路徑中去尋找並加載正確的文件。

import Vue from "vue";
//完整路勁其實是 "../node_modules/vue/dist/vue.js";

  通過 import... 的方式一般用來引入樣式文件或預處理文件,因為他們不需要用變量來接收。

  2,導出

  ES6 通過 export 和export default 導出模塊。導出的含義是向外暴露、輸出,在一個文件中通過 import 導入另一個文件,通過變量即可以接收到導出的數據了。一般情況下,JS文件可以向外輸出變量、函數和對象。

1 let name = 'ren',age = 12;
2 export {name,age};
3 //注意:變量需要用大括號包裹,然后才能向外輸出

  如果僅需向外暴露一個變量:

1 export var name = 'ren';

  使用 export 向外輸出函數的用法和變量相同,這里不再舉例。

  總結:使用 export 向外輸出成員時,可以同時輸出多個,並且必須用‘{}’大括號包裹,在其他地方使用 import 導入時,接收成員的變量名必須和這里輸出的名稱一致,同時,可以根據實際情況,僅接收實際需要的的成員(接收的時候也要用大括號包裹)。

  如果希望通過 export 向外暴露成員,並且在導入的時候自定義接收名稱,那么你可以使用 as 關鍵字重命名。

1 let name = 'ren', age = 12;
2 export {name, age};
3 ************************************
4 import {name as myName, age as myAge} from 'url';

  與 export 相比,export default 有以下幾點不同:首先,在同一個模塊中,export default 只允許向外暴露一次成員;然后,這種方式可以使用任意的名稱接收,不像 export 那樣有嚴格的要求;最后,export 和 export default 可以在同一模塊中同時存在。

1 let person = {name:'ren'};
2 let age = 12;
3 let address = 'cd';
4 export default person;
5 export {age};
6 export {address};
7 **************************
8 import man,{age,address} from 'url'

   小技巧:通常 import 無法直接被瀏覽器識別,即如果在HTML文檔中引入的 JS 文件直接使用了 import 關鍵字,瀏覽器會報錯。要想直接在被HTML文檔引用的 JS 文件中使用 import,需要給該 <script> 標簽添加type屬性,且其值應該是 module。

 

六  異步機制

  ES6新增了兩種實現異步的新機制,Promise和Generator。文筆有限,怕講的不清楚,誤人子弟,請有興趣的同學去下面的鏈接繼續學習,廖老師的教程也是受很多人推崇的,當然MDN更官方。(實際上是需要較大篇幅才能講明白,這里就偷個懶了)

  1,Promise

  https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

  https://www.liaoxuefeng.com/wiki/1022910821149312/1023024413276544

  2,Generator

  https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Generator

  https://www.liaoxuefeng.com/wiki/1022910821149312/1023024381818112

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM