国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

ES6基礎教程

這篇具有很好參考價值的文章主要介紹了ES6基礎教程。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

目錄

?文章來源地址http://www.zghlxwxcb.cn/news/detail-559305.html

1.簡介與環(huán)境搭建

1.1 簡介

1.2 環(huán)境搭建

1.2.1 瀏覽器支持情況

1.2.2 Node.js安裝配置

1.2.3 webpack

1.2.4 gulp

2.聲明與表達式

2.1 let與const

2.1.1 let

2.1.1.1 let與var聲明變量比較

?2.1.1.2 let關鍵代碼說明

2.1.2 const

2.2 解構(gòu)賦值

2.2.1 概述

2.2.2 Arrary(數(shù)組)

2.2.3 Object(對象)

2.3 Symbol

2.3.1 概述

2.3.2 基本用法

2.3.3 使用場景

2.3.3.1 作為屬性名使用

?2.3.3.2 注意事項

2.3.3.3 定義常量使用

2.3.4 Symbol.for()

2.3.5?Symbol.keyFor()

3.內(nèi)置對象

3.1?字符串

3.1.1 字符串擴展方法

3.1.1.1 子串的識別includes、?startsWith、?endsWith

3.1.1.2 字符串重復repeat

3.1.1.3 字符串補全padStart、padEnd

3.1.2 模板字符串

3.1.2.1 基本用法

3.1.2.2?標簽模板

3.2?數(shù)值

3.2.1 數(shù)值的表示

3.2.2 常量

3.2.3 方法

3.2.4 Math對象的擴展

3.2.4.1 普通計算

3.2.4.2 數(shù)字處理

3.2.4.3 判斷

3.2.4.4 對數(shù)方法

3.2.4.5 雙曲函數(shù)方法

3.2.4.6 指數(shù)運算符

3.3?對象

3.3.1 字面量

3.3.2 擴展運算符

3.3.3 新方法

3.3.3.1?Object.assign

3.3.3.2?Object.is

3.4?數(shù)組

3.4.1 數(shù)組創(chuàng)建

3.4.1.1?Array.of()

3.4.1.2?Array.from()

3.4.1.3 類組對象

3.4.1.4 轉(zhuǎn)換可迭代對象

3.4.2 擴展方法

3.4.2.1 查找

3.4.2.2 填充

3.4.2.3 遍歷

3.4.2.4 包含

3.4.2.5?嵌套數(shù)組轉(zhuǎn)一維數(shù)組

3.4.3 數(shù)組緩沖區(qū)

3.4.3.1 通過構(gòu)造函數(shù)創(chuàng)建

3.4.3.2通過視圖創(chuàng)建

3.4.4 定型數(shù)組

3.4.4.1?通過數(shù)組緩沖區(qū)生成

3.4.4.2?通過構(gòu)造函數(shù)生成?

?3.4.4.3?注意事項

3.4.5 擴展運算符

3.5?Map與Set

3.5.1 Map

3.5.1.1 Map與Object的區(qū)別

3.5.1.2 Map中的key

3.5.1.3 Map的迭代

3.5.1.4 Map對象的操作

3.5.2 Set

3.5.2.1 Set中的特殊值

3.5.2.2 類型轉(zhuǎn)換

3.5.2.3 作用

3.6?Reflect與Proxy

3.6.1概述

3.6.2 Proxy

3.6.2.1 基本用法

3.6.2.2 Proxy之handler詳解

3.6.3 Reflect

3.6.3.1 靜態(tài)方法

3.6.4 Proxy與Reflect組合使用

3.6.5 使用場景擴展

4.運算符與語句

4.1 函數(shù)

4.1.1 函數(shù)參數(shù)的擴展—默認參數(shù)

4.1.2?函數(shù)參數(shù)的擴展—不定參數(shù)

4.1.3 箭頭函數(shù)

4.1.3.1 基本用法

4.1.3.2 適合使用箭頭函數(shù)的場景

?4.1.3.3 不適合使用箭頭函數(shù)的場景

4.2 Class類

4.2.1 基礎用法—類的定義

4.2.2 基礎用法—類的主體

4.2.2.1 屬性(prototype)

4.2.2.3 方法

4.2.3 基礎用法—類的實例化

4.2.4?decorator函數(shù)

4.2.4.1?類修飾

4.2.4.2?方法修飾

4.2.5?封裝與繼承

4.2.5.1?getter / setter

4.2.5.2?extends

4.2.5.3 super

4.3 模塊

4.3.1 export與import

4.3.1.1 基本用法

4.3.1.2 as的用法

4.3.1.3 import命令的特點

4.3.1.4?export default 命令

4.3.2 復合使用

5.異步編程

5.1 Promise對象

5.1.1 Promise狀態(tài)

5.1.2 Then方法

?5.2 Generator函數(shù)

5.2.1 基本用法

5.2.1.1 函數(shù)組成

5.2.2.2 執(zhí)行機制

5.2.2.3?函數(shù)返回的遍歷器對象的方法

5.2.2 使用場景

5.3 async函數(shù)

5.3.1 async

5.3.2 await


?

1.簡介與環(huán)境搭建

1.1 簡介

ES6, 全稱 ECMAScript 6.0 ,是 JavaScript 的下一個版本標準,2015.06 發(fā)版。其目標為使JavaScript可以用來編寫復雜的大型應用程序,成為企業(yè)級開發(fā)語言。

ECMAScript和JavaScript的關系:前者是后者的規(guī)格;后者是前者的一種實現(xiàn)。

ECMAScript 背景

JavaScript 是大家所了解的語言名稱,但是這個語言名稱是商標( Oracle 公司注冊的商標)。因此,JavaScript 的正式名稱是 ECMAScript 。1996年11月,JavaScript 的創(chuàng)造者網(wǎng)景公司將 JS 提交給國際化標準組織 ECMA(European computer manufactures association,歐洲計算機制造聯(lián)合會),希望這種語言能夠成為國際標準,隨后 ECMA 發(fā)布了規(guī)定瀏覽器腳本語言的標準,即 ECMAScript。這也有利于這門語言的開放和中立。

1.2 環(huán)境搭建

1.2.1 瀏覽器支持情況

目前各大瀏覽器基本上都支持 ES6 的新特性,其中 Chrome 和 Firefox 瀏覽器對 ES6 新特性最友好,IE7~11 基本不支持 ES6。

以下是各大瀏覽器支持情況及開始時間:

Chrome 58

Edge 14

Firefox 54

Safari 10

Opera 55

2017 年 1 月

2016 年 8 月

2017 年 3 月

2016 年 7 月

2018 年 8 月

1.2.2 Node.js安裝配置

Node.js 是運行在服務端的 JavaScript,它對 ES6 的支持度更高。

關于Node.js的基礎教程參見Node.js 教程

Node.js安裝配置參見Node.js 安裝配置

1.2.3 webpack

webpack 是一個現(xiàn)代 JavaScript 應用程序的靜態(tài)模塊打包器 (module bundler) 。當 webpack 處理應用程序時,它會遞歸地構(gòu)建一個依賴關系圖 (dependency graph) ,其中包含應用程序需要的每個模塊,然后將所有這些模塊打包成一個或多個 bundle 。

webpack 主要有四個核心概念:

  • 入口 (entry):入口會指示 webpack 應該使用哪個模塊,來作為構(gòu)建其內(nèi)部依賴圖的開始。進入入口起點后,webpack 會找出有哪些模塊和庫是入口起點(直接和間接)依賴的。
  • 輸出 (output):output 屬性會告訴 webpack 在哪里輸出它創(chuàng)建的 bundles ,以及如何命名這些文件,默認值為 ./dist。
  • loader:loader 讓 webpack 可以去處理那些非 JavaScript 文件( webpack 自身只理解 JavaScript ),loader 可以將所有類型的文件轉(zhuǎn)換為 webpack 能夠有效處理的模塊。
  • 插件 (plugins):loader 被用于轉(zhuǎn)換某些類型的模塊,而插件則可以做更多的事情。包括打包優(yōu)化、壓縮、定義環(huán)境變量等等。插件的功能強大,是 webpack 擴展非常重要的利器,可以用來處理各種各樣的任務。

1.2.4 gulp

gulp 是一個基于流的自動化構(gòu)建工具,具有易于使用、構(gòu)建快速、插件高質(zhì)和易于學習的特點,常用于輕量級的工程中。

全局gulp安裝

$ npm install --global gulp

項目中引入安裝

$ npm install --save-dev gulp

?運行

$ gulp

2.聲明與表達式

2.1 let與const

2.1.1 let

let用來聲明變量,有以下幾個特點

  • 在代碼塊內(nèi)有效
  • 不能重復聲明
  • 不存在變量提升
  • 不影響作用域鏈

2.1.1.1 let與var聲明變量比較

特點 let var 備注
作用域 代碼塊內(nèi) 全局范圍內(nèi) for循環(huán)計數(shù)器很適合用let
重復聲明 不可以 可以 ?
變量提升 不存在 存在 ?

?2.1.1.2 let關鍵代碼說明

//代碼塊內(nèi)有效
{
  let a = 0;
  var b = 1;
}
console.log(a); // ReferenceError: a is not defined
console.log(b);  // 1

//不允許重復聲明
let a = 1;
let a = 2;
var b = 3;
var b = 4;
console.log(a); // Identifier 'a' has already been declared
console.log(b);  // 4

//不存在變量提升
console.log(a);  //ReferenceError: a is not defined
let a = "apple";
 
console.log(b);  //undefined
var b = "banana";

//暫時性死區(qū):只要作用城內(nèi)存在let const
//他們所聲明的變量或者常量就自動綁定這個區(qū)域,不再受到外部作用域的影響
let a = 2;
function func() {
 console.log(a); //Cannot access 'a' before initialization
 let a = 1;
}
func();

2.1.2 const

const關鍵字用來聲明常量,const聲明有以下特點:

  • 不能重復聲明
  • 聲明必須賦初始值,否則報錯
  • 聲明之后值不允許修改
  • 在代碼塊內(nèi)有效

2.2 解構(gòu)賦值

2.2.1 概述

解構(gòu)賦值是對賦值運算符的擴展。

他是一種針對數(shù)組或者對象進行模式匹配,然后對其中的變量進行賦值。

在代碼書寫上簡潔且易讀,語義更加清晰明了;也方便了復雜對象中數(shù)據(jù)字段獲取。

2.2.2 Arrary(數(shù)組)

//基本
let [a, b, c] = [1, 2, 3];// a = 1, b = 2,  c = 3

//可嵌套
let [a, [[b], c]] = [1, [[2], 3]];// a = 1, b = 2,  c = 3

//可忽略
let [a, , b] = [1, 2, 3];// a=1,b=3

//不完全解構(gòu)
let [a = 1, b] = []; // a = 1, b = undefined

//剩余運算符
let [a, ...b] = [1, 2, 3];//a = 1, b = [2, 3]

//字符串
let [a, b, c, d, e] = 'hello';//a='h', b='e', c='l', d='l',e='o'

//解構(gòu)默認值
let [a = 2] = [undefined]; // a = 2

//當解構(gòu)模式有匹配結(jié)果,且匹配結(jié)果是 undefined 時,會觸發(fā)默認值作為返回結(jié)果。
let [a = 3, b = a] = [];     // a = 3, b = 3
let [a = 3, b = a] = [1];    // a = 1, b = 1
let [a = 3, b = a] = [1, 2]; // a = 1, b = 2

2.2.3 Object(對象)

//基本
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };// foo = 'aaa', bar = 'bbb'
let { baz : foo } = { baz : 'ddd' };// foo = 'ddd'

//可嵌套
let obj = {p: ['hello', {y: 'world'}] };
let {p: [x, { y }] } = obj;
// x = 'hello' ,y = 'world'

//可忽略
let obj = {p: ['hello', {y: 'world'}] };
let {p: [x, {  }] } = obj;// x = 'hello'

//不完全解構(gòu)
let obj = {p: [{y: 'world'}] };
let {p: [{ y }, x ] } = obj;
// x = undefined
// y = 'world'

//剩余運算符
let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40};
// a = 10
// b = 20
// rest = {c: 30, d: 40}

//解構(gòu)默認值
let {a = 10, b = 5} = {a: 3};
// a = 3; b = 5;
let {a: aa = 10, b: bb = 5} = {a: 3};
// aa = 3; bb = 5;

2.3 Symbol

2.3.1 概述

ES6 引入了一種新的原始數(shù)據(jù)類型 Symbol ,表示獨一無二的值,最大的用法是用來定義對象的唯一屬性名。

ES6 數(shù)據(jù)類型除了 Number 、 String 、 Boolean 、 Object、 null 和 undefined ,還新增了 Symbol 。

2.3.2 基本用法

Symbol 函數(shù)棧不能用 new 命令,因為 Symbol 是原始數(shù)據(jù)類型,不是對象??梢越邮芤粋€字符串作為參數(shù),為新創(chuàng)建的 Symbol 提供描述,用來顯示在控制臺或者作為字符串的時候使用,便于區(qū)分。

let sy = Symbol("KK");
console.log(sy);   // Symbol(KK)
typeof(sy);        // "symbol"
 
// 相同參數(shù) Symbol() 返回的值不相等
let sy1 = Symbol("kk"); 
sy === sy1;       // false

2.3.3 使用場景

2.3.3.1 作為屬性名使用

由于每一個 Symbol 的值都是不相等的,所以 Symbol 作為對象的屬性名,可以保證屬性不重名。

let sy = Symbol("key1");
 
// 寫法1
let syObject = {};
syObject[sy] = "kk";
console.log(syObject);    // {Symbol(key1): "kk"}
 
// 寫法2
let syObject = {
  [sy]: "kk"
};
console.log(syObject);    // {Symbol(key1): "kk"}
 
// 寫法3
let syObject = {};
Object.defineProperty(syObject, sy, {value: "kk"});
console.log(syObject);   // {Symbol(key1): "kk"}

Symbol 作為對象屬性名時不能用.運算符,要用方括號。因為.運算符后面是字符串,所以取到的是字符串 sy 屬性,而不是 Symbol 值 sy 屬性。

let syObject = {};
syObject[sy] = "kk";
 
syObject[sy];  // "kk"
syObject.sy;   // undefined

?2.3.3.2 注意事項

Symbol 值作為屬性名時,該屬性是公有屬性不是私有屬性,可以在類的外部訪問。但是不會出現(xiàn)在 for...in 、 for...of 的循環(huán)中,也不會被 Object.keys() 、 Object.getOwnPropertyNames() 返回。如果要讀取到一個對象的 Symbol 屬性,可以通過 Object.getOwnPropertySymbols() 和 Reflect.ownKeys() 取到。

let syObject = {};
syObject[sy] = "kk";
console.log(syObject);
 
for (let i in syObject) {
  console.log(i);
}    // 無輸出
 
Object.keys(syObject);                     // []
Object.getOwnPropertySymbols(syObject);    // [Symbol(key1)]
Reflect.ownKeys(syObject);                 // [Symbol(key1)]

