1. 程式人生 > >es6常用的語法,和不得不知的es6小技巧

es6常用的語法,和不得不知的es6小技巧

1. 強制要求引數

ES6提供了預設引數值機制,允許你為引數設定預設值,防止在函式被呼叫時沒有傳入這些引數。
在下面的例子中,我們寫了一個required()函式作為引數a和b的預設值。這意味著如果a或b其中有一個引數沒有在呼叫時傳值,會預設required()函式,然後丟擲錯誤。

`const required = () => {``throw` `new` `Error(``'Missing parameter'``)};`
`const add = (a = required(), b = required()) => a + b;`
`add(1, 2)` `//3`
`add(1)` `// Error: Missing parameter.`

2. 強大的reduce

陣列的reduce方法用途很廣。它一般被用來把陣列中每一項規約到單個值。但是你可以利用它做更多的事。

2.1 使用reduce同時實現map和filter

假設現在有一個數列,你希望更新它的每一項(map的功能)然後篩選出一部分(filter的功能)。如果是先使用map然後filter的話,你需要遍歷這個陣列兩次。
在下面的程式碼中,我們將數列中的值翻倍,然後挑選出那些大於50的數。有注意到我們是如何非常高效地使用reduce來同時完成map和filter方法的嗎?

`const numbers = [10, 20, 30, 40];`
`const doubledOver50 = numbers.reduce((finalList, num) => {`
`num = num * 2; `
`if` `(num > 50) {`
`finalList.push(num);`
`}`
`return` `finalList;`
`}, []);`
`doubledOver50;` `// [60, 80]`
歡迎加入全棧開發交流划水交流圈:582735936
面向划水1-3年前端人員
幫助突破划水瓶頸,提升思維能力

2.2 使用reduce取代map和filter

如果你認真閱讀了上面的程式碼,你應該能理解reduce是可以取代map和filter的。

2.3 使用reduce匹配圓括號

reduce的另外一個用途是能夠匹配給定字串中的圓括號。對於一個含有圓括號的字串,我們需要知道(和)的數量是否一致,並且(是否出現在)之前。
下面的程式碼中我們使用reduce可以輕鬆地解決這個問題。我們只需要先宣告一個counter變數,初值為0。在遇到(時counter加一,遇到)時counter減一。如果左右括號數目匹配,那最終結果為0。

`//Returns 0 if balanced.`
`const isParensBalanced = (str) => {`
`return` `str.split(``''``).reduce((counter, char) => {`
`if``(counter < 0) {` `//matched ")" before "("`
`return` `counter;`
`}` `else` `if``(char ===` `'('``) {`
`return` `++counter;`
`}` `else` `if``(char ===` `')'``) {`
`return` `--counter;`
`}` `else` `{` `//matched some other char`
`return` `counter;`
`}`
`}, 0);` `//<-- starting value of the counter}`
`isParensBalanced(``'(())'``)` `// 0 <-- balanced`
`isParensBalanced(``'(asdfds)'``)` `//0 <-- balanced`
`isParensBalanced(``'(()'``)` `// 1 <-- not balanced`
`isParensBalanced(``')('``)` `// -1 <-- not balanced`
歡迎加入全棧開發交流划水交流圈:582735936
面向划水1-3年前端人員
幫助突破划水瓶頸,提升思維能力

2.4 統計陣列中相同項的個數

很多時候,你希望統計陣列中重複出現項的個數然後用一個物件表示。那麼你可以使用reduce方法處理這個陣列。
下面的程式碼將統計每一種車的數目然後把總數用一個物件表示。

`var` `cars = [``'BMW'``,``'Benz'``,` `'Benz'``,` `'Tesla'``,` `'BMW'``,` `'Toyota'``];`
`var` `carsObj = cars.reduce(``function` `(obj, name) {`
`obj[name] = obj[name] ? ++obj[name] : 1;`
`return` `obj;`
`}, {});`
`carsObj;` `// => { BMW: 2, Benz: 2, Tesla: 1, Toyota: 1 }`

reduce的其他用處實在是太多了,建議閱讀MDN的相關程式碼示例。

3. 物件解構

3.1 刪除不需要的屬性

