ECMAScript 6 新特性(二)
ECMAScript 6 新特性(一)
ECMAScript 6 新特性(二)(本文)
ECMAScript 7~10 新特性
1. 生成器
生成器函數是 ES6 提供的一種解決異步編程方案,一種特殊的函數,語法行為與傳統函數完全不同。
function* gen() {yield 1; // yield 關鍵字用來暫停函數的執行yield 2;yield 3;
}let iterator = gen();
// 調用
// console.log(iterator); // 不能調用
// iterator.next(); // 正常調用// for (let v of gen()) {
// console.log(v); // 每次返回一個 yield 的值
// }
// 等同于
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
代碼說明:
*
的位置沒有限制;- 生成器函數返回的結果是迭代器對象,調用迭代器對象的 next 方法可以得到 yield 語句后的值;
- yield 相當于函數的暫停標記,也可以認為是函數的分隔符,每調用一次 next 方法,執行一段代碼;
- next 方法可以傳遞實參,作為 yield 語句的返回值 。
1.1 生成器函數參數
function* gen(arg) {console.log(arg);let one = yield 1;console.log(one);let two = yield 2;console.log(two);let three = yield 3;console.log(three);
}let iterator = gen("aaaaa");
console.log(iterator.next());
// next 方法可以傳入實參,傳入參數會被賦值給 yield 表達式的值
console.log(iterator.next("BBB"));
console.log(iterator.next("CCC"));
console.log(iterator.next('DDD'));
1.2 實例
-
1s后控制臺輸出111,2s后輸出222,3s后輸出333
傳統實現
setTimeout(() => {console.log("111");setTimeout(() => {console.log("222");setTimeout(() => {console.log("333");}, 3000);}, 2000); }, 1000);
代碼較為復雜,不易擴展,使用生成器函數實現
function one() {setTimeout(() => {console.log("111");iterator.next();}, 1000); } function two() {setTimeout(() => {console.log("222");iterator.next();}, 2000); } function three() {setTimeout(() => {console.log("333");iterator.next();}, 3000); }function* gen() {yield one();yield two();yield three(); }// 調用生成器函數 let iterator = gen(); iterator.next();
-
模擬獲取用戶數據、訂單數據、商品數據
function getUsers() {setTimeout(() => {let data = "用戶數據";// 調用 next 方法,并將數據傳入iterator.next(data);}, 1000); } function getOrders() {setTimeout(() => {let data = "訂單數據";iterator.next(data);}, 1000); } function getGoods() {setTimeout(() => {let data = "商品數據";iterator.next(data);}, 1000); }function* genData() {let users = yield getUsers();console.log(users);let orders = yield getOrders();console.log(orders);let goods = yield getGoods();console.log(goods); }// 調用生成器函數 let iterator = genData(); iterator.next();
2. Promise
Promise 是 ES6 引入的異步編程的新解決方案。語法上 Promise 是一個構造函數,用來封裝異步操作并可以獲取其成功或失敗的結果。
- Promise 構造函數:
Promise (excutor) {}
; Promise.prototype.then
方法;Promise.prototype.catch
方法。
2.1 then 方法
實例化 Promise 時,使用回調函數作為參數,回調函數通常有兩個參數:
-
resolve 參數
當執行到
resolve( ... )
時,會調用 then 方法中的第一個參數(回調); -
reject 參數
當執行到
reject( ... )
時,會調用 then 方法中的第二個參數(回調);
then 方法中通常有兩個回調函數作為參數,第一個回調在成功時(resolve
)調用,第二個回調在出錯時(reject
)調用,第二個參數可以省略。
基本使用
// 實例化 Promise 對象
const p = new Promise((resolve, reject) => {setTimeout(() => {let data = "Hello, world!";// resolve(data); // 調用 then 方法中第一個回調reject(new Error("出錯了")); // 調用 then 方法中第二個回調}, 1000);
});// 調用 Promise 對象的 then 方法
p.then((value) => {console.log(value);}, // 成功回調(reason) => {console.error(reason);} // 失敗回調
);
下面列舉幾個使用 Promise 進行封裝的案例:
2.1.1 讀取文件
// 引入模塊
const fs = require("fs");// 使用Promise 封裝
const p = new Promise((resolve, reject) => {fs.readFile("./resources/為學.md", (err, data) => {// 如果失敗,則拋出錯誤if (err) reject(err);// 否則,打印文件內容resolve(data);});
});p.then((value) => {console.log(value.toString());},(reason) => {console.log("出錯了:" + reason);}
);
2.1.2 發送 AJAX 請求
// 封裝 Promise 對象
const p = new Promise((resolve, reject) => {// 1. 創建對象const xhr = new XMLHttpRequest();// 2. 初始化xhr.open("GET", "https://dog.ceo/api/breeds/image/random");// 3. 發送xhr.send();// 4. 綁定事件,處理響應結果xhr.onreadystatechange = function () {if (xhr.readyState === 4) {if (xhr.status >= 200 && xhr.status < 300) {resolve(xhr.responseText);} else {reject(xhr.statusText);}}};
});// 指定回調
p.then((data) => {console.log(data);},(error) => {console.log(error);}
);
2.1.3 Promise.prototype.then
// 創建 Promise 對象
const p = new Promise((resolve, reject) => {resolve("用戶數據");// reject("出錯了");
});const result = p.then((data) => {console.log(data);},(error) => {console.warn(error);}
);console.log(result);
2.1.4 說明
調用 then 方法的返回結果是 Promise 對象,對象狀態由回調函數的執行結果決定:
-
返回結果是非 Promise 類型的屬性
返回狀態 resolved(成功),返回值為對象成功的值
const result = p.then((data) => {console.log(data);return 123;},(error) => {console.warn(error);} );console.log(result); // 返回值為 123
如果未使用 return 進行返回,則返回值為 undefined。
-
返回 Promise 對象
返回值和返回狀態均由返回的 promise 對象的返回值和狀態決定
const result = p.then((data) => {console.log(data);return new Promise((resolve, reject) => {resolve("ok");// reject("出錯了");});},(error) => {console.warn(error);} ); console.log(result); // 返回狀態為 resolved,返回值為 ok // console.log(result); // 返回狀態為 rejected,返回值為 出錯了
-
拋出錯誤
返回狀態 rejected(失敗)
const result = p.then((data) => {console.log(data);// throw new Error("出錯了");throw "出錯了";},(error) => {console.warn(error);} );console.log(result); // 返回狀態為 rejected,返回值為 出錯了
由于 promise 可以返回 promise 對象,因此可以進行鏈式調用
// 鏈式調用
p.then((data) => {},(error) => {}
).then((data) => {},// 失敗回調可以省略
)...;
同時避免的回調地域的問題
2.2 catch 方法
指定 Promise 對象失敗的回調
const p = new Promise((resolve, reject) => {setTimeout(() => {reject("出錯了");}, 1000);
});p.catch((err) => {console.log(err); // 輸出 "出錯了"
});
3. Set
ES6 提供了新的數據結構 Set(集合)。它類似于數組,但成員的值都是唯一的,集合實現了 iterator 接口,所以可以使用擴展運算符和 for…of… 進 行遍歷,集合的屬性和方法:
- size 返回集合的元素個數
- add 增加一個新元素,返回當前集合
- delete 刪除元素,返回 boolean 值
- has 檢測集合中是否包含某個元素,返回 boolean 值
- clear 清空集合,返回 undefined
// 聲明一個 set
let s1 = new Set();
let s2 = new Set([1, 2, 3, 4, 5]);console.log(s1, typeof s1); // Set(0) {} object
console.log(s2, typeof s2);// 元素個數
console.log(s2.size); // 5
// 添加元素
s2.add(6);
console.log(s2); // Set(6) {1, 2, 3, 4, 5, 6}
// 刪除元素
s2.delete(1);
console.log(s2); // Set(5) {2, 3, 4, 5, 6}
// 判斷元素是否存在
console.log(s2.has(2)); // true
console.log(s2.has(7)); // false
// 清空集合
s2.clear();
console.log(s2); // Set(0) {}
4. Map
ES6 提供了 Map 數據結構。它類似于對象,也是鍵值對的集合。但是“鍵” 的范圍不限于字符串,各種類型的值(包括對象)都可以當作鍵。Map 也實現了 iterator 接口,所以可以使用擴展運算符和 for…of… 進行遍歷。Map 的屬 性和方法:
- size 返回 Map 的元素個數
- set 增加一個新元素,返回當前 Map
- get 返回鍵名對象的鍵值
- has 檢測 Map 中是否包含某個元素,返回 boolean 值
- clear 清空集合,返回 undefined
// 聲明 map
let m = new Map();// 向 map 中添加鍵值對
m.set("name", "張三");
m.set("change", () => {console.log("change");
});
m.set({ age: 25 }, ["李四"]);
// size
console.log(m.size);
// 刪除
m.delete("name");
// 獲取
console.log(m.get("change"));
// 清空
m.clear();console.log(m);
5. Class
ES6 提供了更接近傳統語言的寫法,引入了 Class(類)這個概念,作為對象的模板。通過 class 關鍵字,可以定義類。基本上,ES6 的 class 可以看作只是一個語法糖,它的絕大部分功能,ES5 都可以做到,新的 class 寫法只是讓對象原型的寫法更加清晰、更像面向對象編程的語法而已。
知識點:
-
class 聲明類
class 類名 {屬性 = 值;...方法() {...} }
-
constructor 定義構造函數初始化
class 類名 {// 構造方法,實例化時自動調用constructor(參數1, 參數2) {this.屬性1 = 參數1;this.屬性2 = 參數2;}... // 屬性、方法 }// 實例化對象 let 對象 = new 類名(參數1, 參數2);
-
extends 繼承父類
class 父類 {... }class 子類 extends 父類 {... }
-
super 調用父級構造方法
class 父類 {constructor(參數1, 參數2) {this.屬性1 = 參數1;this.屬性2 = 參數2;} }class 子類 extends 父類 {constructor(參數1, 參數2, 參數3, 參數4) {super(參數1, 參數2); // 繼承父類的構造方法// 子類的屬性this.屬性1 = 參數3;} }
-
static 定義靜態方法和屬性
class 類名 {static 屬性 = 值;static 方法() {...} }
靜態屬性和方法不能被讀取和繼承。
-
子類重寫父類方法
子類中可以聲明一個跟父類同名的方法
class 父類 {方法() {...} }class 子類 extends 父類 {// 重寫父類中的同名方法方法() {...} }
5.1 get 和 set
class Phone {get price() {console.log("價格屬性被讀取");return 3999}set price(value) {console.log("價格屬性被修改");}
}let p = new Phone();console.log(p.price);
p.price = "free";
6. 數值擴展
Number.EPSILON
是 JavaScript 的最小精度,即 2.220446049250313e-16。
當兩個數的差值小于該值,就可以認為這兩個數相等,主要用于浮點數的計算。
console.log(0.1 + 0.2 === 0.3); // false,因為浮點數計算不精確
console.log(Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON); // true
// 這時就可以認為 0.1 + 0.2 等于 0.3
6.1 二進制、八進制、十六進制
進制 | 前綴 |
---|---|
二進制 | 0b |
八進制 | 0o |
十六進制 | 0x |
let a = 0b1010; // 10
let b = 0o777; // 511
let c = 0x1F; // 31console.log(a, b, c);
6.2 Number 方法
-
Number.isFinite()
檢查是否為有限數console.log(Number.isFinite(Infinity)); // false console.log(Number.isFinite(-Infinity)); // false console.log(Number.isFinite(NaN)); // false console.log(Number.isFinite(0)); // true
Infinity
:無窮大NaN
:非數值
-
Number.isNaN()
檢查是否為NaNconsole.log(Number.isNaN(NaN)); // true console.log(Number.isNaN(0)); // false
-
Number.parseInt()
和Number.parseFloat()
用于將字符串轉換為數字let d = "123"; let e = "123.456"; let f = "0b1010";console.log(Number.parseInt(d)); // 123 console.log(Number.parseFloat(e)); // 123.456 console.log(Number.parseInt(f)); // 0
這兩個方法都會忽略字符串開頭的空格,并且只解析到第一個非數字字符為止。
-
Number.isInteger()
檢查是否為整數console.log(Number.isInteger(123)); // true console.log(Number.isInteger(123.456)); // false
-
Math.trunc()
用于截斷小數部分,返回整數部分let i = 123.456; let j = -123.456;console.log(Math.trunc(i)); // 123 console.log(Math.trunc(j)); // -123
-
Math.sign()
用于判斷一個數的正負號數 返回值 正數 1 零 0 負數 -1 console.log(Math.sign(5)); // 1 console.log(Math.sign(-5)); // -1 console.log(Math.sign(0)); // 0
7. 對象方法擴展
-
Object.is()
判斷兩個值是否完全相等console.log(Object.is(120, 121)); // false console.log(Object.is(1.1 + 1.2, 2.3)); // true
作用類似于 == 或 ===,但區別在于對 NaN 的判斷
console.log(Object.is(NaN, NaN)); // true console.log(NaN === NaN); // false
-
Object.assign()
用于兩個對象的合并const config1 = {host: "localhost",port: 3306,pass: "root", }; const config2 = {host: "www.baidu.com",port: "8080", }; console.log(Object.assign(config1, config2));
合并兩個對象,相同屬性的值,后者覆蓋前者
-
Object.setPrototypeOf()
設置對象的原型對象Object.getPrototypeOf()
獲取對象的原型對象const school = {name: "學院", }; const cities = {xiaoqv: ["商丘", "開封", "洛陽"], }; Object.setPrototypeOf(school, cities); console.log(school); console.log(Object.getPrototypeOf(school));
8. 模塊化
模塊化是指將一個大的程序文件,拆分成許多小的文件,然后將小文件組合起來。
8.1 模塊化的好處
模塊化的優勢有以下幾點
- 防止命名沖突
- 代碼復用
- 高維護性
- 開發人員對文件的修改不會產生過多沖突
- 當某個功能需要修改、更新時,只需要對單個模塊修改即可
8.2 模塊化規范產品
ES6 之前的模塊化規范有:
規范 | 產品 |
---|---|
CommonJS | NodeJS、Browserify |
AMD | requireJS |
CMD | seaJS |
8.3 ES6 模塊化語法
模塊功能主要由兩個命令構成:export
和 import
。
export
命令用于規定模塊的對外接口(暴露模塊接口);import
命令用于輸入其他模塊提供的功能(導入其它模塊)。
暴露接口的幾種方式
-
默認暴露:
export default 接口;
每個模塊只能有一個默認暴露。
導入:
import 接口 from ...;
-
命名暴露:
export 屬性名/對象名/...;
命名導出允許你導出多個值,并且每個導出都有一個名稱。
導入:
import { 屬性名/對象名/... } from ...;
-
混合暴露:
export { 屬性名/對象名/..., 屬性名2/對象名2/... as default };
在一個模塊中同時使用默認暴露和命名暴露。
導入:
import 屬性名2/對象名2/..., { 屬性名/對象名/... } from ...;
-
重新暴露
可以從一個模塊中導出另一個模塊的導出內容。
模塊1:
export 屬性名/對象名/...;
模塊2:
export { 屬性名/對象名/... } from ...;
…
導入:
import { 屬性名/對象名/... } from ...;