2.3.3.3 定義常量使用

在 ES5 使用字符串表示常量,但是用字符串不能保證常量是獨特的,這樣會引起一些問題,但是使用 Symbol 定義常量,這樣就可以保證這一組常量的值都不相等。

const COLOR_RED = Symbol("red");
const COLOR_YELLOW = Symbol("yellow");
const COLOR_BLUE = Symbol("blue");
 
function ColorException(message) {
   this.message = message;
   this.name = "ColorException";
}
function getConstantName(color) {
    switch (color) {
        case COLOR_RED :
            return "COLOR_RED";
        case COLOR_YELLOW :
            return "COLOR_YELLOW ";
        case COLOR_BLUE:
            return "COLOR_BLUE";
        default:
            throw new ColorException("Can't find this color");
    }
}
 
try {
   
   var color = "green"; // green 引發(fā)異常
   var colorName = getConstantName(color);
} catch (e) {
   var colorName = "unknown";
   console.log(e.message, e.name); // 傳遞異常對象到錯誤處理
}

Symbol 的值是唯一的,所以不會出現(xiàn)相同值得常量,即可以保證 switch 按照代碼預想的方式執(zhí)行。?

2.3.4 Symbol.for()

Symbol.for() 類似單例模式,首先會在全局搜索被登記的 Symbol 中是否有該字符串參數(shù)作為名稱的 Symbol 值,如果有即返回該 Symbol 值,若沒有則新建并返回一個以該字符串參數(shù)為名稱的 Symbol 值,并登記在全局環(huán)境中供搜索。

let yellow = Symbol("Yellow");
let yellow1 = Symbol.for("Yellow");
yellow === yellow1;      // false
 
let yellow2 = Symbol.for("Yellow");
yellow1 === yellow2;     // true

2.3.5?Symbol.keyFor()

Symbol.keyFor() 返回一個已登記的 Symbol 類型值的 key ,用來檢測該字符串參數(shù)作為名稱的 Symbol 值是否已被登記。

let yellow1 = Symbol.for("Yellow");
Symbol.keyFor(yellow1);    // "Yellow"

3.內(nèi)置對象

3.1?字符串

3.1.1 字符串擴展方法

3.1.1.1 子串的識別includes、?startsWith、?endsWith

ES6 之前判斷字符串是否包含子串,用 indexOf 方法,ES6 新增了子串的識別方法

  • includes():返回布爾值,判斷是否找到參數(shù)字符串。
  • startsWith():返回布爾值,判斷參數(shù)字符串是否在原字符串的頭部。
  • endsWith():返回布爾值,判斷參數(shù)字符串是否在原字符串的尾部。

注意事項

  • 這三個方法只返回布爾值,如果需要知道子串的位置,還是得用 indexOf 和 lastIndexOf 。
  • 這三個方法如果傳入了正則表達式而不是字符串,會拋出錯誤。而 indexOf 和 lastIndexOf 這兩個方法,它們會將正則表達式轉(zhuǎn)換為字符串并搜索它。

以上三個方法都可以接受兩個參數(shù),需要搜索的字符串,和可選的搜索起始位置索引。

let string = "apple,banana,orange";
string.includes("banana");     // true
string.startsWith("apple");    // true
string.endsWith("apple");      // false
string.startsWith("banana",6)  // true

3.1.1.2 字符串重復repeat

//repeat():返回新的字符串,表示將字符串重復指定次數(shù)返回。
console.log("Hello,".repeat(2));  // "Hello,Hello,"
//如果參數(shù)是小數(shù),向下取整
console.log("Hello,".repeat(3.2));  // "Hello,Hello,Hello,"
//如果參數(shù)是 0 至 -1 之間的小數(shù),會進行取整運算,0 至 -1 之間的小數(shù)取整得到 -0 ,等同于 repeat 零次
console.log("Hello,".repeat(-0.5));  // ""
//如果參數(shù)是 NaN,等同于 repeat 零次
console.log("Hello,".repeat(NaN));  // "" 
//如果參數(shù)是負數(shù)或者 Infinity ,會報錯
console.log("Hello,".repeat(-1));  // RangeError: Invalid count value
console.log("Hello,".repeat(Infinity));  // RangeError: Invalid count value
//如果傳入的參數(shù)是字符串,則會先將字符串轉(zhuǎn)化為數(shù)字
console.log("Hello,".repeat("hh")); // ""
console.log("Hello,".repeat("2"));  // "Hello,Hello,"

3.1.1.3 字符串補全padStart、padEnd

  • padStart:返回新的字符串,表示用參數(shù)字符串從頭部(左側(cè))補全原字符串。
  • padEnd:返回新的字符串,表示用參數(shù)字符串從尾部(右側(cè))補全原字符串。

以上兩個方法接受兩個參數(shù),第一個參數(shù)是指定生成的字符串的最小長度,第二個參數(shù)是用來補全的字符串。如果沒有指定第二個參數(shù),默認用空格填充。

console.log("h".padStart(5,"o"));  // "ooooh"
console.log("h".padEnd(5,"o"));    // "hoooo"
console.log("h".padStart(5));      // "    h"
//如果指定的長度小于或者等于原字符串的長度,則返回原字符串
console.log("hello".padStart(5,"A"));  // "hello"
//如果原字符串加上補全字符串長度大于指定長度,則截去超出位數(shù)的補全字符串
console.log("hello".padEnd(10,",world!"));  // "hello,worl"
//常用于補全位數(shù)
console.log("123".padStart(10,"0"));  // "0000000123"

3.1.2 模板字符串