有時候你不希望保留某些物件屬性,也許是因為它們包含敏感資訊或僅僅是太大了(just too big)。你可能會列舉整個物件然後刪除它們,但實際上只需要簡單的將這些無用屬性賦值給變數,然後把想要保留的有用部分作為剩餘引數就可以了。
下面的程式碼裡,我們希望刪除_internal和tooBig引數。我們可以把它們賦值給internal和tooBig變數,然後在cleanObject中儲存剩下的屬性以備後用。

`let {_internal, tooBig, ...cleanObject} = {el1:` `'1'``, _internal:``"secret"``, tooBig:{}, el2:` `'2'``, el3:` `'3'``};`
`console.log(cleanObject);` `// {el1: '1', el2: '2', el3: '3'}`

3.2 在函式引數中解構巢狀物件

在下面的程式碼中,engine是物件car中巢狀的一個物件。如果我們對engine的vin屬性感興趣,使用解構賦值可以很輕鬆地得到它。

`var` `car = {`
`model:` `'bmw 2018'``,`
`engine: {`
`v6:` `true``,`
`turbo:` `true``,`
`vin: 12345`
`}`
`}`
`const modelAndVIN = ({model, engine: {vin}}) => {`
`console.log(`model: ${model} vin: ${vin}`);`
`}`
`modelAndVIN(car);` `// => model: bmw 2018 vin: 12345`

3.3 合併物件

ES6帶來了擴充套件運算子(...)。它一般被用來解構陣列,但你也可以用它處理物件。
接下來,我們使用擴充套件運算子來展開一個新的物件,第二個物件中的屬性值會改寫第一個物件的屬性值。比如object2的b和c就會改寫object1的同名屬性。

`let object1 = { a:1, b:2,c:3 }`
`let object2 = { b:30, c:40, d:50}`
`let merged = {…object1, …object2}` `//spread and re-add into merged`
`console.log(merged)` `// {a:1, b:30, c:40, d:50}`

4. Sets

4.1 使用Set實現陣列去重

在ES6中,因為Set只儲存唯一值,所以你可以使用Set刪除重複項。

`let arr = [1, 1, 2, 2, 3, 3];`
`let deduped = [...``new` `Set(arr)]` `// [1, 2, 3]`

4.2 對Set使用陣列方法

使用擴充套件運算子就可以簡單的將Set轉換為陣列。所以你可以對Set使用Array的所有原生方法。
比如我們想要對下面的Set進行filter操作,獲取大於3的項。

`let mySet =` `new` `Set([1,2, 3, 4, 5]);`
`var` `filtered = [...mySet].filter((x) => x > 3)` `// [4, 5]`

5. 陣列解構

有時候你會將函式返回的多個值放在一個數組裡。我們可以使用陣列解構來獲取其中每一個值。

5.1 數值交換

`let param1 = 1;`
`let param2 = 2;`
`//swap and assign param1 & param2 each others values`
`[param1, param2] = [param2, param1];`
`console.log(param1)` `// 2`
`console.log(param2)` `// 1`

5.2 接收函式返回的多個結果

在下面的程式碼中,我們從/post中獲取一個帖子,然後在/comments中獲取相關評論。由於我們使用的是async/await,函式把返回值放在一個數組中。而我們使用陣列解構後就可以把返回值直接賦給相應的變數。

`async` `function` `getFullPost(){`
`return` `await Promise.all([`
`fetch(``'/post'``),`
`fetch(``'/comments'``)`
`]);`
`}`
`const [post, comments] = getFullPost();`
歡迎加入全棧開發交流划水交流圈:582735936
面向划水1-3年前端人員
幫助突破划水瓶頸,提升思維能力

什麼是ES6?

ECMAScript 6(以下簡稱ES6)是JavaScript語言的下一代標準,已經在2015年6月正式釋出了。Mozilla公司將在這個標準的基礎上,推出JavaScript 2.0。

ECMAScript和JavaScript到底是什麼關係?很多初學者會感到困惑,簡單來說,ECMAScript是JavaScript語言的國際標準,JavaScript是ECMAScript的實現。

一、let和const

