ES6系列目錄
1 let 和 const命令
2 變量的解構賦值
3 字符串的拓展
4 正則的拓展
5 數值的拓展
6 函數的拓展
7 數組的拓展
8 對象的拓展
9 Symbol
10 Set和Map數據結構
11 Proxy
12 Promise對象
13 Iterator和 for...of循環
14 Generator函數和應用
15 Class語法和繼承
16 Module語法和加載實現
所有整理的文章都收錄到我《Cute-JavaScript》系列文章中,訪問地址:http://js.pingan8787.com
1 let 和 const命令
在ES6中,我們通常實用 let
表示變量, const
表示常量,並且 let
和 const
都是塊級作用域,且在當前作用域有效不能重複聲明。
1.1 let 命令
let
命令的用法和 var
相似,但是 let
只在所在代碼塊內有效。基礎用法:
{
let a = 1;
let b = 2;
}
並且 let
有以下特點:
不存在變量提升:
var
聲明一個變量一個函數,都會伴隨着變量提升的問題,導致實際開發過程經常出現一些邏輯上的疑惑,按照一般思維習慣,變量都是需要先聲明後使用。
// var
console.log(v1); // undefined
var v1 = 2;
// 由於變量提升 代碼實際如下
var v1;
console.log(v1)
v1 = 2;
// let
console.log(v2); // ReferenceError
let v2 = 2;
不允許重複聲明:
let
和const
在相同作用域下,都不能重複聲明同一變量,並且不能在函數內重新聲明參數。
// 1. 不能重複聲明同一變量
// 報錯
function f1 (){
let a = 1;
var a = 2;
}
// 報錯
function f2 (){
let a = 1;
let a = 2;
}
// 2. 不能在函數內重新聲明參數
// 報錯
function f3 (a1){
let a1;
}
// 不報錯
function f4 (a2){
{
let a2
}
}
1.2 const 命令
const
聲明一個只讀的常量。基礎用法:
const PI = 3.1415926;
console.log(PI); // 3.1415926
注意點:
const
聲明後,無法修改值;
const PI = 3.1415926;
PI = 3;
// TypeError: Assignment to constant variable.
const
聲明時,必須賦值;
const a ;
// SyntaxError: Missing initializer in const declaration.
const
聲明的常量,let
不能重複聲明;
const PI = 3.1415926;
let PI = 0;
// Uncaught SyntaxError: Identifier 'PI' has already been declared
2 變量的解構賦值
解構賦值概念:在ES6中,直接從數組和對象中取值,按照對應位置,賦值給變量的操作。
2.1 數組
基礎用法:
// ES6 之前
let a = 1;
let b = 2;
// ES6 之後
let [a, b] = [1, 2];
本質上,只要等號兩邊模式一致,左邊變量即可獲取右邊對應位置的值,更多用法:
let [a, [[b], c]] = [1, [[2], 3]];
console.log(a, b, c); // 1, 2, 3
let [ , , c] = [1, 2, 3];
console.log(c); // 3
let [a, , c] = [1, 2, 3];
console.log(a,c); // 1, 3
let [a, ...b] = [1, 2, 3];
console.log(a,b); // 1, [2,3]
let [a, b, ..c.] = [1];
console.log(a, b, c); // 1, undefined, []
注意點:
如果解構不成功,變量的值就等於
undefined
。
let [a] = []; // a => undefined
let [a, b] = [1]; // a => 1 , b => undefined
當左邊模式多於右邊,也可以解構成功。
let [a, b] = [1, 2, 3];
console.log(a, b); // 1, 2
兩邊模式不同,報錯。
let [a] = 1;
let [a] = false;
let [a] = NaN;
let [a] = undefined;
let [a] = null;
let [a] = {};
指定解構的默認值:基礎用法:
let [a = 1] = []; // a => 1
let [a, b = 2] = [a]; // a => 1 , b => 2
特殊情況:
let [a = 1] = [undefined]; // a => 1
let [a = 1] = [null]; // a => null
右邊模式對應的值,必須嚴格等於 undefined
,默認值才能生效,而 null
不嚴格等於 undefined
。
2.2 對象的解構賦值
與數組解構不同的是,對象解構不需要嚴格按照順序取值,而只要按照變量名去取對應屬性名的值,若取不到對應屬性名的值,則爲 undefined
。
基礎用法:
let {a, b} = {a:1, b:2}; // a => 1 , b => 2
let {a, b} = {a:2, b:1}; // a => 2 , b => 1
let {a} = {a:3, b:2, c:1};// a => 3
let {a} = {b:2, c:1}; // a => undefined
注意點:
若變量名和屬性名不一致,則需要修改名稱。
let {a:b} = {a:1, c:2};
// error: a is not defined
// b => 1
對象的解構賦值的內部機制,是先找到同名屬性,然後再賦給對應的變量。真正被賦值的是後者,而不是前者。a
是匹配的模式, b
纔是變量。真正被賦值的是變量 b
,而不是模式 a
。
對象解構也支持嵌套解構。
let obj = {
a:[ 1, { b: 2}]
};
let {a, a: [c, {b}]} = obj;
// a=>[1, {b: 2}], b => 2, c => 1
指定解構的默認值:
let {a=1} = {}; // a => 1
let {a, b=1} = {a:2}; // a => 2, b => 1
let {a:b=3} = {}; // b => 3
let {a:b=3} = {a:4}; // b = >4
// a是模式,b是變量 牢記
let {a=1} = {a:undefined}; // a => 1
let {a=1} = {a:null}; // a => null
// 因爲null與undefined不嚴格相等,所以賦值有效
// 導致默認值1不會生效。
2.3 字符串的解構賦值
字符串的解構賦值中,字符串被轉換成了一個類似數組的對象。基礎用法:
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
let {length:len} = 'hello';// len => 5
2.4 數值和布爾值的解構賦值
解構賦值的規則是,只要等號右邊的值不是對象或數組,就先將其轉爲對象。由於 undefined
和 null
無法轉爲對象,所以對它們進行解構賦值,都會報錯。
// 數值和布爾值的包裝對象都有toString屬性
let {toString: s} = 123;
s === Number.prototype.toString // true
let {toString: s} = true;
s === Boolean.prototype.toString // true
let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError
2.5 函數參數的解構賦值
基礎用法:
function fun ([a, b]){
return a + b;
}
fun ([1, 2]); // 3
指定默認值的解構:
function fun ({a=0, b=0} = {}){
return [a, b];
}
fun ({a:1, b:2}); // [1, 2]
fun ({a:1}); // [1, 0]
fun ({}); // [0, 0]
fun (); // [0, 0]
function fun ({a, b} = {a:0, b:0}){
return [a, b];
}
fun ({a:1, b:2}); // [1, 2]
fun ({a:1}); // [1, undefined]
fun ({}); // [undefined, undefined]
fun (); // [0, 0]
2.6 應用
交換變量的值:
let a = 1,b = 2;
[a, b] = [b, a]; // a =>2 , b => 1
函數返回多個值:
// 返回一個數組
function f (){
return [1, 2, 3];
}
let [a, b, c] = f(); // a=>1, b=>2, c=>3
// 返回一個對象
function f (){
return {a:1, b:2};
}
let {a, b} = f(); // a=>1, b=>2
快速對應參數: 快速的將一組參數與變量名對應。
function f([a, b, c]) {...}
f([1, 2, 3]);
function f({a, b, c}) {...}
f({b:2, c:3, a:1});
提取JSON數據:
let json = {
name : 'leo',
age: 18
}
let {name, age} = json;
console.log(name,age); // leo, 18
遍歷Map結構:
const m = new Map();
m.set('a', 1);
m.set('b', 2);
for (let [k, v] of m){
console.log(k + ' : ' + v);
}
// 獲取鍵名
for (let [k] of m){...}
// 獲取鍵值
for (let [,k] of m){...}
輸入模塊的指定方法: 用於按需加載模塊中需要用到的方法。
const {log, sin, cos} = require('math');
3 字符串的拓展
3.1 includes(),startsWith(),endsWith()
在我們判斷字符串是否包含另一個字符串時,ES6之前,我們只有 typeof
方法,ES6之後我們又多了三種方法:
includes():返回布爾值,表示是否找到參數字符串。
startsWith():返回布爾值,表示參數字符串是否在原字符串的頭部。
endsWith():返回布爾值,表示參數字符串是否在原字符串的尾部。
let a = 'hello leo';
a.startsWith('leo'); // false
a.endsWith('o'); // true
a.includes('lo'); // true
並且這三個方法都支持第二個參數,表示起始搜索的位置。
let a = 'hello leo';
a.startsWith('leo',1); // false
a.endsWith('o',5); // true
a.includes('lo',6); // false
endsWith
是針對前 n
個字符,而其他兩個是針對從第 n
個位置直到結束。
3.2 repeat()
repeat
方法返回一個新字符串,表示將原字符串重複 n
次。基礎用法:
'ab'.repeat(3); // 'ababab'
'ab'.repeat(0); // ''
特殊用法:
參數爲
小數
,則取整
'ab'.repeat(2.3); // 'abab'
參數爲
負數
或Infinity
,則報錯
'ab'.repeat(-1); // RangeError
'ab'.repeat(Infinity); // RangeError
參數爲
0到-1的小數
或NaN
,則取0
'ab'.repeat(-0.5); // ''
'ab'.repeat(NaN); // ''
參數爲
字符串
,則轉成數字
'ab'.repeat('ab'); // ''
'ab'.repeat('3'); // 'ababab'
3.3 padStart(),padEnd()
用於將字符串頭部或尾部補全長度, padStart()
爲頭部補全, padEnd()
爲尾部補全。2個參數,第一個指定字符串最小長度,第二個用於補全的字符串。基礎用法 :
'x'.padStart(5, 'ab'); // 'ababx'
'x'.padEnd(5, 'ab'); // 'xabab'
特殊用法:
原字符串長度,大於或等於指定最小長度,則返回原字符串。
'xyzabc'.padStart(5, 'ab'); // 'xyzabc'
用來補全的字符串長度和原字符串長度之和,超過指定最小長度,則截去超出部分的補全字符串。
'ab'.padStart(5,'012345'); // "012ab"
省略第二個參數,則用
空格
補全。
'x'.padStart(4); // ' x'
'x'.padEnd(4); // 'x '
3.4 模版字符串
用於拼接字符串,ES6之前:
let a = 'abc' +
'def' +
'ghi';
ES6之後:
let a = `
abc
def
ghi
`
拼接變量:
在反引號(`)中使用 ${}
包裹變量或方法。
// ES6之前
let a = 'abc' + v1 + 'def';
// ES6之後
let a = `abc${v1}def`
4 正則的拓展
4.1 介紹
在ES5中有兩種情況。
參數是字符串,則第二個參數爲正則表達式的修飾符。
let a = new RegExp('abc', 'i');
// 等價於
let a = /abx/i;
參數是正則表達式,返回一個原表達式的拷貝,且不能有第二個參數,否則報錯。
let a = new RegExp(/abc/i);
//等價於
let a = /abx/i;
let a = new RegExp(/abc/, 'i');
// Uncaught TypeError
ES6中使用:
new RegExp(/abc/ig, 'i');
4.2 字符串的正則方法
常用的四種方法:match()
、 replace()
、 search()
和 split()
。
4.3 u修飾符
添加 u
修飾符,是爲了處理大於 uFFFF
的Unicode字符,即正確處理四個字節的UTF-16編碼。
/^\uD83D/u.test('\uD83D\uDC2A'); // false
/^\uD83D/.test('\uD83D\uDC2A'); // true
由於ES5之前不支持四個字節UTF-16編碼,會識別爲兩個字符,導致第二行輸出 true
,加入 u
修飾符後ES6就會識別爲一個字符,所以輸出 false
。
注意:u
修飾符後,會改變下面正則表達式的行爲:
(1)點字符 點字符(
.
)在正則中表示除了換行符以外的任意單個字符。對於碼點大於0xFFFF
的Unicode字符,點字符不能識別,必須加上u
修飾符。
var a = "?";
/^.$/.test(a); // false
/^.$/u.test(a); // true
(2)Unicode字符表示法 使用ES6新增的大括號表示Unicode字符時,必須在表達式添加
u
修飾符,才能識別大括號。
/\u{61}/.test('a'); // false
/\u{61}/u.test('a'); // true
/\u{20BB7}/u.test('?'); // true
(3)量詞 使用
u
修飾符後,所有量詞都會正確識別碼點大於0xFFFF
的 Unicode 字符。
/a{2}/.test('aa'); // true
/a{2}/u.test('aa'); // true
/?{2}/.test('??'); // false
/?{2}/u.test('??'); // true
(4)i修飾符 不加
u
修飾符,就無法識別非規範的K
字符。
/[a-z]/i.test('\u212A') // false
/[a-z]/iu.test('\u212A') // true
檢查是否設置 u
修飾符:使用 unicode
屬性。
const a = /hello/;
const b = /hello/u;
a.unicode // false
b.unicode // true
4.4 y修飾符
y
修飾符與 g
修飾符類似,也是全局匹配,後一次匹配都是從上一次匹配成功的下一個位置開始。區別在於, g
修飾符只要剩餘位置中存在匹配即可,而 y
修飾符是必須從剩餘第一個開始。
var s = 'aaa_aa_a';
var r1 = /a+/g;
var r2 = /a+/y;
r1.exec(s) // ["aaa"]
r2.exec(s) // ["aaa"]
r1.exec(s) // ["aa"] 剩餘 '_aa_a'
r2.exec(s) // null
lastIndex
屬性:
指定匹配的開始位置:
const a = /a/y;
a.lastIndex = 2; // 從2號位置開始匹配
a.exec('wahaha'); // null
a.lastIndex = 3; // 從3號位置開始匹配
let c = a.exec('wahaha');
c.index; // 3
a.lastIndex; // 4
返回多個匹配:y
修飾符對 match
方法只能返回第一個匹配,與 g
修飾符搭配能返回所有匹配。
'a1a2a3'.match(/a\d/y); // ["a1"]
'a1a2a3'.match(/a\d/gy); // ["a1", "a2", "a3"]
檢查是否使用 y
修飾符:sticky
屬性檢查。
const a = /hello\d/y;
a.sticky; // true
4.5 flags屬性
flags
屬性返回所有正則表達式的修飾符。
/abc/ig.flags; // 'gi'