模板字符串相當于加強版的字符串,用反引號?`,除了作為普通字符串,還可以用來定義多行字符串,還可以在字符串中加入變量和表達式。

3.1.2.1 基本用法

//普通字符串
let string = `Hello'\n'world`;
console.log(string); 
// "Hello'
// 'world"

//多行字符串
let string1 =  `Hey,
can you stop angry now?`;
console.log(string1);
// Hey,
// can you stop angry now?

//字符串插入變量和表達式。變量名寫在 ${} 中,${} 中可以放入 JavaScript 表達式。
let name = "Mike";
let age = 27;
let info = `My Name is ${name},I am ${age+1} years old next year.`
console.log(info);// My Name is Mike,I am 28 years old next year.

//字符串中調(diào)用函數(shù)
function f(){
  return "have fun!";
}
let string2= `Game start,${f()}`;
console.log(string2);  // Game start,have fun!

注意:模板字符串中的換行和空格都是會被保留的

3.1.2.2?標簽模板

標簽模板,是一個函數(shù)的調(diào)用,其中調(diào)用的參數(shù)是模板字符串

alert`Hello world!`;
// 等價于
alert('Hello world!');

//當模板字符串中帶有變量,會將模板字符串參數(shù)處理成多個參數(shù)
function f(stringArr,...values){
 let result = "";
 for(let i=0;i<stringArr.length;i++){
  result += stringArr[i];
  if(values[i]){
   result += values[i];
        }
    }
 return result;
}
let name = 'Mike';
let age = 27;
f`My Name is ${name},I am ${age+1} years old next year.`;
// "My Name is Mike,I am 28 years old next year."
 
f`My Name is ${name},I am ${age+1} years old next year.`;
// 等價于
f(['My Name is',',I am ',' years old next year.'],'Mike',28);

//過濾 HTML 字符串,防止用戶輸入惡意內(nèi)容
function f(stringArr,...values){
 let result = "";
 for(let i=0;i<stringArr.length;i++){
  result += stringArr[i];
   if(values[i]){
     result += String(values[i]).replace(/&/g, "&amp;")
               .replace(/</g, "&lt;")
               .replace(/>/g, "&gt;");
    }
 }
 return result;
}
name = '<Amy&MIke>';
f`<p>Hi, ${name}.I would like send you some message.</p>`;
// <p>Hi, &lt;Amy&amp;MIke&gt;.I would like send you some message.</p>


//國際化處理(轉(zhuǎn)化多國語言)
i18n`Hello ${name}, you are visitor number ${visitorNumber}.`;  // 你好**,你是第**位訪問者

3.2?數(shù)值

3.2.1 數(shù)值的表示

//二進制表示法新寫法: 前綴 0b 或 0B 
console.log(0b11 === 3); // true
console.log(0B11 === 3); // true
//八進制表示法新寫法: 前綴 0o 或 0O
console.log(0o11 === 9); // true
console.log(0O11 === 9); // true

3.2.2 常量

Number.EPSILON?屬性表示 1 與大于 1 的最小浮點數(shù)之間的差。

它的值接近于?2.2204460492503130808472633361816E-16,或者?2-52。

屬性特性:

  • writable:false
  • enumerable:false
  • configurable:false?
//測試數(shù)值是否在誤差范圍內(nèi)
0.1 + 0.2 === 0.3; // false
// 在誤差范圍內(nèi)即視為相等
equal = (Math.abs(0.1 - 0.3 + 0.2) < Number.EPSILON); // true

?安全整數(shù):安全整數(shù)表示在 JavaScript 中能夠精確表示的整數(shù),安全整數(shù)的范圍在 2 的 -53 次方到 2 的 53 次方之間(不包括兩個端點),超過這個范圍的整數(shù)無法精確表示。

安全整數(shù)屬性特性:

  • writable:false
  • enumerable:false
  • configurable:false?
//最大安全數(shù)
Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2; // true
Number.MAX_SAFE_INTEGER === Number.MAX_SAFE_INTEGER + 1;     // false
Number.MAX_SAFE_INTEGER - 1 === Number.MAX_SAFE_INTEGER - 2; // false
//最小安全數(shù)
Number.MIN_SAFE_INTEGER + 1 === Number.MIN_SAFE_INTEGER + 2; // false
Number.MIN_SAFE_INTEGER === Number.MIN_SAFE_INTEGER - 1;     // false
Number.MIN_SAFE_INTEGER - 1 === Number.MIN_SAFE_INTEGER - 2; // true

3.2.3 方法

/*
 Number.isFinite() 用于檢查一個數(shù)值是否為有限的( finite ),即不是 Infinity
*/
console.log( Number.isFinite(1));   // true
console.log( Number.isFinite(0.1)); // true 
// NaN 不是有限的
console.log( Number.isFinite(NaN)); // false 
console.log( Number.isFinite(Infinity));  // false
console.log( Number.isFinite(-Infinity)); // false 
// Number.isFinate 沒有隱式的 Number() 類型轉(zhuǎn)換,所有非數(shù)值都返回 false
console.log( Number.isFinite('foo')); // false
console.log( Number.isFinite('15'));  // false
console.log( Number.isFinite(true));  // false
Number.isNaN()
用于檢查一個值是否為 NaN 。
console.log(Number.isNaN(NaN));      // true
console.log(Number.isNaN('true'/0)); // true 
// 在全局的 isNaN() 中,以下皆返回 true,因為在判斷前會將非數(shù)值向數(shù)值轉(zhuǎn)換
// 而 Number.isNaN() 不存在隱式的 Number() 類型轉(zhuǎn)換,非 NaN 全部返回 false
Number.isNaN("NaN");      // false
Number.isNaN(undefined);  // false
Number.isNaN({});         // false
Number.isNaN("true");     // false

/*
Number.parseInt() 用于將給定字符串轉(zhuǎn)化為指定進制的整數(shù)
*/
// 不指定進制時默認為 10 進制
Number.parseInt('12.34'); // 12
Number.parseInt(12.34);   // 12
 // 指定進制
Number.parseInt('0011',2); // 3
 
// 與全局的 parseInt() 函數(shù)是同一個函數(shù)
Number.parseInt === parseInt; // true
Number.parseFloat()
用于把一個字符串解析成浮點數(shù)。
Number.parseFloat('123.45')    // 123.45
Number.parseFloat('123.45abc') // 123.45
 
// 無法被解析成浮點數(shù),則返回 NaN
Number.parseFloat('abc') // NaN
 
// 與全局的 parseFloat() 方法是同一個方法
Number.parseFloat === parseFloat // true
Number.isInteger()
用于判斷給定的參數(shù)是否為整數(shù)。
Number.isInteger(value)
Number.isInteger(0); // true
// JavaScript 內(nèi)部,整數(shù)和浮點數(shù)采用的是同樣的儲存方法,因此 1 與 1.0 被視為相同的值
Number.isInteger(1);   // true
Number.isInteger(1.0); // true
 
Number.isInteger(1.1);     // false
Number.isInteger(Math.PI); // false
 
// NaN 和正負 Infinity 不是整數(shù)
Number.isInteger(NaN);       // false
Number.isInteger(Infinity);  // false
Number.isInteger(-Infinity); // false
 
Number.isInteger("10");  // false
Number.isInteger(true);  // false
Number.isInteger(false); // false
Number.isInteger([1]);   // false
 
// 數(shù)值的精度超過 53 個二進制位時,由于第 54 位及后面的位被丟棄,會產(chǎn)生誤判
Number.isInteger(1.0000000000000001) // true
 
// 一個數(shù)值的絕對值小于 Number.MIN_VALUE(5E-324),即小于 JavaScript 能夠分辨
// 的最小值,會被自動轉(zhuǎn)為 0,也會產(chǎn)生誤判
Number.isInteger(5E-324); // false
Number.isInteger(5E-325); // true
Number.isSafeInteger()
用于判斷數(shù)值是否在安全范圍內(nèi)。
Number.isSafeInteger(Number.MIN_SAFE_INTEGER - 1); // false
Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1); // false

3.2.4 Math對象的擴展

3.2.4.1 普通計算

/*Math.cbrt 用于計算一個數(shù)的立方根*/
Math.cbrt(1);  // 1
Math.cbrt(0);  // 0
Math.cbrt(-1); // -1
// 會對非數(shù)值進行轉(zhuǎn)換
Math.cbrt('1'); // 1 
// 非數(shù)值且無法轉(zhuǎn)換為數(shù)值時返回 NaN
Math.cbrt('hhh'); // NaN

/*Math.imul 兩個數(shù)以 32 位帶符號整數(shù)形式相乘的結(jié)果,返回的也是一個 32 位的帶符號整數(shù)*/
// 大多數(shù)情況下,結(jié)果與 a * b 相同 
Math.imul(1, 2);   // 2 
// 用于正確返回大數(shù)乘法結(jié)果中的低位數(shù)值
Math.imul(0x7fffffff, 0x7fffffff); // 1

/*Math.hypot 用于計算所有參數(shù)的平方和的平方根*/
Math.hypot(3, 4); // 5 
// 非數(shù)值會先被轉(zhuǎn)換為數(shù)值后進行計算
Math.hypot(1, 2, '3'); // 3.741657386773941
Math.hypot(true);      // 1
Math.hypot(false);     // 0 
// 空值會被轉(zhuǎn)換為 0
Math.hypot();   // 0
Math.hypot([]); // 0 
// 參數(shù)為 Infinity 或 -Infinity 返回 Infinity
Math.hypot(Infinity); // Infinity
Math.hypot(-Infinity); // Infinity 
// 參數(shù)中存在無法轉(zhuǎn)換為數(shù)值的參數(shù)時返回 NaN
Math.hypot(NaN);         // NaN
Math.hypot(3, 4, 'foo'); // NaN
Math.hypot({});          // NaN

/*Math.clz32 用于返回數(shù)字的32 位無符號整數(shù)形式的前導0的個數(shù)*/
Math.clz32(0); // 32
Math.clz32(1); // 31
Math.clz32(0b01000000000100000000000000000000); // 1 
// 當參數(shù)為小數(shù)時,只考慮整數(shù)部分
Math.clz32(0.5); // 32 
// 對于空值或非數(shù)值,會轉(zhuǎn)化為數(shù)值再進行計算
Math.clz32('1');       // 31
Math.clz32();          // 32
Math.clz32([]);        // 32
Math.clz32({});        // 32
Math.clz32(NaN);       // 32
Math.clz32(Infinity);  // 32
Math.clz32(-Infinity); // 32
Math.clz32(undefined); // 32
Math.clz32('hhh');     // 32

3.2.4.2 數(shù)字處理

/*Math.trunc 用于返回數(shù)字的整數(shù)部分*/
Math.trunc(12.3); // 12
Math.trunc(12);   // 12 
// 整數(shù)部分為 0 時也會判斷符號
Math.trunc(-0.5); // -0
Math.trunc(0.5);  // 0 
// Math.trunc 會將非數(shù)值轉(zhuǎn)為數(shù)值再進行處理
Math.trunc("12.3"); // 12
// 空值或無法轉(zhuǎn)化為數(shù)值時時返回 NaN
Math.trunc();           // NaN
Math.trunc(NaN);        // NaN
Math.trunc("hhh");      // NaN
Math.trunc("123.2hhh"); // NaN

/*Math.fround 用于獲取數(shù)字的32位單精度浮點數(shù)形式*/
// 對于 2 的 24 次方取負至 2 的 24 次方之間的整數(shù)(不含兩個端點),返回結(jié)果與參數(shù)本身一致
Math.fround(-(2**24)+1);  // -16777215
Math.fround(2 ** 24 - 1); // 16777215 
// 用于將 64 位雙精度浮點數(shù)轉(zhuǎn)為 32 位單精度浮點數(shù)
Math.fround(1.234) // 1.125
// 當小數(shù)的精度超過 24 個二進制位,會丟失精度
Math.fround(0.3); // 0.30000001192092896
// 參數(shù)為 NaN 或 Infinity 時返回本身
Math.fround(NaN)      // NaN
Math.fround(Infinity) // Infinity 
// 參數(shù)為其他非數(shù)值類型時會將參數(shù)進行轉(zhuǎn)換 
Math.fround('5');  // 5
Math.fround(true); // 1
Math.fround(null); // 0
Math.fround([]);   // 0
Math.fround({});   // NaN

3.2.4.3 判斷

/*Math.sign 判斷數(shù)字的符號(正、負、0)*/
Math.sign(1);  // 1
Math.sign(-1); // -1 
// 參數(shù)為 0 時,不同符號的返回不同
Math.sign(0);  // 0
Math.sign(-0); // -0 
// 判斷前會對非數(shù)值進行轉(zhuǎn)換
Math.sign('1');  // 1
Math.sign('-1'); // -1   
// 參數(shù)為非數(shù)值(無法轉(zhuǎn)換為數(shù)值)時返回 NaN
Math.sign(NaN);   // NaN 
Math.sign('hhh'); // NaN

3.2.4.4 對數(shù)方法

/*Math.expm1() 用于計算 e 的 x 次方減 1 的結(jié)果,即 Math.exp(x) - 1*/
Math.expm1(1);  // 1.718281828459045
Math.expm1(0);  // 0
Math.expm1(-1); // -0.6321205588285577
// 會對非數(shù)值進行轉(zhuǎn)換
Math.expm1('0'); //0
// 參數(shù)不為數(shù)值且無法轉(zhuǎn)換為數(shù)值時返回 NaN
Math.expm1(NaN); // NaN

/*Math.log1p(x) 用于計算1 + x 的自然對數(shù),即 Math.log(1 + x)*/
Math.log1p(1);  // 0.6931471805599453
Math.log1p(0);  // 0
Math.log1p(-1); // -Infinity 
// 參數(shù)小于 -1 時返回 NaN
Math.log1p(-2); // NaN

/*Math.log10(x) 用于計算以 10 為底的 x 的對數(shù)*/
Math.log10(1);   // 0
// 計算前對非數(shù)值進行轉(zhuǎn)換
Math.log10('1'); // 0
// 參數(shù)為0時返回 -Infinity
Math.log10(0);   // -Infinity
// 參數(shù)小于0或參數(shù)不為數(shù)值(且無法轉(zhuǎn)換為數(shù)值)時返回 NaN
Math.log10(-1);  // NaN

/*Math.log2() 用于計算 2 為底的 x 的對數(shù)*/
Math.log2(1);   // 0
// 計算前對非數(shù)值進行轉(zhuǎn)換
Math.log2('1'); // 0
// 參數(shù)為0時返回 -Infinity
Math.log2(0);   // -Infinity
// 參數(shù)小于0或參數(shù)不為數(shù)值(且無法轉(zhuǎn)換為數(shù)值)時返回 NaN
Math.log2(-1);  // NaN

3.2.4.5 雙曲函數(shù)方法

  • Math.sinh(x): 用于計算雙曲正弦。
  • Math.cosh(x): 用于計算雙曲余弦。
  • Math.tanh(x): 用于計算雙曲正切。
  • Math.asinh(x): 用于計算反雙曲正弦。
  • Math.acosh(x): 用于計算反雙曲余弦。
  • Math.atanh(x): 用于計算反雙曲正切。

3.2.4.6 指數(shù)運算符

1 ** 2; // 1
// 右結(jié)合,從右至左計算
2 ** 2 ** 3; // 256
// **=
let exam = 2;
exam ** = 2; // 4

3.3?對象

3.3.1 字面量

/*屬性的簡潔表示法 ES6允許對象的屬性直接寫變量,這時候?qū)傩悦亲兞棵瑢傩灾凳亲兞恐?/
const age = 12;
const name = "Amy";
const person = {age, name};
person   //{age: 12, name: "Amy"}
//等同于
const person = {age: age, name: name}


/*方法名也可以簡寫*/
const person = {
  sayHi(){
    console.log("Hi");
  }
}
person.sayHi();  //"Hi"
//等同于
const person = {
  sayHi:function(){
    console.log("Hi");
  }
}
person.sayHi();//"Hi"
//如果是Generator 函數(shù),則要在前面加一個星號
const obj = {
  * myGenerator() {
    yield 'hello world';
  }
};
//等同于
const obj = {
  myGenerator: function* () {
    yield 'hello world';
  }
};


/*屬性名表達式 ES6允許用表達式作為屬性名,但是一定要將表達式放在方括號內(nèi)*/
const obj = {
 ["he"+"llo"](){
   return "Hi";
  }
}
obj.hello();  //"Hi"
//屬性的簡潔表示法和屬性名表達式不能同時使用,否則會報錯
const hello = "Hello";
const obj = {
 [hello]
};
obj  //SyntaxError: Unexpected token }
 
const hello = "Hello";
const obj = {
 [hello+"2"]:"world"
};
obj  //{Hello2: "world"}

3.3.2 擴展運算符

擴展運算符(...)用于取出參數(shù)對象所有可遍歷屬性然后拷貝到當前對象

//基本用法
let person = {name: "Amy", age: 15};
let someone = { ...person };
someone;  //{name: "Amy", age: 15}
//合并兩個對象
let age = {age: 15};
let name = {name: "Amy"};
let person = {...age, ...name};
person;  //{age: 15, name: "Amy"}
//自定義的屬性和拓展運算符對象里面屬性的相同的時候:自定義的屬性在拓展運算符后面,則拓展運算符對象內(nèi)部同名的屬性將被覆蓋掉
let person = {name: "Amy", age: 15};
let someone = { ...person, name: "Mike", age: 17};
someone;  //{name: "Mike", age: 17}
//自定義的屬性在拓展運算度前面,則變成設置新對象默認屬性值
let person = {name: "Amy", age: 15};
let someone = {name: "Mike", age: 17, ...person};
someone;  //{name: "Amy", age: 15}
//拓展運算符后面是空對象,沒有任何效果也不會報錯
let a = {...{}, a: 1, b: 2};
a;  //{a: 1, b: 2}
//拓展運算符后面是null或者undefined,沒有效果也不會報錯
let b = {...null, ...undefined, a: 1, b: 2};
b;  //{a: 1, b: 2}

3.3.3 新方法

3.3.3.1?Object.assign

Object.assign(target,source_1,...):用于將源對象的所有可枚舉屬性復制到目標對象中

//基本用法
let target = {a: 1};
let object2 = {b: 2};
let object3 = {c: 3};
Object.assign(target,object2,object3);  
// 第一個參數(shù)是目標對象,后面的參數(shù)是源對象
target;  // {a: 1, b: 2, c: 3

/*
如果目標對象和源對象有同名屬性,或者多個源對象有同名屬性,則后面的屬性會覆蓋前面的屬性
如果該函數(shù)只有一個參數(shù),當參數(shù)為對象時,直接返回該對象;當參數(shù)不是對象時,會先將參數(shù)轉(zhuǎn)為對象然后返回
*/
Object.assign(3);         // Number {3}
typeof Object.assign(3);  // "object"

//因為 null 和 undefined 不能轉(zhuǎn)化為對象,所以會報錯
Object.assign(null);       // TypeError: Cannot convert undefined or null to object
Object.assign(undefined);  // TypeError: Cannot convert undefined or null to object
//當參數(shù)不止一個時,null 和 undefined 不放第一個,即不為目標對象時,會跳過 null 和 undefined ,不報錯
Object.assign(1,undefined);  // Number {1}
Object.assign({a: 1},null);  // {a: 1} 
Object.assign(undefined,{a: 1});  // TypeError: Cannot convert undefined or null to object

//assign 的屬性拷貝是淺拷貝
let sourceObj = { a: { b: 1}};
let targetObj = {c: 3};
Object.assign(targetObj, sourceObj);
targetObj.a.b = 2;
sourceObj.a.b;  // 2
//同名屬性替換
targetObj = { a: { b: 1, c:2}};
sourceObj = { a: { b: "hh"}};
Object.assign(targetObj, sourceObj);
targetObj;  // {a: {b: "hh"}}
//數(shù)組的處理 會將數(shù)組處理成對象,所以先將 [2,3] 轉(zhuǎn)為 {0:2,1:3} ,然后再進行屬性復制,所以源對象的 0 號屬性覆蓋了目標對象的 0
Object.assign([2,3], [5]);  // [5,3]

3.3.3.2?Object.is

Object.is(value1, value2):用來比較兩個值是否嚴格相等,與(===)基本類似。

//基本用法
Object.is("q","q");      // true
Object.is(1,1);          // true
Object.is([1],[1]);      // false
Object.is({q:1},{q:1});  // false

//與(===)的區(qū)別
//一是+0不等于-0
Object.is(+0,-0);  //false
+0 === -0  //true
//二是NaN等于本身
Object.is(NaN,NaN); //true
NaN === NaN  //false

3.4?數(shù)組

3.4.1 數(shù)組創(chuàng)建

3.4.1.1?Array.of()

將參數(shù)中所有值作為元素形成數(shù)組

console.log(Array.of(1, 2, 3, 4)); // [1, 2, 3, 4]
// 參數(shù)值可為不同類型
console.log(Array.of(1, '2', true)); // [1, '2', true] 
// 參數(shù)為空時返回空數(shù)組
console.log(Array.of()); // []

3.4.1.2?Array.from()

將類數(shù)組對象或可迭代對象轉(zhuǎn)化為數(shù)組

語法:Array.from(arrayLike[, mapFn[, thisArg]])

參數(shù)說明:

  • arrayLike:想要轉(zhuǎn)換的類數(shù)組對象或可迭代對象
  • mapFn:可選,map 函數(shù),用于對每個元素進行處理,放入數(shù)組的是處理后的元素
  • thisArg:可選,用于指定 map 函數(shù)執(zhí)行時的 this 對象
// 參數(shù)為數(shù)組,返回與原數(shù)組一樣的數(shù)組
console.log(Array.from([1, 2])); // [1, 2] 
// 參數(shù)含空位
console.log(Array.from([1, , 3])); // [1, undefined, 3]

//mapFn
console.log(Array.from([1, 2, 3], (n) => n * 2)); // [2, 4, 6]

//thisArg
let map = {
    do: function(n) {
        return n * 2;
    }
}
let arrayLike = [1, 2, 3];
console.log(Array.from(arrayLike, function (n){
    return this.do(n);
}, map)); // [2, 4, 6]

3.4.1.3 類組對象

一個類數(shù)組對象必須含有 length 屬性,且元素屬性名必須是數(shù)值或者可轉(zhuǎn)換為數(shù)值的字符?

let arr = Array.from({
  0: '1',
  1: '2',
  2: 3,
  length: 3
});
console.log(arr); // ['1', '2', 3]
 
// 沒有 length 屬性,則返回空數(shù)組
let array = Array.from({
  0: '1',
  1: '2',
  2: 3,
});
console.log(array); // []
 
// 元素屬性名不為數(shù)值且無法轉(zhuǎn)換為數(shù)值,返回長度為 length 元素值為 undefined 的數(shù)組  
let array1 = Array.from({
  a: 1,
  b: 2,
  length: 2
});
console.log(array1); // [undefined, undefined]

3.4.1.4 轉(zhuǎn)換可迭代對象

//轉(zhuǎn)換map
let map = new Map();
map.set('key0', 'value0');
map.set('key1', 'value1');
console.log(Array.from(map)); // [['key0', 'value0'],['key1','value1']]
//轉(zhuǎn)換set
let arr = [1, 2, 3];
let set = new Set(arr);
console.log(Array.from(set)); // [1, 2, 3]
//轉(zhuǎn)換字符串
let str = 'abc';
console.log(Array.from(str)); // ["a", "b", "c"]

3.4.2 擴展方法

3.4.2.1 查找

  • find() 查找數(shù)組中符合條件的元素,若有多個符合條件的元素,則返回第一個元素
  • findIndex() 查找數(shù)組中符合條件的元素索引,若有多個符合條件的元素,則返回第一個元素索引
/* find() 查找數(shù)組中符合條件的元素,若有多個符合條件的元素,則返回第一個元素*/
let arr = Array.of(1, 2, 3, 4);
console.log(arr.find(item => item > 2)); // 3 
// 數(shù)組空位處理為 undefined
console.log([, 1].find(n => true)); // undefined

/*findIndex() 查找數(shù)組中符合條件的元素索引,若有多個符合條件的元素,則返回第一個元素索引*/
let arr = Array.of(1, 2, 1, 3);
// 參數(shù)1:回調(diào)函數(shù)
// 參數(shù)2(可選):指定回調(diào)函數(shù)中的 this 值
console.log(arr.findIndex(item => item == 2)); // 1 
// 數(shù)組空位處理為 undefined
console.log([, 1].findIndex(n => true)); //0

3.4.2.2 填充

  • fill() 將一定范圍索引的數(shù)組元素內(nèi)容填充為單個指定的值
  • copyWithin() 將一定范圍索引的數(shù)組元素修改為此數(shù)組另一指定范圍索引的元素
/*fill() 將一定范圍索引的數(shù)組元素內(nèi)容填充為單個指定的值*/
let arr = Array.of(1, 2, 3, 4);
// 參數(shù)1:用來填充的值
// 參數(shù)2:被填充的起始索引
// 參數(shù)3(可選):被填充的結(jié)束索引,默認為數(shù)組末尾
console.log(arr.fill(0,1,2)); // [1, 0, 3, 4]

/*copyWithin() 將一定范圍索引的數(shù)組元素修改為此數(shù)組另一指定范圍索引的元素*/
// 參數(shù)1:被修改的起始索引
// 參數(shù)2:被用來覆蓋的數(shù)據(jù)的起始索引
// 參數(shù)3(可選):被用來覆蓋的數(shù)據(jù)的結(jié)束索引,默認為數(shù)組末尾
console.log([1, 2, 3, 4].copyWithin(0,2,4)); // [3, 4, 3, 4] 
// 參數(shù)1為負數(shù)表示倒數(shù)
console.log([1, 2, 3, 4].copyWithin(-2, 0)); // [1, 2, 1, 2] 
console.log([1, 2, ,4].copyWithin(0, 2, 4)); // [, 4, , 4]

3.4.2.3 遍歷

  • entries() 遍歷鍵值對
  • keys() 遍歷鍵名
  • values() 遍歷鍵值
/*entries()??????? 遍歷鍵值對*/
for(let [key, value] of ['a', 'b'].entries()){
    console.log(key, value);
}
// 0 "a"
// 1 "b"
 
// 不使用 for... of 循環(huán)
let entries = ['a', 'b'].entries();
console.log(entries.next().value); // [0, "a"]
console.log(entries.next().value); // [1, "b"]
 
// 數(shù)組含空位
console.log([...[,'a'].entries()]); // [[0, undefined], [1, "a"]]

/*keys()??????? 遍歷鍵名*/
for(let key of ['a', 'b'].keys()){
    console.log(key);
}
// 0
// 1
 
// 數(shù)組含空位
console.log([...[,'a'].keys()]); // [0, 1]

/*values()??????? 遍歷鍵值*/
for(let value of ['a', 'b'].values()){
    console.log(value);
}
// "a"
// "b"
 
// 數(shù)組含空位
console.log([...[,'a'].values()]); // [undefined, "a"]

3.4.2.4 包含

  • includes() 數(shù)組是否包含指定值

注意:與 Set 和 Map 的 has 方法區(qū)分;Set 的 has 方法用于查找值;Map 的 has 方法用于查找鍵名

// 參數(shù)1:包含的指定值
[1, 2, 3].includes(1);    // true
 
// 參數(shù)2:可選,搜索的起始索引,默認為0
[1, 2, 3].includes(1, 2); // false
 
// NaN 的包含判斷
[1, NaN, 3].includes(NaN); // true

3.4.2.5?嵌套數(shù)組轉(zhuǎn)一維數(shù)組

  • flat()
  • flatMap()??????? 先對數(shù)組中每個元素進行了的處理,再對數(shù)組執(zhí)行 flat() 方法
/*flat()*/
console.log([1 ,[2, 3]].flat()); // [1, 2, 3] 
// 指定轉(zhuǎn)換的嵌套層數(shù)
console.log([1, [2, [3, [4, 5]]]].flat(2)); // [1, 2, 3, [4, 5]] 
// 不管嵌套多少層
console.log([1, [2, [3, [4, 5]]]].flat(Infinity)); // [1, 2, 3, 4, 5] 
// 自動跳過空位
console.log([1, [2, , 3]].flat());<p> // [1, 2, 3]

/*flatMap()??????? 先對數(shù)組中每個元素進行了的處理,再對數(shù)組執(zhí)行 flat() 方法*/
// 參數(shù)1:遍歷函數(shù),該遍歷函數(shù)可接受3個參數(shù):當前元素、當前元素索引、原數(shù)組
// 參數(shù)2:指定遍歷函數(shù)中 this 的指向
console.log([1, 2, 3].flatMap(n => [n * 2])); // [2, 4, 6]

3.4.3 數(shù)組緩沖區(qū)

  • 數(shù)組緩沖區(qū)是內(nèi)存中的一段地址
  • 定型數(shù)組的基礎
  • 實際字節(jié)數(shù)在創(chuàng)建時確定,之后只可修改其中的數(shù)據(jù),不可修改大小

3.4.3.1 通過構(gòu)造函數(shù)創(chuàng)建

let buffer = new ArrayBuffer(10);
console.log(buffer.byteLength); // 10
//分割已有數(shù)組緩沖區(qū)
let buffer = new ArrayBuffer(10);
let buffer1 = buffer.slice(1, 3);
console.log(buffer1.byteLength); // 2

3.4.3.2通過視圖創(chuàng)建

  • 視圖是用來操作內(nèi)存的接口
  • 視圖可以操作數(shù)組緩沖區(qū)或緩沖區(qū)字節(jié)的子集,并按照其中一種數(shù)值數(shù)據(jù)類型來讀取和寫入數(shù)據(jù)
  • DataView 類型是一種通用的數(shù)組緩沖區(qū)視圖,其支持所有8種數(shù)值型數(shù)據(jù)類型
// 默認 DataView 可操作數(shù)組緩沖區(qū)全部內(nèi)容
let buffer = new ArrayBuffer(10);
dataView = new DataView(buffer); 
dataView.setInt8(0,1);
console.log(dataView.getInt8(0)); // 1
 
// 通過設定偏移量(參數(shù)2)與長度(參數(shù)3)指定 DataView 可操作的字節(jié)范圍
let buffer1 = new ArrayBuffer(10);
dataView1 = new DataView(buffer1, 0, 3);
dataView1.setInt8(5,1); // RangeError

3.4.4 定型數(shù)組

數(shù)組緩沖區(qū)的特定類型的視圖,可以強制使用特定的數(shù)據(jù)類型,而不是使用通用的 DataView 對象來操作數(shù)組緩沖區(qū)

3.4.4.1?通過數(shù)組緩沖區(qū)生成

//通過數(shù)組緩沖區(qū)生成
let buffer = new ArrayBuffer(10),
    view = new Int8Array(buffer);
console.log(view.byteLength); // 10

3.4.4.2?通過構(gòu)造函數(shù)生成?

//通過構(gòu)造函數(shù)生成
let view = new Int32Array(10);
console.log(view.byteLength); // 40
console.log(view.length);     // 10
 
// 不傳參則默認長度為0
// 在這種情況下數(shù)組緩沖區(qū)分配不到空間,創(chuàng)建的定型數(shù)組不能用來保存數(shù)據(jù)
let view1 = new Int32Array();
console.log(view1.byteLength); // 0
console.log(view1.length);     // 0
 
// 可接受參數(shù)包括定型數(shù)組、可迭代對象、數(shù)組、類數(shù)組對象
let arr = Array.from({
  0: '1',
  1: '2',
  2: 3,
  length: 3
});
let view2 = new Int16Array([1, 2]),
    view3 = new Int32Array(view2),
    view4 = new Int16Array(new Set([1, 2, 3])),
    view5 = new Int16Array([1, 2, 3]),
    view6 = new Int16Array(arr);
console.log(view2 .buffer === view3.buffer); // false
console.log(view4.byteLength); // 6
console.log(view5.byteLength); // 6
console.log(view6.byteLength); // 6

?3.4.4.3?注意事項

/*
length 屬性不可寫,如果嘗試修改這個值,在非嚴格模式下會直接忽略該操作,
在嚴格模式下會拋出錯誤
*/
let view = new Int16Array([1, 2]);
view.length = 3;
console.log(view.length); // 2


/*
定型數(shù)組可使用 entries()、keys()、values()進行迭代
*/
let view = new Int16Array([1, 2]);
for(let [k, v] of view.entries()){
    console.log(k, v);
}
// 0 1
// 1 2

/*
find() 等方法也可用于定型數(shù)組,但是定型數(shù)組中的方法會額外檢查數(shù)值類型是否安全,也會通過 Symbol.species 確認方法的返回值是定型數(shù)組而非普通數(shù)組。concat() 方法由于兩個定型數(shù)組合并結(jié)果不確定,故不能用于定型數(shù)組;另外,由于定型數(shù)組的尺寸不可更改,可以改變數(shù)組的尺寸的方法,例如 splice() ,不適用于定型數(shù)組
*/
let view = new Int16Array([1, 2]);
view.find((n) > 1); // 2

/*
所有定型數(shù)組都含有靜態(tài) of() 方法和 from() 方法,運行效果分別與 Array.of() 方法和 Array.from() 方法相似,區(qū)別是定型數(shù)組的方法返回定型數(shù)組,而普通數(shù)組的方法返回普通數(shù)組
*/
let view = Int16Array.of(1, 2);
console.log(view instanceof Int16Array); // true

/*
定型數(shù)組不是普通數(shù)組,不繼承自 Array
*/
let view = new Int16Array([1, 2]);
console.log(Array.isArray(view)); // false

/*
定型數(shù)組中增加了 set() 與 subarray() 方法。 set() 方法用于將其他數(shù)組復制到已有定型數(shù)組, subarray() 用于提取已有定型數(shù)組的一部分形成新的定型數(shù)組
*/
// set 方法
// 參數(shù)1:一個定型數(shù)組或普通數(shù)組
// 參數(shù)2:可選,偏移量,開始插入數(shù)據(jù)的位置,默認為0
let view= new Int16Array(4);
view.set([1, 2]);
view.set([3, 4], 2);
console.log(view); // [1, 2, 3, 4]
 
// subarray 方法
// 參數(shù)1:可選,開始位置
// 參數(shù)2:可選,結(jié)束位置(不包含結(jié)束位置)
let view= new Int16Array([1, 2, 3, 4]), 
    subview1 = view.subarray(), 
    subview2 = view.subarray(1), 
    subview3 = view.subarray(1, 3);
console.log(subview1); // [1, 2, 3, 4]
console.log(subview2); // [2, 3, 4]
console.log(subview3); // [2, 3]

3.4.5 擴展運算符

//復制數(shù)組
let arr = [1, 2],
    arr1 = [...arr];
console.log(arr1); // [1, 2]
 
// 數(shù)組含空位
let arr2 = [1, , 3],
    arr3 = [...arr2];
console.log(arr3); [1, undefined, 3]

//合并數(shù)組
console.log([...[1, 2],...[3, 4]]); // [1, 2, 3, 4]

3.5?Map與Set

3.5.1 Map

Map 對象保存鍵值對。任何值(對象或者原始值) 都可以作為一個鍵或一個值。

3.5.1.1 Map與Object的區(qū)別

  • 一個 Object 的鍵只能是字符串或者 Symbols,但一個 Map 的鍵可以是任意值。
  • Map 中的鍵值是有序的(FIFO 原則),而添加到對象中的鍵則不是。
  • Map 的鍵值對個數(shù)可以從 size 屬性獲取,而 Object 的鍵值對個數(shù)只能手動計算。
  • Object 都有自己的原型,原型鏈上的鍵名有可能和你自己在對象上的設置的鍵名產(chǎn)生沖突。

3.5.1.2 Map中的key

//key是字符串
var myMap = new Map();
var keyString = "a string"; 
myMap.set(keyString, "和鍵'a string'關聯(lián)的值"); 
myMap.get(keyString);    // "和鍵'a string'關聯(lián)的值"

//key是對象
var myMap = new Map();
var keyObj = {}; 
myMap.set(keyObj, "和鍵 keyObj 關聯(lián)的值");?
myMap.get(keyObj); // "和鍵 keyObj 關聯(lián)的值"
myMap.get({}); // undefined, 因為 keyObj !== {}

//key是函數(shù)
var myMap = new Map();
var keyFunc = function () {}; // 函數(shù) 
myMap.set(keyFunc, "和鍵 keyFunc 關聯(lián)的值"); 
myMap.get(keyFunc); // "和鍵 keyFunc 關聯(lián)的值"
myMap.get(function() {}) // undefined, 因為 keyFunc !== function () {}

//key是NaN 
//雖然 NaN 和任何值甚至和自己都不相等(NaN !== NaN 返回true),NaN作為Map的鍵來說是沒有區(qū)別的。
var myMap = new Map();
myMap.set(NaN, "not a number"); 
myMap.get(NaN); // "not a number" 
var otherNaN = Number("foo");
myMap.get(otherNaN); // "not a number"

3.5.1.3 Map的迭代

對 Map 進行遍歷,以下兩個最高級。

for...of

var myMap = new Map();
myMap.set(0, "zero");
myMap.set(1, "one"); 
// 將會顯示兩個 log。 一個是 "0 = zero" 另一個是 "1 = one"
for (var [key, value] of myMap) {
  console.log(key + " = " + value);
}
/* 
這個 entries 方法返回一個新的 Iterator 對象,它按插入順序包含了 Map 對象中每個元素的 [key, value] 數(shù)組。 
將會顯示兩個 log。 一個是 "0 = zero" 另一個是 "1 = one"
*/
for (var [key, value] of myMap.entries()) {
  console.log(key + " = " + value);
}
/*
 這個 keys 方法返回一個新的 Iterator 對象, 它按插入順序包含了 Map 對象中每個元素的鍵。
 將會顯示兩個log。 一個是 "0" 另一個是 "1" 
*/
for (var key of myMap.keys()) {
  console.log(key);
}
/* 
這個 values 方法返回一個新的 Iterator 對象,它按插入順序包含了 Map 對象中每個元素的值。
將會顯示兩個log。 一個是 "zero" 另一個是 "one" 
*/
for (var value of myMap.values()) {
  console.log(value);
}

forEach

var myMap = new Map();
myMap.set(0, "zero");
myMap.set(1, "one"); 
// 將會顯示兩個 logs。 一個是 "0 = zero" 另一個是 "1 = one"
myMap.forEach(function(value, key) {
  console.log(key + " = " + value);
}, myMap)

3.5.1.4 Map對象的操作

//Map 與 Array的轉(zhuǎn)換
var kvArray = [["key1", "value1"], ["key2", "value2"]]; 
// Map 構(gòu)造函數(shù)可以將一個 二維 鍵值對數(shù)組轉(zhuǎn)換成一個 Map 對象
var myMap = new Map(kvArray); 
// 使用 Array.from 函數(shù)可以將一個 Map 對象轉(zhuǎn)換成一個二維鍵值對數(shù)組
var outArray = Array.from(myMap);

//Map的克隆
var myMap1 = new Map([["key1", "value1"], ["key2", "value2"]]);
var myMap2 = new Map(myMap1);
// 打印 false。 Map 對象構(gòu)造函數(shù)生成實例,迭代出新的對象。
console.log(myMap1=== myMap2); 

//Map的合并
var first = new Map([[1, 'one'], [2, 'two'], [3, 'three'],]);
var second = new Map([[1, 'uno'], [2, 'dos']]); 
// 合并兩個 Map 對象時,如果有重復的鍵值,則后面的會覆蓋前面的,對應值即 uno,dos, three
var merged = new Map([...first, ...second]);

3.5.2 Set

Set 對象允許你存儲任何類型的唯一值,無論是原始值或者是對象引用。

3.5.2.1 Set中的特殊值

Set 對象存儲的值總是唯一的,所以需要判斷兩個值是否恒等。有幾個特殊值需要特殊對待:

  • +0 與 -0 在存儲判斷唯一性的時候是恒等的,所以不重復
  • undefined 與 undefined 是恒等的,所以不重復
  • NaN 與 NaN 是不恒等的,但是在 Set 中只能存一個,不重復
let mySet = new Set();
 
mySet.add(1); // Set(1) {1}
mySet.add(5); // Set(2) {1, 5}
mySet.add(5); // Set(2) {1, 5} 這里體現(xiàn)了值的唯一性
mySet.add("some text"); // Set(3) {1, 5, "some text"} 這里體現(xiàn)了類型的多樣性

var o = {a: 1, b: 2}; 
mySet.add(o);
// Set(5) {1, 5, "some text", {…}, {…}} 
//這里體現(xiàn)了對象之間引用不同不恒等,即使值相同,Set 也能存儲
mySet.add({a: 1, b: 2}); 

3.5.2.2 類型轉(zhuǎn)換

// Array 轉(zhuǎn) Set
var mySet = new Set(["value1", "value2", "value3"]);
// 用...操作符,將 Set 轉(zhuǎn) Array
var myArray = [...mySet];
// String 轉(zhuǎn) Set
// 注:Set 中 toString 方法是不能將 Set 轉(zhuǎn)換成 String
var mySet = new Set('hello');  // Set(4) {"h", "e", "l", "o"}

3.5.2.3 作用

//數(shù)組去重
var mySet = new Set([1, 2, 3, 4, 4]);
[...mySet]; // [1, 2, 3, 4]

//并集
var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
var union = new Set([...a, ...b]); // {1, 2, 3, 4}

//交集
var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
var intersect = new Set([...a].filter(x => b.has(x))); // {2, 3}

//差集
var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
var difference = new Set([...a].filter(x => !b.has(x))); // {1}

3.6?Reflect與Proxy

3.6.1概述

Proxy 與 Reflect 是 ES6 為了操作對象引入的 API 。

Proxy 可以對目標對象的讀取、函數(shù)調(diào)用等操作進行攔截,然后進行操作處理。它不直接操作對象,而是像代理模式,通過對象的代理對象進行操作,在進行這些操作時,可以添加一些需要的額外操作。

Reflect 可以用于獲取目標對象的行為,它與 Object 類似,但是更易讀,為操作對象提供了一種更優(yōu)雅的方式。它的方法與 Proxy 是對應的。

3.6.2 Proxy

3.6.2.1 基本用法

一個 Proxy 對象由兩個部分組成: target 、 handler 。在通過 Proxy 構(gòu)造函數(shù)生成實例對象時,需要提供這兩個參數(shù)。 target 即目標對象, handler 是一個對象,聲明了代理 target 的指定行為

let target = {
    name: 'Tom',
    age: 24
}
let handler = {
    get: function(target, key) {
        console.log('getting '+key);
        return target[key]; // 不是target.key
    },
    set: function(target, key, value) {
        console.log('setting '+key);
        target[key] = value;
    }
}
let proxy = new Proxy(target, handler)
proxy.name     // 實際執(zhí)行 handler.get
proxy.age = 25 // 實際執(zhí)行 handler.set
// getting name
// setting age
// 25
 
// target 可以為空對象
let targetEpt = {}
let proxyEpt = new Proxy(targetEpt, handler)
// 調(diào)用 get 方法,此時目標對象為空,沒有 name 屬性
proxyEpt.name // getting name
// 調(diào)用 set 方法,向目標對象中添加了 name 屬性
proxyEpt.name = 'Tom'
// setting name
// "Tom"
// 再次調(diào)用 get ,此時已經(jīng)存在 name 屬性
proxyEpt.name
// getting name
// "Tom"
 
// 通過構(gòu)造函數(shù)新建實例時其實是對目標對象進行了淺拷貝,因此目標對象與代理對象會互相
// 影響
targetEpt
// {name: "Tom"}
 
// handler 對象也可以為空,相當于不設置攔截操作,直接訪問目標對象
let targetEmpty = {}
let proxyEmpty = new Proxy(targetEmpty,{})
proxyEmpty.name = "Tom"
targetEmpty // {name: "Tom"}

Proxy.revocable()?:用于返回一個可取消的 Proxy 實例

let {proxy, revoke} = Proxy.revocable({}, {});
proxy.name = "Tom";
revoke();
proxy.name 
// TypeError: Cannot perform 'get' on a proxy that has been revoked

3.6.2.2 Proxy之handler詳解

get(target, propKey, receiver):用于 target 對象上 propKey 的讀取操作

let exam ={
    name: "Tom",
    age: 24
}
let proxy = new Proxy(exam, {
  get(target, propKey, receiver) {
    console.log('Getting ' + propKey);
    return target[propKey];
  }
})
proxy.name 
// Getting name
// "Tom"


//get() 方法可以繼承
let proxy = new Proxy({}, {
  get(target, propKey, receiver) {
      // 實現(xiàn)私有屬性讀取保護
      if(propKey[0] === '_'){
          throw new Erro(`Invalid attempt to get private     "${propKey}"`);
      }
      console.log('Getting ' + propKey);
      return target[propKey];
  }
});
 
let obj = Object.create(proxy);
obj.name
// Getting name

set(target, propKey, value, receiver):用于攔截 target 對象上的 propKey 的賦值操作。如果目標對象自身的某個屬性,不可寫且不可配置,那么set方法將不起作用

注意:嚴格模式下,set代理如果沒有返回true,就會報錯

let validator = {
    set: function(obj, prop, value) {
        if (prop === 'age') {
            if (!Number.isInteger(value)) {
                throw new TypeError('The age is not an integer');
            }
            if (value > 200) {
                throw new RangeError('The age seems invalid');
            }
        }
        // 對于滿足條件的 age 屬性以及其他屬性,直接保存
        obj[prop] = value;
    }
};
let proxy= new Proxy({}, validator)
proxy.age = 100;
proxy.age           // 100
proxy.age = 'oppps' // 報錯
proxy.age = 300     // 報錯

//第四個參數(shù) receiver 表示原始操作行為所在對象,一般是 Proxy 實例本身
const handler = {
    set: function(obj, prop, value, receiver) {
        obj[prop] = receiver;
    }
};
const proxy = new Proxy({}, handler);
proxy.name= 'Tom';
proxy.name=== proxy // true
 
const exam = {}
Object.setPrototypeOf(exam, proxy)
exam.name = "Tom"
exam.name === exam // true

apply(target, ctx, args):用于攔截函數(shù)的調(diào)用、call 和 reply 操作。target 表示目標對象,ctx 表示目標對象上下文,args 表示目標對象的參數(shù)數(shù)組

function sub(a, b){
    return a - b;
}
let handler = {
    apply: function(target, ctx, args){
        console.log('handle apply');
        return Reflect.apply(...arguments);
    }
}
let proxy = new Proxy(sub, handler)
proxy(2, 1) 
// handle apply
// 1

has(target, propKey):用于攔截 HasProperty 操作,即在判斷 target 對象是否存在 propKey 屬性時,會被這個方法攔截。此方法不判斷一個屬性是對象自身的屬性,還是繼承的屬性

注意:此方法不攔截 for ... in 循環(huán)

let  handler = {
    has: function(target, propKey){
        console.log("handle has");
        return propKey in target;
    }
}
let exam = {name: "Tom"}
let proxy = new Proxy(exam, handler)
'name' in proxy
// handle has
// true

construct(target, args):用于攔截 new 命令。返回值必須為對象

let handler = {
    construct: function (target, args, newTarget) {
        console.log('handle construct')
        return Reflect.construct(target, args, newTarget)  
    }
}
class Exam { 
    constructor (name) {  
        this.name = name 
    }
}
let ExamProxy = new Proxy(Exam, handler)
let proxyObj = new ExamProxy('Tom')
console.log(proxyObj)
// handle construct
// exam {name: "Tom"}

deleteProperty(target, propKey):用于攔截 delete 操作,如果這個方法拋出錯誤或者返回 false ,propKey 屬性就無法被 delete 命令刪除

defineProperty(target, propKey,propDesc):用于攔截 Object.definePro若目標對象不可擴展,增加目標對象上不存在的屬性會報錯;若屬性不可寫或不可配置,則不能改變這些屬性

let handler = {
    defineProperty: function(target, propKey, propDesc){
        console.log("handle defineProperty");
        return true;
    }
}
let target = {}
let proxy = new Proxy(target, handler)
proxy.name = "Tom"
// handle defineProperty
target
// {name: "Tom"}
 
// defineProperty 返回值為false,添加屬性操作無效
let handler1 = {
    defineProperty: function(target, propKey, propDesc){
        console.log("handle defineProperty");
        return false;
    }
}
let target1 = {}
let proxy1 = new Proxy(target1, handler1)
proxy1.name = "Jerry"
target1
// {}

getOwnPropertyDescriptor(target, propKey):用于攔截 Object.getOwnPropertyD() 返回值為屬性描述對象或者 undefined

let handler = {
    getOwnPropertyDescriptor: function(target, propKey){
        return Object.getOwnPropertyDescriptor(target, propKey);
    }
}
let target = {name: "Tom"}
let proxy = new Proxy(target, handler)
Object.getOwnPropertyDescriptor(proxy, 'name')
// {value: "Tom", writable: true, enumerable: true, configurable: 
// true}

getPrototypeOf(target):主要用于攔截獲取對象原型的操作

包括以下操作:

  • Object.prototype._proto_
  • Object.prototype.isPrototypeOf()
  • Object.getPrototypeOf()
  • Reflect.getPrototypeOf()
  • instanceof
let exam = {}
let proxy = new Proxy({},{
    getPrototypeOf: function(target){
        return exam;
    }
})
Object.getPrototypeOf(proxy) // {}

/*返回值必須是對象或者 null ,否則報錯。另外,如果目標對象不可擴展(non-extensible),getPrototypeOf 方法必須返回目標對象的原型對象*/
let proxy = new Proxy({},{
    getPrototypeOf: function(target){
        return true;
    }
})
Object.getPrototypeOf(proxy)
// TypeError: 'getPrototypeOf' on proxy: trap returned neither object // nor null

isExtensible(target):用于攔截 Object.isExtensible 操作,該方法只能返回布爾值,否則返回值會被自動轉(zhuǎn)為布爾值

let proxy = new Proxy({},{
    isExtensible:function(target){
        return true;
    }
})
Object.isExtensible(proxy) // true

/*它的返回值必須與目標對象的isExtensible屬性保持一致,否則會拋出錯誤*/
let proxy = new Proxy({},{
    isExtensible:function(target){
        return false;
    }
})
Object.isExtensible(proxy)
// TypeError: 'isExtensible' on proxy: trap result does not reflect 
// extensibility of proxy target (which is 'true')

ownKeys(target):用于攔截對象自身屬性的讀取操作

主要包括以下操作:

  • Object.getOwnPropertyNames()
  • Object.getOwnPropertySymbols()
  • Object.keys()
  • or...in
/*
方法返回的數(shù)組成員,只能是字符串或 Symbol 值,否則會報錯。
若目標對象中含有不可配置的屬性,則必須將這些屬性在結(jié)果中返回,否則就會報錯。
若目標對象不可擴展,則必須全部返回且只能返回目標對象包含的所有屬性,不能包含不存在的屬性,否則也會報錯。
*/
let proxy = new Proxy( {
  name: "Tom",
  age: 24
}, {
    ownKeys(target) {
        return ['name'];
    }
});
Object.keys(proxy)
// [ 'name' ]f返回結(jié)果中,三類屬性會被過濾:
//          - 目標對象上沒有的屬性
//          - 屬性名為 Symbol 值的屬性
//          - 不可遍歷的屬性
 
let target = {
  name: "Tom",
  [Symbol.for('age')]: 24,
};
// 添加不可遍歷屬性 'gender'
Object.defineProperty(target, 'gender', {
  enumerable: false,
  configurable: true,
  writable: true,
  value: 'male'
});
let handler = {
    ownKeys(target) {
        return ['name', 'parent', Symbol.for('age'), 'gender'];
    }
};
let proxy = new Proxy(target, handler);
Object.keys(proxy)
// ['name']

preventExtensions(target) :攔截 Object.preventExtensions 操作,該方法必須返回一個布爾值,否則會自動轉(zhuǎn)為布爾值

// 只有目標對象不可擴展時(即 Object.isExtensible(proxy) 為 false ),
// proxy.preventExtensions 才能返回 true ,否則會報錯
var proxy = new Proxy({}, {
  preventExtensions: function(target) {
    return true;
  }
});
// 由于 proxy.preventExtensions 返回 true,此處也會返回 true,因此會報錯
Object.preventExtensions(proxy) 被// TypeError: 'preventExtensions' on proxy: trap returned truish but // the proxy target is extensible
 
// 解決方案
 var proxy = new Proxy({}, {
  preventExtensions: function(target) {
    // 返回前先調(diào)用 Object.preventExtensions
    Object.preventExtensions(target);
    return true;
  }
});
Object.preventExtensions(proxy)
// Proxy {}

setPrototypeOf:主要用來攔截 Object.setPrototypeOf 方法,返回值必須為布爾值,否則會被自動轉(zhuǎn)為布爾值,若目標對象不可擴展,setPrototypeOf 方法不得改變目標對象的原型。

let proto = {}
let proxy = new Proxy(function () {}, {
    setPrototypeOf: function(target, proto) {
        console.log("setPrototypeOf");
        return true;
    }
}
);
Object.setPrototypeOf(proxy, proto);
// setPrototypeOf

3.6.3 Reflect

ES6 中將 Object 的一些明顯屬于語言內(nèi)部的方法移植到了 Reflect 對象上(當前某些方法會同時存在于 Object 和 Reflect 對象上),未來的新方法會只部署在 Reflect 對象上。

Reflect 對象對某些方法的返回結(jié)果進行了修改,使其更合理。

Reflect 對象使用函數(shù)的方式實現(xiàn)了 Object 的命令式操作。

3.6.3.1 靜態(tài)方法

Reflect.get(target, name, receiver):查找并返回 target 對象的 name 屬性

let exam = {
    name: "Tom",
    age: 24,
    get info(){
        return this.name + this.age;
    }
}
Reflect.get(exam, 'name'); // "Tom"
 
// 當 target 對象中存在 name 屬性的 getter 方法, getter 方法的 this 會綁定 // receiver
let receiver = {
    name: "Jerry",
    age: 20
}
Reflect.get(exam, 'info', receiver); // Jerry20
 
// 當 name 為不存在于 target 對象的屬性時,返回 undefined
Reflect.get(exam, 'birth'); // undefined
 
// 當 target 不是對象時,會報錯
Reflect.get(1, 'name'); // TypeError

Reflect.set(target, name, value, receiver):將 target 的 name 屬性設置為 value。返回值為 boolean ,true 表示修改成功,false 表示失敗。當 target 為不存在的對象時,會報錯?

let exam = {
    name: "Tom",
    age: 24,
    set info(value){
        return this.age = value;
    }
}
exam.age; // 24
Reflect.set(exam, 'age', 25); // true
exam.age; // 25
 
// value 為空時會將 name 屬性清除
Reflect.set(exam, 'age', ); // true
exam.age; // undefined
 
// 當 target 對象中存在 name 屬性 setter 方法時,setter 方法中的 this 會綁定 // receiver , 所以修改的實際上是 receiver 的屬性,
let receiver = {
    age: 18
}
Reflect.set(exam, 'info', 1, receiver); // true
receiver.age; // 1
 
let receiver1 = {
    name: 'oppps'
}
Reflect.set(exam, 'info', 1, receiver1);
receiver1.age; // 1

Reflect.has(obj, name):是 name in obj 指令的函數(shù)化,用于查找 name 屬性在 obj 對象中是否存在。返回值為 boolean。如果 obj 不是對象則會報錯 TypeError

let exam = {
    name: "Tom",
    age: 24
}
Reflect.has(exam, 'name'); // true

Reflect.deleteProperty(obj, property):是 delete obj[property] 的函數(shù)化,用于刪除 obj 對象的 property 屬性,返回值為 boolean。如果 obj 不是對象則會報錯 TypeError

let exam = {
    name: "Tom",
    age: 24
}
Reflect.deleteProperty(exam , 'name'); // true
exam // {age: 24} 
// property 不存在時,也會返回 true
Reflect.deleteProperty(exam , 'name'); // true

Reflect.construct(obj, args):等同于 new target(...args)?

function exam(name){
    this.name = name;
}
Reflect.construct(exam, ['Tom']); // exam {name: "Tom"}

Reflect.getPrototypeOf(obj):用于讀取 obj 的 _proto_ 屬性。在 obj 不是對象時不會像 Object 一樣把 obj 轉(zhuǎn)為對象,而是會報錯

class Exam{}
let obj = new Exam()
Reflect.getPrototypeOf(obj) === Exam.prototype // true

?Reflect.setPrototypeOf(obj, newProto):用于設置目標對象的 prototype

let obj ={}
Reflect.setPrototypeOf(obj, Array.prototype); // true

Reflect.apply(func, thisArg, args):等同于 Function.prototype.apply.call(func, thisArg, args) 。func 表示目標函數(shù);thisArg 表示目標函數(shù)綁定的 this 對象;args 表示目標函數(shù)調(diào)用時傳入的參數(shù)列表,可以是數(shù)組或類似數(shù)組的對象。若目標函數(shù)無法調(diào)用,會拋出 TypeError?

Reflect.apply(Math.max, Math, [1, 3, 5, 3, 1]); // 5

Reflect.defineProperty(target, propertyKey, attributes):用于為目標對象定義屬性。如果 target 不是對象,會拋出錯誤

let myDate= {}
Reflect.defineProperty(MyDate, 'now', {
  value: () => Date.now()
}); // true
 
const student = {};
Reflect.defineProperty(student, "name", {value: "Mike"}); // true
student.name; // "Mike"

?Reflect.getOwnPropertyDescriptor(target, propertyKey):用于得到 target 對象的 propertyKey 屬性的描述對象。在 target 不是對象時,會拋出錯誤表示參數(shù)非法,不會將非對象轉(zhuǎn)換為對象

var exam = {}
Reflect.defineProperty(exam, 'name', {
  value: true,
  enumerable: false,
})
Reflect.getOwnPropertyDescriptor(exam, 'name')
// { configurable: false, enumerable: false, value: true, writable:
// false}
 
 
// propertyKey 屬性在 target 對象中不存在時,返回 undefined
Reflect.getOwnPropertyDescriptor(exam, 'age') // undefined

Reflect.isExtensible(target):用于判斷 target 對象是否可擴展。返回值為 boolean 。如果 target 參數(shù)不是對象,會拋出錯誤

let exam = {}
Reflect.isExtensible(exam) // true

?Reflect.preventExtensions(target):用于讓 target 對象變?yōu)椴豢蓴U展。如果 target 參數(shù)不是對象,會拋出錯誤

let exam = {}
Reflect.preventExtensions(exam) // true

Reflect.ownKeys(target):用于返回 target 對象的所有屬性,等同于 Object.getOwnPropertyNames 與Object.getOwnPropertySymbols 之和

var exam = {
  name: 1,
  [Symbol.for('age')]: 4
}
Reflect.ownKeys(exam) // ["name", Symbol(age)]

3.6.4 Proxy與Reflect組合使用

Reflect 對象的方法與 Proxy 對象的方法是一一對應的。所以 Proxy 對象的方法可以通過調(diào)用 Reflect 對象的方法獲取默認行為,然后進行額外操作。

let exam = {
    name: "Tom",
    age: 24
}
let handler = {
    get: function(target, key){
        console.log("getting "+key);
        return Reflect.get(target,key);
    },
    set: function(target, key, value){
        console.log("setting "+key+" to "+value)
        Reflect.set(target, key, value);
    }
}
let proxy = new Proxy(exam, handler)
proxy.name = "Jerry"
proxy.name
// setting name to Jerry
// getting name
// "Jerry"

3.6.5 使用場景擴展

//實現(xiàn)觀察者模式
// 定義 Set 集合
const queuedObservers = new Set();
// 把觀察者函數(shù)都放入 Set 集合中
const observe = fn => queuedObservers.add(fn);
// observable 返回原始對象的代理,攔截賦值操作
const observable = obj => new Proxy(obj, {set});
function set(target, key, value, receiver) {
  // 獲取對象的賦值操作
  const result = Reflect.set(target, key, value, receiver);
  // 執(zhí)行所有觀察者
  queuedObservers.forEach(observer => observer());
  // 執(zhí)行賦值操作
  return result;
}

4.運算符與語句

4.1 函數(shù)

4.1.1 函數(shù)參數(shù)的擴展—默認參數(shù)

/*基本用法*/
function fn(name,age=17){
 console.log(name+","+age);
}
fn("Amy",18);  // Amy,18
fn("Amy","");  // Amy,
fn("Amy");     // Amy,17

/*使用函數(shù)默認參數(shù)時,不允許有同名參數(shù)*/
// 不報錯
function fn(name,name){
 console.log(name);
} 
// 報錯  SyntaxError: Duplicate parameter name not allowed in this context
function fn(name,name,age=17){
 console.log(name+","+age);
}

/*只有在未傳遞參數(shù),或者參數(shù)為 undefined 時,才會使用默認參數(shù),null 值被認為是有效的值傳遞*/
function fn(name,age=17){
    console.log(name+","+age);
}
fn("Amy",null); // Amy,null

/*
函數(shù)參數(shù)默認值存在暫時性死區(qū),在函數(shù)參數(shù)默認值表達式中,還未初始化賦值的參數(shù)值無法作為其他參數(shù)的默認值*/
function f(x,y=x){
    console.log(x,y);
}
f(1);  // 1 1 
function f(x=y){
    console.log(x);
}
f();  // ReferenceError: y is not defined

4.1.2?函數(shù)參數(shù)的擴展—不定參數(shù)

不定參數(shù)用來表示不確定參數(shù)個數(shù),形如,...變量名,由...加上一個具名參數(shù)標識符組成。具名參數(shù)只能放在參數(shù)組的最后,并且有且只有一個不定參數(shù)

function f(...values){
    console.log(values.length);
}
f(1,2);      //2
f(1,2,3,4);  //4

4.1.3 箭頭函數(shù)

箭頭函數(shù)提供了一種更加簡潔的函數(shù)書寫方式?;菊Z法是:箭頭=>函數(shù)體

4.1.3.1 基本用法

/*基本用法*/
var f = v => v;
//等價于
var f = function(a){
 return a;
}
f(1);  //1

/*當箭頭函數(shù)沒有參數(shù)或者有多個參數(shù),要用 () 括起來*/
var f = (a,b) => a+b;
f(6,2);  //8

/*當箭頭函數(shù)函數(shù)體有多行語句,用 {} 包裹起來,表示代碼塊,當只有一行語句,并且需要返回結(jié)果時,可以省略 {} , 結(jié)果會自動返回。*/
var f = (a,b) => {
 let result = a+b;
 return result;
}
f(6,2);  // 8

/*當箭頭函數(shù)要返回對象的時候,為了區(qū)分于代碼塊,要用 () 將對象包裹起來*/
// 報錯
var f = (id,name) => {id: id, name: name};
f(6,2);  // SyntaxError: Unexpected token
// 不報錯
var f = (id,name) => ({id: id, name: name});
f(6,2);  // {id: 6, name: 2}

/*沒有 this、super、arguments 和 new.target 綁定*/
var func = () => {
  // 箭頭函數(shù)里面沒有 this 對象,
  // 此時的 this 是外層的 this 對象,即 Window 
  console.log(this)
}
func(55)  // Window 
 
var func = () => {    
  console.log(arguments)
}
func(55);  // ReferenceError: arguments is not defined

/*箭頭函數(shù)體中的 this 對象,是定義函數(shù)時的對象,而不是使用函數(shù)時的對象*/
function fn(){
  setTimeout(()=>{
    // 定義時,this 綁定的是 fn 中的 this 對象
    console.log(this.a);
  },0)
}
var a = 20;
// fn 的 this 對象為 {a: 18}
fn.call({a: 18});  // 18
//不可以作為構(gòu)造函數(shù),也就是不能使用 new 命令,否則會報錯

4.1.3.2 適合使用箭頭函數(shù)的場景

當我們需要維護一個 this 上下文的時候,就可以使用箭頭函數(shù)

/*當我們需要維護一個 this 上下文的時候,就可以使用箭頭函數(shù)*/
// 回調(diào)函數(shù)
var Person = {
    'age': 18,
    'sayHello': function () {
      setTimeout(function () {
        console.log(this.age);
      });
    }
};
var age = 20;
Person.sayHello();  // 20 此時 this 指向的是全局對象 
var Person1 = {
    'age': 18,
    'sayHello': function () {
      setTimeout(()=>{
        console.log(this.age);
      });
    }
};
var age = 20;
Person1.sayHello();  // 18

?4.1.3.3 不適合使用箭頭函數(shù)的場景

1)定義函數(shù)的方法,且該方法中包含 this

/*定義函數(shù)的方法,且該方法中包含 this時,不適合使用剪頭函數(shù)*/
var Person = {
    'age': 18,
    'sayHello': ()=>{
        console.log(this.age);
      }
};
var age = 20;
Person.sayHello();  // 20  此時 this 指向的是全局對象 
var Person1 = {
    'age': 18,
    'sayHello': function () {
        console.log(this.age);
    }
};
var age = 20;
Person1.sayHello();   // 18 此時的 this 指向 Person1 對象

2)需要動態(tài) this 的時候

/*需要動態(tài) this 的時候 不適合使用剪頭函數(shù)*/
//button 的監(jiān)聽函數(shù)是箭頭函數(shù),所以監(jiān)聽函數(shù)里面的 this 指向的是定義的時候外層的 this 對象,即 Window,導致無法操作到被點擊的按鈕對象
var button = document.getElementById('userClick');
button.addEventListener('click', () => {
     this.classList.toggle('on');
});

4.2 Class類

在ES6中,class (類)作為對象的模板被引入,可以通過 class 關鍵字定義類。class 的本質(zhì)是 function。它可以看作一個語法糖,讓對象原型的寫法更加清晰、更像面向?qū)ο缶幊痰恼Z法。

4.2.1 基礎用法—類的定義

注意事項:

  • 類定義不會被提升,這意味著,必須在訪問前對類進行定義,否則就會報錯
  • 類中方法不需要 function 關鍵字
  • 方法間不能加分號
/*類定義——類表達式可以為匿名或命名*/
// 匿名類
let Example = class {
    constructor(a) {
        this.a = a;
    }
}
// 命名類
let Example = class Example {
    constructor(a) {
        this.a = a;
    }
}

/*類的聲明*/
class Example {
    constructor(a) {
        this.a = a;
    }
}
//類不可重復聲明,否則報錯
//報錯 Uncaught SyntaxError: Identifier 'Example' has already been declared
class Example{}
class Example{}
//報錯 Uncaught SyntaxError: Identifier 'Example' has already been declared
let Example1 = class{}
class Example{}

4.2.2 基礎用法—類的主體

4.2.2.1 屬性(prototype)

屬性:覆蓋方法 / 初始化時添加方法

  • 靜態(tài)屬性:class 本身的屬性,即直接定義在類內(nèi)部的屬性( Class.propname ),不需要實例化
  • 公共屬性
  • 實例屬性:定義在實例對象( this )上的屬性
  • name屬性:返回跟在 class 后的類名(存在時)
/*prototype 屬性*/
Example.prototype={
    //methods
}
//添加方法
Object.assign(Example.prototype,{
    //methods
})
//靜態(tài)屬性 ES6 中規(guī)定,Class 內(nèi)部只有靜態(tài)方法,沒有靜態(tài)屬性。
class Example {
// 新提案
    static a = 2;
}
Example.b = 2;// 目前可行寫法

//公共屬性
class Example{}
Example.prototype.a = 2;
//實例屬性
class Example {
    a = 2;
    constructor () {
        console.log(this.a);
    }
}
//name屬性
let Example=class Exam {
    constructor(a) {
        this.a = a;
    }
}
console.log(Example.name); // Exam 
let Example=class {
    constructor(a) {
        this.a = a;
    }
}
console.log(Example.name); // Example

4.2.2.3 方法

  • constructor 方法是類的默認方法,創(chuàng)建類的實例化對象時被調(diào)用?
  • 靜態(tài)方法
  • 原型方法
  • 實例方法
/*方法*/
//constructor 方法
class Example{
    constructor(){
      console.log('我是constructor');
    }
}
new Example(); // 我是constructor
//返回對象
class Test {
    constructor(){
        // 默認返回實例對象 this
    }
}
console.log(new Test() instanceof Test); // true
class Example {
    constructor(){
        return new Test();// 指定返回對象
    }
}
console.log(new Example() instanceof Example); // false
//靜態(tài)方法
class Example{
    static sum(a, b) {
        console.log(a+b);
    }
}
Example.sum(1, 2); // 3
//原型方法
class Example {
    sum(a, b) {
        console.log(a + b);
    }
}
let exam = new Example();
exam.sum(1, 2); // 3
//實例方法
class Example {
    constructor() {
        this.sum = (a, b) => {
            console.log(a + b);
        }
    }
}

4.2.3 基礎用法—類的實例化

//new class 的實例化必須通過 new 關鍵字
class Example {} 
let exam1 = Example(); // Class constructor Example cannot be invoked without 'new'
//實例化對象 共享原型對象
class Example {
    constructor(a, b) {
        this.a = a;
        this.b = b;
        console.log('Example');
    }
    sum() {
        return this.a + this.b;
    }
}
let exam1 = new Example(2, 1);
let exam2 = new Example(3, 1); 
// __proto__ 已廢棄,不建議使用
// console.log(exam1.__proto__ == exam2.__proto__);  
console.log(Object.getPrototypeOf(exam1) === Object.getPrototypeOf(exam2));// true 
Object.getPrototypeOf(exam1).sub = function() {
    return this.a - this.b;
}
console.log(exam1.sub()); // 1
console.log(exam2.sub()); // 2


4.2.4?decorator函數(shù)

4.2.4.1?類修飾

decorator 是一個函數(shù),用來修改類的行為,在代碼編譯時產(chǎn)生作用

//一個參數(shù) 第一個參數(shù) target,指向類本身
function testable(target) {
    target.isTestable = true;
}
@testable
class Example {}
Example.isTestable; // true
//多個參數(shù)——嵌套實現(xiàn)
function testable(isTestable) {
    return function(target) {
        target.isTestable=isTestable;
    }
}
@testable(true)
class Example {}
Example.isTestable; // true
//實例屬性
//上面兩個例子添加的是靜態(tài)屬性,若要添加實例屬性,在類的 prototype 上操作即可

4.2.4.2?方法修飾

//3個參數(shù):target(類的原型對象)、name(修飾的屬性名)、descriptor(該屬性的描述對象)
class Example {
    @writable
    sum(a, b) {
        return a + b;
    }
}
function writable(target, name, descriptor) {
    descriptor.writable = false;
    return descriptor; // 必須返回
}
//修飾器執(zhí)行順序 由外向內(nèi)進入,由內(nèi)向外執(zhí)行
class Example {
    @logMethod(1)
    @logMethod(2)
    sum(a, b){
        return a + b;
    }
}
function logMethod(id) {
    console.log('evaluated logMethod'+id);
    return (target, name, desctiptor) => console.log('excuted logMethod '+id);
}
// evaluated logMethod 1
// evaluated logMethod 2
// excuted logMethod 2
// excuted logMethod 1

4.2.5?封裝與繼承

封裝與繼承不可繼承常規(guī)對象

var Father = {
    // ...
}
class Child extends Father {
     // ...
}
// Uncaught TypeError: Class extends value #<Object> is not a constructor or null
 
// 解決方案
Object.setPrototypeOf(Child.prototype, Father);

4.2.5.1?getter / setter

//定義
class Example{
    constructor(a, b) {
        this.a = a; // 實例化時調(diào)用 set 方法
        this.b = b;
    }
    get a(){
        console.log('getter');
        return this.a;
    }
    set a(a){
        console.log('setter');
        this.a = a; // 自身遞歸調(diào)用
    }
}
let exam = new Example(1,2); // 不斷輸出 setter ,最終導致 RangeError
class Example1{
    constructor(a, b) {
        this.a = a;
        this.b = b;
    }
    get a(){
        console.log('getter');
        return this._a;
    }
    set a(a){
        console.log('setter');
        this._a = a;
    }
}
let exam1 = new Example1(1,2); // 只輸出 setter , 不會調(diào)用 getter 方法
console.log(exam1._a); // 1, 可以直接訪問

//getter 不可單獨出現(xiàn),否則報錯
class Example {
    constructor(a) {
        this.a = a; 
    }
    get a() {
        return this.a;
    }
}
let exam = new Example(1); // Uncaught TypeError: Cannot set property // a of #<Example> which has only a getter

//getter 與 setter 必須同級出現(xiàn)
class Father {
    constructor(){}
    get a() {
        return this._a;
    }
}
class Child extends Father {
    constructor(){
        super();
    }
    set a(a) {
        this._a = a;
    }
}
let test = new Child();
test.a = 2;
console.log(test.a); // undefined
class Father1 {
    constructor(){}
    // 或者都放在子類中
    get a() {
        return this._a;
    }
    set a(a) {
        this._a = a;
    }
}
class Child1 extends Father1 {
    constructor(){
        super();
    }
}
let test1 = new Child1();
test1.a = 2;
console.log(test1.a); // 2

4.2.5.2?extends

通過 extends 實現(xiàn)類的繼承

class Child extends Father { ... }

4.2.5.3 super

//子類 constructor 方法中必須有 super ,且必須出現(xiàn)在 this 之前
class Father {
    constructor() {}
}
class Child extends Father {
    constructor() {}
    // or 
    // constructor(a) {
        // this.a = a;
        // super();
    // }
}
let test = new Child(); // Uncaught ReferenceError: Must call super 
// constructor in derived class before accessing 'this' or returning from derived constructor

//調(diào)用父類構(gòu)造函數(shù),只能出現(xiàn)在子類的構(gòu)造函數(shù)
class Father {
    test(){
        return 0;
    }
    static test1(){
        return 1;
    }
}
class Child extends Father {
    constructor(){
        super();
    }
}
class Child1 extends Father {
    test2() {
        super(); // Uncaught SyntaxError: 'super' keyword unexpected     
        // here
    }
}
//調(diào)用父類方法, super 作為對象,在普通方法中,指向父類的原型對象,在靜態(tài)方法中,指向父類
class Child2 extends Father {
    constructor(){
        super();
        // 調(diào)用父類普通方法
        console.log(super.test()); // 0
    }
    static test3(){
        // 調(diào)用父類靜態(tài)方法
        return super.test1+2;
    }
}
Child2.test3(); // 3

4.3 模塊

在 ES6 前, 實現(xiàn)模塊化使用的是 RequireJS 或者 seaJS(分別是基于 AMD 規(guī)范的模塊化庫,? 和基于 CMD 規(guī)范的模塊化庫)。ES6 引入了模塊化,其設計思想是在編譯時就能確定模塊的依賴關系,以及輸入和輸出的變量。ES6 的模塊化分為導出(export) @與導入(import)兩個模塊。

特點:

  • ES6 的模塊自動開啟嚴格模式,不管你有沒有在模塊頭部加上?use strict;。
  • 模塊中可以導入和導出各種類型的變量,如函數(shù),對象,字符串,數(shù)字,布爾值,類等。
  • 每個模塊都有自己的上下文,每一個模塊內(nèi)聲明的變量都是局部變量,不會污染全局作用域。
  • 每一個模塊只加載一次(是單例的), 若再去加載同目錄下同文件,直接從內(nèi)存中讀取。

4.3.1 export與import

4.3.1.1 基本用法

模塊導入導出各種類型的變量,如字符串,數(shù)值,函數(shù),類

  • 導出的函數(shù)聲明與類聲明必須要有名稱(export default 命令另外考慮)。?
  • 不僅能導出聲明還能導出引用(例如函數(shù))。
  • export 命令可以出現(xiàn)在模塊的任何位置,但必需處于模塊頂層。
  • import 命令會提升到整個模塊的頭部,首先執(zhí)行
/*-----export [test.js]-----*/
let myName = "Tom";
let myAge = 20;
let myfn = function(){
    return "My name is" + myName + "! I'm '" + myAge + "years old."
}
let myClass =  class myClass {
    static a = "yeah!";
}
export { myName, myAge, myfn, myClass } 
/*-----import [xxx.js]-----*/
import { myName, myAge, myfn, myClass } from "./test.js";
console.log(myfn());// My name is Tom! I'm 20 years old.
console.log(myAge);// 20
console.log(myName);// Tom
console.log(myClass.a );// yeah!

建議使用大括號指定所要輸出的一組變量寫在文檔尾部,明確導出的接口。函數(shù)與類都需要有對應的名稱,導出文檔尾部也避免了無對應名稱。

4.3.1.2 as的用法

export 命令導出的接口名稱,須和模塊內(nèi)部的變量有一一對應關系。導入的變量名,須和導出的接口名稱相同,即順序可以不一致。不同模塊導出接口名稱命名重復, 使用 as 重新定義變量名。

/*-----export [test.js]-----*/
let myName = "Tom";
export { myName as exportName }
/*-----import [xxx.js]-----*/
import { exportName } from "./test.js";
console.log(exportName);// Tom
使用 as 重新定義導出的接口名稱,隱藏模塊內(nèi)部的變量
/*-----export [test1.js]-----*/
let myName = "Tom";
export { myName }
/*-----export [test2.js]-----*/
let myName = "Jerry";
export { myName }
/*-----import [xxx.js]-----*/
import { myName as name1 } from "./test1.js";
import { myName as name2 } from "./test2.js";
console.log(name1);// Tom
console.log(name2);// Jerry

4.3.1.3 import命令的特點

  • 只讀屬性:不允許在加載模塊的腳本里面,改寫接口的引用指向,即可以改寫 import 變量類型為對象的屬性值,不能改寫 import 變量類型為基本類型的值。?
  • 單例模式:多次重復執(zhí)行同一句 import 語句,那么只會執(zhí)行一次,而不會執(zhí)行多次。import 同一模塊,聲明不同接口引用,會聲明對應變量,但只執(zhí)行一次 import 。
  • 靜態(tài)執(zhí)行特性:import 是靜態(tài)執(zhí)行,所以不能使用表達式和變量。
//只讀屬性
import {a} from "./xxx.js"
a = {}; // error
import {a} from "./xxx.js"
a.foo = "hello"; // a = { foo : 'hello' }
//單例模式
import { a } "./xxx.js";
import { b } "./xxx.js";
// 相當于 import { a, b } from "./xxx.js";
import { a } from "./xxx.js";// 相當于 import { a } "./xxx.js"; 
import { b } from "./xxx.js";
//靜態(tài)執(zhí)行特性
import { "f" + "oo" } from "methods";
// error
let module = "methods";
import { foo } from module;
// error
if (true) {
  import { foo } from "method1";
} else {
  import { foo } from "method2";
}


4.3.1.4?export default 命令

  • 在一個文件或模塊中,export、import 可以有多個,export default 僅有一個。
  • export default 中的 default 是對應的導出接口變量。
  • 通過 export 方式導出,在導入時要加{ },export default 則不需要。
  • export default 向外暴露的成員,可以使用任意變量來接收。
var a = "My name is Tom!";
export default a; // 僅有一個
export default var c = "error"; 
// error,default 已經(jīng)是對應的導出變量,不能跟著變量聲明語句 
import b from "./xxx.js"; // 不需要加{}, 使用任意變量接收

4.3.2 復合使用

export 與 import 可以在同一模塊使用,使用特點:

  • 可以將導出接口改名,包括 default。
  • 復合使用 export 與 import ,也可以導出全部,當前模塊導出的接口會覆蓋繼承導出的。
export { foo, bar } from "methods";
// 約等于下面兩段語句,不過上面導入導出方式該模塊沒有導入 foo 與 bar
import { foo, bar } from "methods";
export { foo, bar };
/* ------- 特點 1 --------*/
// 普通改名
export { foo as bar } from "methods";
// 將 foo 轉(zhuǎn)導成 default
export { foo as default } from "methods";
// 將 default 轉(zhuǎn)導成 foo
export { default as foo } from "methods"; 
/* ------- 特點 2 --------*/
export * from "methods";

5.異步編程

5.1 Promise對象

Promise對象是異步編程的一種解決方案。從語法上說,Promise 是一個對象,從它可以獲取異步操作的消息。

5.1.1 Promise狀態(tài)

特點:

  • Promise 異步操作有三種狀態(tài):pending(進行中)、fulfilled(已成功)和 rejected(已失?。3水惒讲僮鞯慕Y(jié)果,任何其他操作都無法改變這個狀態(tài)。
  • Promise 對象只有:從 pending 變?yōu)?fulfilled 和從 pending 變?yōu)?rejected 的狀態(tài)改變。只要處于 fulfilled 和 rejected ,狀態(tài)就不會再變了即 resolved(已定型)
const p1 = new Promise(function(resolve,reject){
    resolve('success1');
    resolve('success2');
}); 
const p2 = new Promise(function(resolve,reject){  
    resolve('success3'); 
    reject('reject');
});
p1.then(function(value){  
    console.log(value); // success1
});
p2.then(function(value){ 
    console.log(value); // success3
});

缺點:

  • 無法取消 Promise ,一旦新建它就會立即執(zhí)行,無法中途取消。
  • 如果不設置回調(diào)函數(shù),Promise 內(nèi)部拋出的錯誤,不會反應到外部。
  • 當處于 pending 狀態(tài)時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)。

5.1.2 Then方法

then 方法接收兩個函數(shù)作為參數(shù),第一個參數(shù)是 Promise 執(zhí)行成功時的回調(diào),第二個參數(shù)是 Promise 執(zhí)行失敗時的回調(diào),兩個函數(shù)只會有一個被調(diào)用。

特點:

  • 在 JavaScript 事件隊列的當前運行完成之前,回調(diào)函數(shù)永遠不會被調(diào)用。
  • 通過?.then?形式添加的回調(diào)函數(shù),不論什么時候,都會被調(diào)用。
  • 通過多次調(diào)用.then可以添加多個回調(diào)函數(shù),它們會按照插入順序并且獨立運行。
  • then 方法將返回一個 resolved 或 rejected 狀態(tài)的 Promise 對象用于鏈式調(diào)用,且 Promise 對象的值就是這個返回值。
/*在 JavaScript 事件隊列的當前運行完成之前,回調(diào)函數(shù)永遠不會被調(diào)用。*/
const p = new Promise(function(resolve,reject){
  resolve('success');
});
 
p.then(function(value){
  console.log(value);
});
 
console.log('first');// first // success

/*
通過 .then 形式添加的回調(diào)函數(shù),不論什么時候,都會被調(diào)用。
通過多次調(diào)用.then可以添加多個回調(diào)函數(shù),它們會按照插入順序并且獨立運行。
then 方法將返回一個 resolved 或 rejected 狀態(tài)的 Promise 對象用于鏈式調(diào)用,且 Promise 對象的值就是這個返回值。
*/
const p = new Promise(function(resolve,reject){
  resolve(1);
}).then(function(value){ // 第一個then // 1
  console.log(value);
  return value * 2;
}).then(function(value){ // 第二個then // 2
  console.log(value);
}).then(function(value){ // 第三個then // undefined
  console.log(value);
  return Promise.resolve('resolve'); 
}).then(function(value){ // 第四個then // resolve
  console.log(value);
  return Promise.reject('reject'); 
}).then(function(value){ // 第五個then //reject:reject
  console.log('resolve:' + value);
}, function(err) {
  console.log('reject:' + err);
});

注意點:

  • 簡便的 Promise 鏈式編程最好保持扁平化,不要嵌套 Promise。
  • 注意總是返回或終止 Promise 鏈。
/*
創(chuàng)建新 Promise 但忘記返回它時,對應鏈條被打破,導致 p4 會與 p2 和 p3 同時進行。
大多數(shù)瀏覽器中不能終止的 Promise 鏈里的 rejection,建議后面都跟上 .catch(error => console.log(error));
*/
const p1 = new Promise(function(resolve,reject){
  resolve(1);
}).then(function(result) {
  p2(result).then(newResult => p3(newResult));
}).then(() => p4());

?5.2 Generator函數(shù)

ES6 新引入了 Generator 函數(shù),可以通過 yield 關鍵字,把函數(shù)的執(zhí)行流掛起,為改變執(zhí)行流程提供了可能,從而為異步編程提供解決方案。

5.2.1 基本用法

5.2.1.1 函數(shù)組成

Generator 有兩個區(qū)分于普通函數(shù)的部分:

  • 在 function 后面,函數(shù)名之前有個 *?
  • 函數(shù)內(nèi)部有 yield 表達式。

其中 * 用來表示函數(shù)為 Generator 函數(shù),yield 用來定義函數(shù)內(nèi)部的狀態(tài)。

function* func(){
 console.log("one");
 yield '1';
 console.log("two");
 yield '2'; 
 console.log("three");
 return '3';
}

5.2.2.2 執(zhí)行機制

調(diào)用 Generator 函數(shù)和調(diào)用普通函數(shù)一樣,在函數(shù)名后面加上()即可,但是 Generator 函數(shù)不會像普通函數(shù)一樣立即執(zhí)行,而是返回一個指向內(nèi)部狀態(tài)對象的指針,所以要調(diào)用遍歷器對象Iterator 的 next 方法,指針就會從函數(shù)頭部或者上一次停下來的地方開始執(zhí)行。?

/*
第一次調(diào)用 next 方法時,從 Generator 函數(shù)的頭部開始執(zhí)行,先是打印了 one ,執(zhí)行到 yield 就停下來,并將yield 后邊表達式的值 '1',作為返回對象的 value 屬性值,此時函數(shù)還沒有執(zhí)行完, 返回對象的 done 屬性值是 false。
第二次調(diào)用 next 方法時,同上步 。
第三次調(diào)用 next 方法時,先是打印了 three ,然后執(zhí)行了函數(shù)的返回操作,并將 return 后面的表達式的值,作為返回對象的 value 屬性值,此時函數(shù)已經(jīng)結(jié)束,多以 done 屬性值為true 。
第四次調(diào)用 next 方法時, 此時函數(shù)已經(jīng)執(zhí)行完了,所以返回 value 屬性值是 undefined ,done 屬性值是 true 。如果執(zhí)行第三步時,沒有 return 語句的話,就直接返回 {value: undefined, done: true}。
*/
f.next();
// one
// {value: "1", done: false} 
f.next();
// two
// {value: "2", done: false} 
f.next();
// three
// {value: "3", done: true} 
f.next();
// {value: undefined, done: true}

5.2.2.3?函數(shù)返回的遍歷器對象的方法

next 方法

一般情況下,next 方法不傳入?yún)?shù)的時候,yield 表達式的返回值是 undefined 。當 next 傳入?yún)?shù)的時候,該參數(shù)會作為上一步y(tǒng)ield的返回值。

function* sendParameter(){
    console.log("start");
    var x = yield '2';
    console.log("one:" + x);
    var y = yield '3';
    console.log("two:" + y);
    console.log("total:" + (x + y));
}
//next不傳參
var sendp1 = sendParameter();
sendp1.next();// start {value: "2", done: false}
sendp1.next();// one:undefined {value: "3", done: false} 
sendp1.next();// two:undefined total:NaN {value: undefined, done: true}

//next傳參
var sendp2 = sendParameter();
sendp2.next(10);// start {value: "2", done: false}
sendp2.next(20);// one:20 {value: "3", done: false}
sendp2.next(30);// two:30  total:50 {value: undefined, done: true}

//除了使用 next ,還可以使用 for... of 循環(huán)遍歷 Generator 函數(shù)生產(chǎn)的 Iterator 對象。

?return 方法

  • return 方法返回給定值,并結(jié)束遍歷?Generator 函數(shù)。
  • return 方法提供參數(shù)時,返回該參數(shù);不提供參數(shù)時,返回 undefined 。
function* foo(){
    yield 1;
    yield 2;
    yield 3;
}
var f = foo();
f.next();// {value: 1, done: false}
f.return("foo");// {value: "foo", done: true}
f.next();// {value: undefined, done: true}

/*
throw 方法
throw 方法可以再 Generator 函數(shù)體外面拋出異常,再函數(shù)體內(nèi)部捕獲。
*/
var g = function* () {
  try {
    yield;
  } catch (e) {
    console.log('catch inner', e);
  }
}; 
//遍歷器對象拋出了兩個錯誤,第一個被 Generator 函數(shù)內(nèi)部捕獲,第二個因為函數(shù)體內(nèi)部的catch 函數(shù)已經(jīng)執(zhí)行過了,不會再捕獲這個錯誤,所以這個錯誤就拋出 Generator 函數(shù)體,被函數(shù)體外的 catch 捕獲。
var i = g();
i.next(); 
try {
  i.throw('a');
  i.throw('b');
} catch (e) {
  console.log('catch outside', e);
}
// catch inner a
// catch outside b

yield*?表達式

yield* 表達式表示 yield 返回一個遍歷器對象,用于在 Generator 函數(shù)內(nèi)部,調(diào)用另一個 Generator 函數(shù)。

function* callee() {
    console.log('callee: ' + (yield));
}
function* caller() {
    while (true) {
        yield* callee();
    }
}
const callerObj = caller();
callerObj.next();// {value: undefined, done: false}
callerObj.next("a");// callee: a  {value: undefined, done: false}
callerObj.next("b");// callee: b {value: undefined, done: false} 
// 等同于
function* caller() {
    while (true) {
        for (var value of callee) {
          yield value;
        }
    }
}

5.2.2 使用場景

實現(xiàn) Iterator:為不具備 Iterator?接口的對象提供遍歷方法。

/*
Reflect.ownKeys() 返回對象所有的屬性,不管屬性是否可枚舉,包括 Symbol。
jane 原生是不具備 Iterator 接口無法通過 for... of遍歷。這邊用了 Generator 函數(shù)加上了 Iterator 接口,所以就可以遍歷 jane 對象了。
*/
function* objectEntries(obj) {
    const propKeys = Reflect.ownKeys(obj);
    for (const propKey of propKeys) {
        yield [propKey, obj[propKey]];
    }
}
 
const jane = { first: 'Jane', last: 'Doe' };
for (const [key,value] of objectEntries(jane)) {
    console.log(`${key}: ${value}`);
}
// first: Jane
// last: Doe

5.3 async函數(shù)

async 是 ES7 才有的與異步操作有關的關鍵字,和 Promise , Generator 有很大關聯(lián)的。

5.3.1 async

語法:async function name([param[, param[, ... param]]]) { statements }

參數(shù):

  • name: 函數(shù)名稱。
  • param: 要傳遞給函數(shù)的參數(shù)的名稱。
  • statements: 函數(shù)體語句。

返回值:async 函數(shù)返回一個 Promise 對象,可以使用 then 方法添加回調(diào)函數(shù)。

async function helloAsync(){
    return "helloAsync";
  }  
console.log(helloAsync())  // Promise {<resolved>: "helloAsync"} 
helloAsync().then(v=>{
   console.log(v);         // helloAsync
})
/*
async 函數(shù)中可能會有 await 表達式,async 函數(shù)執(zhí)行時,如果遇到 await 就會先暫停執(zhí)行 ,等到觸發(fā)的異步操作完成后,恢復 async 函數(shù)的執(zhí)行并返回解析值。
await 關鍵字僅在 async function 中有效。如果在 async function 函數(shù)體外使用 await ,你只會得到一個語法錯誤。
*/
function testAwait(){
   return new Promise((resolve) => {
       setTimeout(function(){
          console.log("testAwait");
          resolve();
       }, 1000);
   });
}
 
async function helloAsync(){
   await testAwait();
   console.log("helloAsync");
 }
helloAsync();
// testAwait
// helloAsync

5.3.2 await

await 操作符用于等待一個 Promise 對象, 它只能在異步函數(shù) async function 內(nèi)部使用。

語法:[return_value] = await expression;

參數(shù):expression: 一個 Promise 對象或者任何要等待的值。

返回值:返回 Promise 對象的處理結(jié)果。如果等待的不是 Promise 對象,則返回該值本身。如果一個 Promise 被傳遞給一個 await 操作符,await 將等待 Promise 正常處理完成并返回其處理結(jié)果。

await針對所跟不同表達式的處理方式:

  • Promise 對象:await 會暫停執(zhí)行,等待 Promise 對象 resolve,然后恢復 async 函數(shù)的執(zhí)行并返回解析值。
  • 非 Promise 對象:直接返回對應的值。
function testAwait (x) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(x);
    }, 2000);
  });
} 
async function helloAsync() {
  var x = await testAwait ("hello world");
  console.log(x); 
}
helloAsync ();// hello world

//正常情況下,await 命令后面是一個 Promise 對象,它也可以跟其他值,如字符串,布爾值,數(shù)值以及普通函數(shù)。
function testAwait(){
   console.log("testAwait");
}
async function helloAsync(){
   await testAwait();
   console.log("helloAsync");
}
helloAsync();
// testAwait
// helloAsync

?

?

?

到了這里,關于ES6基礎教程的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權(quán),不承擔相關法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • 前端框架學習-ES6新特性(尚硅谷web筆記)

    ECMASript 是由 Ecma 國際通過 ECMA-262 標準化的腳本程序設計語言。javaScript也是該規(guī)范的一種實現(xiàn)。 筆記出處:b站 尚硅谷Web前端ES6教程,涵蓋ES6-ES11 阮一峰大佬的:ECMAScript 6 入門 ES6 let 使用let聲明變量的特點: 不允許重復聲 塊兒級別作用域 不存在變量提升 不影

    2024年02月12日
    瀏覽(45)
  • 前端架構(gòu)師之01_ES6_基礎

    簡單來說,ECMAScript是JavaScript語言的國際標準,JavaScript是實現(xiàn)ECMAScript標準的腳本語言。 2011年,ECMA國際標準化組織在發(fā)布ECMAScript 5.1版本之后,就開始著手制定第6版規(guī)范。 存在的問題:這個版本引入的語法功能太多,而且制定過程當中,還有很多組織和個人不斷提交新功能

    2024年02月07日
    瀏覽(47)
  • 【web前端學習】7個ES6解構(gòu)技巧讓代碼更簡潔

    【web前端學習】7個ES6解構(gòu)技巧讓代碼更簡潔

    各位前端er,代碼打久了,是否厭倦了編寫臃腫且難以閱讀的代碼?想要提升編碼技能并使代碼更具可讀性和簡潔性? 今天這篇文章,與小千一起深入學習 ES6 解構(gòu)的世界,向您展示如何使用它來編寫更干凈、更高效的代碼 本文將從 解構(gòu)對象和數(shù)組到使用默認值和展開運算符

    2024年02月05日
    瀏覽(21)
  • ES6(ECMAScript 2015)有哪些新屬性,如何判斷當前瀏覽器是否支持?

    ES6(ECMAScript 2015)有哪些新屬性,如何判斷當前瀏覽器是否支持?

    ES6(ECMAScript 2015)引入了許多新的語法和特性,以增強 JavaScript 編程語言的功能。以下是一些常見的 ES6 語法和特性以及它們的解釋: let 和 const 聲明 : let 和 const 用于聲明變量,代替了舊的 var 。 let 聲明的變量具有塊級作用域,而 const 聲明的變量是不可變的(常量

    2024年02月07日
    瀏覽(27)
  • 前端-ES6

    let 和 const 為了解決var的作用域的問題,而且var 有變量提升,會出現(xiàn)全局污染的問題 let 塊狀作用域,并且不能重復聲明 const 一般用于聲明常量,一旦被聲明無法修改,但是const 可以聲明一個對象,對象內(nèi)部的屬性是可以修改的 建議: 在默認的情況下用const, 而只有你知道

    2024年02月12日
    瀏覽(17)
  • ES6基礎知識二:ES6中數(shù)組新增了哪些擴展?

    ES6基礎知識二:ES6中數(shù)組新增了哪些擴展?

    一、擴展運算符的應用 ES6通過擴展元素符…,好比 rest 參數(shù)的逆運算,將一個數(shù)組轉(zhuǎn)為用逗號分隔的參數(shù)序列 主要用于函數(shù)調(diào)用的時候,將一個數(shù)組變?yōu)閰?shù)序列 可以將某些數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)為數(shù)組 能夠更簡單實現(xiàn)數(shù)組復制 const a1 = [1, 2]; const […a2] = a1; // [1,2] 數(shù)組的合并也更為

    2024年02月16日
    瀏覽(26)
  • 前端面試問題匯總 - ES6篇

    let?和?const?聲明 :?引入塊級作用域的變量聲明方式,let?聲明的變量可重新賦值,const?聲明的變量不可重新賦值。 箭頭函數(shù) (Arrow?Functions):?提供了更簡潔的函數(shù)定義語法,并且沒有自己的?this、arguments、super?或?new.target,這些值繼承自執(zhí)行上下文。 模板字符串 (

    2024年04月16日
    瀏覽(23)
  • 前端小白的學習之路(ES6 一)

    提示:聲明:let與const,長度單位:em與rem,vw與wh,解構(gòu)賦值,箭頭函數(shù)(簡介) 目錄 一、ES6介紹 二、letconst 1.let 1) 用 let 聲明的變量不能提前引用 2) 不允許重復聲明變量 3) 可以產(chǎn)生塊級作用域 { } ? 總結(jié) 2.const 三、?長度單位 1.em與rem 1).em 2).rem 2.vw與vh 四、?解構(gòu)賦

    2024年04月15日
    瀏覽(26)
  • ES6 基礎復習

    ES6 基礎復習

    變量可能具有的特性 塊級作用域,變量提升(變量聲明之前使用該變量),全局屬性,重復聲明,暫時性死區(qū)(變量聲明之前的區(qū)域無法使用該變量),初始值設置,指針指向(重復賦值) 塊級作用域,不存在變量提升,不能重復聲明,不能重復賦值 塊級作用域,不存在變

    2024年02月02日
    瀏覽(17)
  • ES6基礎語法

    ES6基礎語法

    目錄 解構(gòu) 數(shù)組解構(gòu) 對象解構(gòu) ?基本數(shù)據(jù)解構(gòu) 對象 對象簡寫 箭頭函數(shù) 擴展運算符 函數(shù)參數(shù)解構(gòu) ?對象API拓展 Object.is() Object.assign() Object.getPrototypeOf() Object.setPrototypeOf() Object.keys() Object.values() Object.entries() Object.fromEntries() ?數(shù)組方法拓展 String類型方法 String.prototype.trimStart()/

    2024年02月16日
    瀏覽(17)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包