在JavaScript中咱們以前主要用關鍵var來定義變數,ES6之後,新增了定義變數的兩個關鍵字,分別是let和const。
對於變數來說,在ES5中var定義的變數會提升到作用域中所有的函式與語句前面,而ES6中let定義的變數則不會,let宣告的變數會在其相應的程式碼塊中建立一個暫時性死區,直至變數被宣告。
let和const都能夠宣告塊級作用域,用法和var是類似的,let的特點是不會變數提升,而是被鎖在當前塊中。

一個非常簡單的例子:

`function` `test() {`
`if``(``true``) {`
`console.log(a)``//TDZ,俗稱臨時死區,用來描述變數不提升的現象`
`let a = 1`
`}`
`}`
`test()` `// a is not defined`
`function` `test() {`
`if``(``true``) {`
`let a = 1`
`}`
`console.log(a)`
`}`
`test()` `// a is not defined`

唯一正確的使用方法:先宣告,再訪問。

`function` `test() {`
`if``(``true``) {`
`let a = 1`
`console.log(a)`
`}`
`}`
`test()` `// 1`
歡迎加入全棧開發交流划水交流圈:582735936
面向划水1-3年前端人員
幫助突破划水瓶頸,提升思維能力

const

宣告常量,一旦宣告,不可更改,而且常量必須初始化賦值。
const雖然是常量,不允許修改預設賦值,但如果定義的是物件Object,那麼可以修改物件內部的屬性值。

`const type = {`
`a: 1`
`}`
`type.a = 2` `//沒有直接修改type的值,而是修改type.a的屬性值,這是允許的。`
`console.log(type)` `// {a: 2}`

const和let的異同點

相同點:const和let都是在當前塊內有效,執行到塊外會被銷燬,也不存在變數提升(TDZ),不能重複宣告。

不同點:const不能再賦值,let宣告的變數可以重複賦值。
const實際上保證的,並不是變數的值不得改動,而是變數指向的那個記憶體地址所儲存的資料不得改動。對於簡單型別的資料(數值、字串、布林值),值就儲存在變數指向的那個記憶體地址,因此等同於常量。但對於複合型別的資料(主要是物件和陣列),變數指向的記憶體地址,儲存的只是一個指向實際資料的指標,const只能保證這個指標是固定的(即總是指向另一個固定的地址),至於它指向的資料結構是不是可變的,就完全不能控制了。因此,將一個物件宣告為常量必須非常小心。

塊級作用域的使用場景

除了上面提到的常用宣告方式,我們還可以在迴圈中使用,最出名的一道面試題:迴圈中定時器閉包的考題
for迴圈中使用var宣告的迴圈變數,會跳出迴圈體汙染當前的函式。

`for``(``var` `i = 0; i < 5; i++) {`
`setTimeout(() => {`
`console.log(i)` `//5, 5, 5, 5, 5`
`}, 0)`
`}`
`console.log(i)` `//5 i跳出迴圈體汙染外部函式`
`//將var改成let之後`
`for``(let i = 0; i < 5; i++) {`
`setTimeout(() => {`
`console.log(i)` `// 0,1,2,3,4`
`}, 0)`
`}`
`console.log(i)``//i is not defined i無法汙染外部函式`

在實際開發中,我們選擇使用var、let還是const,取決於我們的變數是不是需要更新,通常我們希望變數保證不被惡意修改,而使用大量的const。使用const宣告,宣告一個物件的時候,也推薦使用const,當你需要修改宣告的變數值時,使用let,var能用的場景都可以使用let替代。

symbol

ES6 以前,我們知道5種基本資料型別分別是Undefined,Null,Boolean,Number以及String,然後加上一種引用型別Object構成了JavaScript中所有的資料型別,但是ES6出來之後,新增了一種資料型別,名叫symbol,像它的名字表露的一樣,意味著獨一無二,意思是每個 Symbol型別都是獨一無二的,不與其它 Symbol 重複。
可以通過呼叫 Symbol() 方法將建立一個新的 Symbol 型別的值,這個值獨一無二,不與任何值相等。

`var` `mySymbol=Symbol();`
`console.log(``typeof` `mySymbol)` `//"symbol"`

二、字串

ES6字串新增的方法
UTF-16碼位:ES6強制使用UTF-16字串編碼。關於UTF-16的解釋請自行百度瞭解。
codePointAt():該方法支援UTF-16,接受編碼單元的位置而非字串位置作為引數,返回與字串中給定位置對應的碼位,即一個整數值。
String.fromCodePoiont():作用與codePointAt相反,檢索字串中某個字元的碼位,也可以根據指定的碼位生成一個字元。
normalize():提供Unicode的標準形式,接受一個可選的字串引數,指明應用某種Unicode標準形式。
在ES6中,新增了3個新方法。每個方法都接收2個引數,需要檢測的子字串,以及開始匹配的索引位置。

模板字串

字串是JavaScript中基本型別之一,應該算是除了物件之外是使用最為頻繁的型別吧,字串中包含了例如substr,replace,indexOf,slice等等諸多方法,ES6引入了模板字串的特性,用反引號來表示,可以表示多行字串以及做到文字插值(利用模板佔位符)。

`// 以前的多行字串我們這麼寫:`
`console.log(``"hello world 1\n\`
`hello cala"``);`
`// "hello world`
`// hello cala"`
`//有了模板字串之後`
`console.log(`hello world`
`string text line 2`);`
`// "hello world`
`// hello cala"`

可以用${}來表示模板佔位符,可以將你已經定義好的變數傳進括弧中,例如:

`var` `name=``"cala"``;`
`var` `age=22;`
`console.log(`hello,I``'am ${name},my age is ${age}`)`
`//hello,I'``am cala,my age is 22`
`includes(str, index):如果在字串中檢測到指定文字,返回``true``,否則``false``。`
`let t =` `'abcdefg'`
`if``(t.includes(``'cde'``)) {`
`console.log(2)`
`}`
`//true`
`startsWith(str, index):如果在字串起始部分檢測到指定文字,返回``true``,否則返回``false``。`
`let t =` `'abcdefg'`
`if``(t.startsWith(``'ab'``)) {`
`console.log(2)`
`}`
`//true`
`endsWith(str, index):如果在字串的結束部分檢測到指定文字,返回``true``,否則返回``false``。`
`let t =` `'abcdefg'`
`if``(t.endsWith(``'fg'``)) {`
`console.log(2)`
`}`
`//true`

如果你只是需要匹配字串中是否包含某子字串,那麼推薦使用新增的方法,如果需要找到匹配字串的位置,使用indexOf()。

三、函式

函式的預設引數

在ES5中,我們給函式傳引數,然後在函式體內設定預設值,如下面這種方式。

`function` `a(num, callback) {`
`num = num || 6`
`callback = callback ||` `function` `(data) {console.log(``'ES5: '``, data)}`
`callback(num * num)`
`}`
`a()` `//ES5: 36,不傳參輸出預設值`
`//你還可以這樣使用callback`
`a(10,` `function``(data) {`
`console.log(data * 10)` `// 1000, 傳參輸出新數值`
`})`

在ES6中,我們使用新的預設值寫法

`function` `a(num = 6, callback =` `function` `(data) {console.log(``'ES6: '``, data)}) {`
`callback(num * num)`
`}`
`a()` `//ES6: 36, 不傳參輸出預設值`
`a(10,` `function``(data) {`
`console.log(data * 10)` `// 1000,傳參輸出新數值`
`})`

四、箭頭函式(=>)

(箭頭函式比較重要,現在簡單提一下,遲一點有空專門寫一篇箭頭函式的文章。)

`const arr = [5, 10]`
`const s = arr.reduce((sum, item) => sum + item)`
`console.log(s)` `// 15`

箭頭函式中this的使用跟普通函式也不一樣,在JavaScript的普通函式中,都會有一個自己的this值,主要分為:

普通函式:

1、函式作為全域性函式被呼叫時,this指向全域性物件
2、函式作為物件中的方法被呼叫時,this指向該物件
3、函式作為建構函式的時候,this指向建構函式new出來的新物件
4、還可以通過call,apply,bind改變this的指向

箭頭函式:

1、箭頭函式沒有this,函式內部的this來自於父級最近的非箭頭函式,並且不能改變this的指向。
2、箭頭函式沒有super
3、箭頭函式沒有arguments
4、箭頭函式沒有new.target繫結。
5、不能使用new
6、沒有原型
7、不支援重複的命名引數。

箭頭函式的簡單理解

1、箭頭函式的左邊表示輸入的引數,右邊表示輸出的結果。

`const s = a => a`
`cosole.log(s(2))` `// 2`

2、在箭頭函式中,this屬於詞法作用域,直接由上下文確定,對於普通函式中指向不定的this,箭頭函式中處理this無疑更加簡單,如下:

`//ES5普通函式`
`function` `Man(){`
`this``.age=22;`
`return` `function``(){`
`this``.age+1;`
`}`
`}`
`var` `cala=``new` `Man();`
`console.log(cala())``//undefined`
`//ES6箭頭函式`
`function` `Man(){`
`this``.age=22;`
`return` `() =>` `this``.age+1;`
`}`
`var` `cala=``new` `Man();`
`console.log(cala())``//23`

3、箭頭函式中沒有arguments(我們可以用rest引數替代),也沒有原型,也不能使用new 關鍵字,例如:

`//沒有arguments`
`var` `foo=(a,b)=>{``return` `arguments[0]*arguments[1]}`
`console.log(foo(3,5))`
`//arguments is not defined`
`//沒有原型`
`var` `Obj = () => {};`
`console.log(Obj.prototype);`
`// undefined`
`//不能使用new 關鍵字`
`var` `Obj = () => {``"hello world"``};`
`var` `o =` `new` `Obj();`
`// TypeError: Obj is not a constructor`

4、箭頭函式給陣列排序

`const arr = [10, 50, 30, 40, 20]`
`const s = arr.sort((a, b) => a - b)`
`console.log(s)` `// [10,20,30,40,50]`

尾呼叫優化

尾呼叫是指在函式return的時候呼叫一個新的函式,由於尾呼叫的實現需要儲存到記憶體中,在一個迴圈體中,如果存在函式的尾呼叫,你的記憶體可能爆滿或溢位。

ES6中,引擎會幫你做好尾呼叫的優化工作,你不需要自己優化,但需要滿足下面3個要求:

1、函式不是閉包
2、尾呼叫是函式最後一條語句
3、尾呼叫結果作為函式返回

尾呼叫實際用途——遞迴函式優化
在ES5時代,我們不推薦使用遞迴,因為遞迴會影響效能。
但是有了尾呼叫優化之後,遞迴函式的效能有了提升。

`//新型尾優化寫法`
`"use strict"``;`
`function` `a(n, p = 1) {`
`if``(n <= 1) {`
`return` `1 * p`
`}`
`let s = n * p`
`return` `a(n - 1, s)`
`}`
`//求 1 x 2 x 3的階乘`
`let sum = a(3)`
`console.log(sum)` `// 6`

五、ES6物件新增方法

Object.assign()

Object.assign()方法用於將所有可列舉屬性的值從一個或多個源物件複製到目標物件。它將返回目標物件。
Object.assign 方法只會拷貝源物件自身的並且可列舉的屬性到目標物件。該方法使用源物件的[[Get]]和目標物件的[[Set]],所以它會呼叫相關 getter 和 setter。因此,它分配屬性,而不僅僅是複製或定義新的屬性。如果合併源包含getter,這可能使其不適合將新屬性合併到原型中。為了將屬性定義(包括其可列舉性)複製到原型,應使用Object.getOwnPropertyDescriptor()和Object.defineProperty() 。
String型別和 Symbol 型別的屬性都會被拷貝。

合併物件

`var` `o1 = { a: 1 };`
`var` `o2 = { b: 2 };`
`var` `o3 = { c: 3 };`
`var` `obj = Object.assign(o1, o2, o3);`
`console.log(obj);` `// { a: 1, b: 2, c: 3 }`
`console.log(o1);` `// { a: 1, b: 2, c: 3 }, 注意目標物件自身也會改變。`

合併具有相同屬性的物件

`var` `o1 = { a: 1, b: 1, c: 1 };`
`var` `o2 = { b: 2, c: 2 };`
`var` `o3 = { c: 3 };`
`var` `obj = Object.assign({}, o1, o2, o3);`
`console.log(obj);` `// { a: 1, b: 2, c: 3 }`
歡迎加入全棧開發交流划水交流圈:582735936
面向划水1-3年前端人員
幫助突破划水瓶頸,提升思維能力

六、Map和Set

Map和Set都叫做集合,但是他們也有所不同。Set常被用來檢查物件中是否存在某個鍵名,Map集合常被用來獲取已存的資訊。
Set是有序列表,含有相互獨立的非重複值。
Array和Set對比
都是一個儲存多值的容器,兩者可以互相轉換,但是在使用場景上有區別。如下:
Array的indexOf方法比Set的has方法效率低下
Set不含有重複值(可以利用這個特性實現對一個數組的去重)
Set通過delete方法刪除某個值,而Array只能通過splice。兩者的使用方便程度前者更優
Array的很多新方法map、filter、some、every等是Set沒有的(但是通過兩者可以互相轉換來使用)
Object和Map對比
Object是字串-值,Map是值-值
Object鍵為string型別,Map的鍵是任意型別
手動計算Object尺寸,Map.size可以獲取尺寸

Map的排序是插入順序
Object有原型,所以對映中有一些預設的鍵。可以理解為Map=Object.create(null)

Set操作集合

`let set =` `new` `Set()`
`// Set轉化為陣列`
`let arr = Array.from(set)`
`let arr = [...set]`
`// 例項屬性(繼承自Set)`
`set.constructor === Set`
`set.size`
`// 操作方法`
`set.add(1)` `// 新增一個值`
`set.``delete``(1)` `//刪除一個值`
`set.has(1)` `//判斷是否有這個值(Array中的indexOf)`
`set.clear()` `//清除所有值`
`// 獲取用於遍歷的成員方法(Set的遍歷順序就是插入順序)`
`set.keys()` `// 返回鍵名的遍歷器`
`set.values()` `// 返回鍵值得遍歷器`
`set.entries()` `// 返回鍵值對的遍歷器`
`set.forEach()` `// 迴圈遍歷每個值(和Array的方法一致)`
`for` `(let key of set.keys()){}`
`for` `(let val of set.values()){}`
`for` `(let entry of set.entries()){}`
`// 使用陣列方法來處理set值`
`set =` `new` `Set(arr)`
`set =` `new` `Set([...set].map((x) => x = x * 2))`
`set =` `new` `Set([...set].filter((x) => x > 2))`

Map的方法集合

`let map =` `new` `Map()`
`// 例項屬性(繼承自Map)`
`map.constructor === Map`
`map.size`
`// 操作方法`
`map.set(1,2)`
`map.get(1)`
`map.``delete``(1)`
`map.has(1)`
`map.clear()`
`// 遍歷方法`
`map.keys()`
`map.values()`
`map.entries()`
`map.forEach()`
`// Map和陣列的轉換`
`map =` `new` `Map([[``'key'``,``'val'``],[2,1]])` `// 要求雙成員陣列`
`let arr = [...map]`
`// 值得注意的是Map的鍵是跟記憶體繫結的`
`map.set([1],` `'s'``)`
`map.get([1])`
`let arr = [1]`
`let arr1 = [1]`
`map.set(arr,` `'s'``)`
`map.get(arr)`
`map.set(arr1,` `'s'``)`
`map.get(arr1)`

七、迭代器(Iterator)

1、entries() 返回迭代器:返回鍵值對

`//陣列`
`const arr = [``'a'``,` `'b'``,` `'c'``];`
`for``(let v of arr.entries()) {`
`console.log(v)`
`}`
`// [0, 'a'] [1, 'b'] [2, 'c']`
`//Set`
`const arr =` `new` `Set([``'a'``,` `'b'``,` `'c'``]);`
`for``(let v of arr.entries()) {`
`console.log(v)`
`}`
`// ['a', 'a'] ['b', 'b'] ['c', 'c']`
`//Map`
`const arr =` `new` `Map();`
`arr.set(``'a'``,` `'a'``);`
`arr.set(``'b'``,` `'b'``);`
`for``(let v of arr.entries()) {`
`console.log(v)`
`}`
`// ['a', 'a'] ['b', 'b']`

2、values() 返回迭代器:返回鍵值對的value

`//陣列`
`const arr = [``'a'``,` `'b'``,` `'c'``];`
`for``(let v of arr.values()) {`
`console.log(v)`
`}`
`//'a' 'b' 'c'`
`//Set`
`const arr =` `new` `Set([``'a'``,` `'b'``,` `'c'``]);`
`for``(let v of arr.values()) {`
`console.log(v)`
`}`
`// 'a' 'b' 'c'`
`//Map`
`const arr =` `new` `Map();`
`arr.set(``'a'``,` `'a'``);`
`arr.set(``'b'``,` `'b'``);`
`for``(let v of arr.values()) {`
`console.log(v)`
`}`
`// 'a' 'b'`

3、keys() 返回迭代器:返回鍵值對的key

`//陣列`
`const arr = [``'a'``,` `'b'``,` `'c'``];`
`for``(let v of arr.keys()) {`
`console.log(v)`
`}`
`// 0 1 2`
`//Set`
`const arr =` `new` `Set([``'a'``,` `'b'``,` `'c'``]);`
`for``(let v of arr.keys()) {`
`console.log(v)`
`}`
`// 'a' 'b' 'c'`
`//Map`
`const arr =` `new` `Map();`
`arr.set(``'a'``,` `'a'``);`
`arr.set(``'b'``,` `'b'``);`
`for``(let v of arr.keys()) {`
`console.log(v)`
`}`
`// 'a' 'b'`

雖然上面列舉了3種內建的迭代器方法,但是不同集合的型別還有自己預設的迭代器,在for of中,陣列和Set的預設迭代器是values(),Map的預設迭代器是entries()。

for of迴圈解構

物件本身不支援迭代,但是我們可以自己新增一個生成器,返回一個key,value的迭代器,然後使用for of迴圈解構key和value。

`const obj = {`
`a: 1,`
`b: 2,`
`*[Symbol.iterator]() {`
`for``(let i` `in` `obj) {`
`yield [i, obj[i]]`
`}`
`}`
`}`
`for``(let [key, value] of obj) {`
`console.log(key, value)`
`}`
`// 'a' 1, 'b' 2`

字串迭代器

`const str =` `'abc'``;`
`for``(let v of str) {`
`console.log(v)`
`}`
`// 'a' 'b' 'c'`

ES6給陣列添加了幾個新方法:find()、findIndex()、fill()、copyWithin()

1、find():傳入一個回撥函式,找到陣列中符合當前搜尋規則的第一個元素,返回它,並且終止搜尋。

`const arr = [1,` `"2"``, 3, 3,` `"2"``]`
`console.log(arr.find(n =>` `typeof` `n ===` `"number"``))` `// 1`

2、findIndex():傳入一個回撥函式,找到陣列中符合當前搜尋規則的第一個元素,返回它的下標,終止搜尋。

`const arr = [1,` `"2"``, 3, 3,` `"2"``]`
`console.log(arr.findIndex(n =>` `typeof` `n ===` `"number"``))` `// 0`

3、fill():用新元素替換掉陣列內的元素,可以指定替換下標範圍。

`arr.fill(value, start, end)`

4、copyWithin():選擇陣列的某個下標,從該位置開始複製陣列元素,預設從0開始複製。也可以指定要複製的元素範圍。

`arr.copyWithin(target, start, end)`
`const arr = [1, 2, 3, 4, 5]`
`console.log(arr.copyWithin(3))` `// [1,2,3,1,2] 從下標為3的元素開始,複製陣列,所以4, 5被替換成1, 2`
`const arr1 = [1, 2, 3, 4, 5]`
`console.log(arr1.copyWithin(3, 1))` `// [1,2,3,2,3] 從下標為3的元素開始,複製陣列,指定複製的第一個元素下標為
1,所以4, 5被替換成2, 3`
`const arr2 = [1, 2, 3, 4, 5]`
`console.log(arr2.copyWithin(3, 1, 2))` `// [1,2,3,2,5] 從下標為3的元素開始,複製陣列,指定複製的第一個元素下標為1,結束位置為2,所以4被替換成2`

ES6中類class、Promise與非同步程式設計、代理(Proxy)和反射(Reflection)API,這幾塊內容比較複雜,以後有機會再詳細寫。