一、ES6 基本認識 1、什麼是 ES6? ES6 指的是 ECMAScript 6.0,是JavaScript 語言的一個標準。其目標是使JavaScript 可以用來編寫複雜的大型的應用程式,成為企業級開發的語言。 2、ES6 與 JavaScript 的區別? ES6 是 JavaScript ...
一、ES6 基本認識
1、什麼是 ES6?
ES6 指的是 ECMAScript 6.0,是JavaScript 語言的一個標準。其目標是使JavaScript 可以用來編寫複雜的大型的應用程式,成為企業級開發的語言。
2、ES6 與 JavaScript 的區別?
ES6 是 JavaScript 的一個標準,JavaScript 是 ES6 的具體實現。
3、Babel 轉碼器?
Babel 是一個被廣泛使用的 ES6 轉碼器,其可以將 ES6 代碼轉為 ES5 代碼,從而在現有環境下執行,即使用 ES6 編寫代碼而無需擔心不能運行。
簡單的講就是 一些 瀏覽器 不支持 ES6 部分語法,可以使用 Babel 轉碼器將 ES6 語法 轉為 ES5 語法,從而被 瀏覽器 識別。
比如:
ES6 可以使用 箭頭函數來 替代 普通函數,通過 Babel 轉碼器,可以將 箭頭函數 轉為 普通函數。這樣就不需要去擔心瀏覽器是否支持這種語法。
【ES6】 input.map(item => item + 1); 【ES5】 input.map(function (item) { return item + 1; });
二、常用特性
1、let 命令
(1)基本內容
let 命令通常用來聲明局部變數。
特性:局部有效、不存在變數提升、暫時性死區、不允許重覆聲明。
(2)特性一:局部有效。
let 命令類似於 var,用來聲明變數,但是 var 是全局有效,let 只在其所在的代碼塊內生效,出了代碼塊就獲取不到該值。
如下例:(看的可能有點繞,多瞅兩遍)
var 定義的是全局變數,對於 for 迴圈來說,整個迴圈都只對一個 變數進行操作。看下麵例子的第一個迴圈,在迴圈體內操作 i 會對迴圈有影響。由於進行了兩次 i++,所以數組有部分值為 空,且只進行了部分迴圈。
let 定義的是局部變數,對於 for 迴圈來說,每次迴圈都是不同的作用域,且 let 只對當前作用域有效。更有趣的是,迴圈語句內部是一個子作用域(即 在內部定義一個同名的 let 變數,不會影響外部的 let 變數)。看下麵例子的第二個迴圈,每次迴圈操作,j 都是不同的值,且 迴圈內部 定義了 同名的 let 變數 j ,由於作用域的問題,其並不會影響迴圈語句中的 j,所以執行了全部迴圈。
【舉例:】 var a = []; // 用來記錄每次迴圈需要列印的初始值 var b = []; // 用來記錄每次迴圈的初始值 var count = 0; // 用來記錄迴圈的次數 for(var i = 0; i < 10; i++, count++) { a[i] = function() { console.log("當前迴圈的值 i 為: " + i); }; b[i] = i++; } console.log("當前迴圈執行次數為: " + count); // 由於 i 為全局變數,每次迴圈都會進行兩次 i++,所以真實迴圈次數小於 10,所以輸出為 5 a[6](); // 由於操作的都是同一變數,所以函數調用的是 最後一次修改的 i 值,所以輸出為 10 console.log("每次迴圈的初始值 i 為: " + b); // 用於只進行了部分迴圈,所有數組有些並沒有賦值,即為空值。所以輸出為 0,,2,,4,,6,,8 console.log("迴圈執行後的 i 值: " + i); // i = 10 時退出迴圈, i 為全局變數,所以輸出 為 10 var c = []; // 用來記錄每次迴圈需要列印的初始值 var d = []; // 用來記錄每次迴圈的初始值 var count = 0; // 用來記錄迴圈的次數 for(let j = 0; j < 10; j++, count++) { let j = 5; c[j] = function() { console.log("當前迴圈的值 j 為: " + j); }; d[j] = j++; } console.log("當前迴圈執行次數為: " + count); // 由於 j 為 局部變數,迴圈內部定義的 let 同名變數 j (子作用域)不會影響 迴圈語句的 j,真實迴圈執行 10 次,所以輸出為 10 c[5](); // 每次操作都是不同的變數,且執行了 j++ 操作,所以輸出為 6 console.log("每次迴圈的初始值 j 為: " + d); // 由於內部每次都給 d[5] 賦值,其餘元素均為空值,所以輸出為 ,,,,,5 console.log("迴圈執行後的 j 值: " + j); // 由於 j 為局部變數,只能存在於 for 迴圈代碼塊中, 所以此處會報錯,輸出 ReferenceError: j is not defined
(3)特性二:不存在變數提升
變數提升指的是 變數可以在聲明前使用。
let 不存在變數提升,即聲明變數後,才可以使用該變數,不能在聲明前使用,否則會報錯。
【舉例:】 console.log(a); // 不報錯,輸出 undefined console.log(b); // 報錯,輸出 ReferenceError: b is not defined var a = 10; let b = 20;
(4)特性三:暫時性死區
暫時性死區指的是 剛開始進入當前作用域,所要使用的變數就已經存在了,但是不可獲取,當變數被聲明後,才可以獲取該變數。
【舉例:】 var tmp = 123; console.log(tmp); //不報錯,輸出 123 if (true) { console.log(tmp); // 報錯,ReferenceError: Cannot access 'tmp' before initialization let tmp; } 第一次定義 tmp 為 var 型,所以可以正常輸出 123, 進入 if 語句後,由於存在 let 定義的 tmp,系統判定 tmp 為局部變數而非全局變數。 導致 console.log(tmp) 中 tmp 出現在 變數聲明前(變數提升失敗), 從而報錯,此處即為暫時性死區。
(5)特性四:不重覆聲明
在同一塊 let 作用域中,若使用 let 聲明一個變數,則不能再重覆聲明同一個變數。
【舉例:】 // 報錯,Identifier 'a' has already been declared { let a = 1; var a = 2; } // 不報錯,undefined { var a = 1; var a = 2; } // 報錯,Identifier 'a' has already been declared { let a = 1; let a = 2; }
2、const 命令
(1)基本內容
const 通常用來聲明一個只讀的常量,一旦聲明,常量的值不能被修改,且聲明時必須初始化。
用法類似於 let,局部有效、不存在變數提升、不重覆聲明。
【舉例:(常量值不可被修改)】 const PI = 3.1415926 console.log(PI); // 輸出 3.1415926 PI = 3.14 // 報錯,輸出 Assignment to constant variable. 【舉例:(常量聲明時需要初始化)】 const a // 報錯,輸出 Missing initializer in const declaration 【舉例:局部有效】 { const PI = 3.1415926; } console.log(PI); // 報錯,輸出 PI is not defined 【舉例:不存在變數提升】 { console.log(PI); // 報錯,輸出 Cannot access 'PI' before initialization const PI = 3.1415926; } 【舉例:不重覆聲明】 { var PI = 3.14 const PI = 3.1415926; // 報錯,輸出 SyntaxError: Identifier 'PI' has already been declared }
(2)若 const 聲明的是對象,那麼 其不變的是 指向對象的地址,對象的值仍可以改變。可以通過object.freeze() 方法凍結對象(即對象不可修改)。
【舉例:var,對象可被修改】 { var f = {name : 'tom', age : '12'}; console.log(f.name + ", " + f.age); // tom, 12 f.name = 'jarry'; f.age = 44; console.log(f.name + ", " + f.age); // jarry, 44 f = {name : 'rick', age : '22'}; console.log(f.name + ", " + f.age); // rick, 22 } 【舉例:const,對象內容可被修改,但是對象不可被修改】 { const f = {name : 'tom', age : '12'}; console.log(f.name + ", " + f.age); // tom, 12 f.name = 'jarry'; f.age = 44; console.log(f.name + ", " + f.age); // jarry, 44 f = {name : 'rick', age : '22'}; // TypeError: Assignment to constant variable. } 【舉例:freeze,對象不可被修改,對象內容不可被修改】 { const f = Object.freeze({name : 'tom', age : '12'}); console.log(f.name + ", " + f.age); // tom, 12 f.name = 'jarry'; f.age = 44; console.log(f.name + ", " + f.age); // tom, 12 f = {name : 'rick', age : '22'}; // TypeError: Assignment to constant variable. }
3、解構表達式
(1)什麼是解構?
解構指的是 ES6 支持按照一定的模式,從數組或者對象中提取值,並將提取的值 對變數進行賦值。
(2)數組的解構賦值
一般情況下,只要 = 左右兩側 的模式相同,左邊的變數 就會賦值 上 右邊對應的值。
【未使用解構表達式給賦值:】 let a = 10; let b = 20; let c = 30; console.log(a, b, c); 【使用 解構表達式賦值:】 let [a, b, c] = [100, 200, 300]; console.log(a, b, c);
解構不成功時,對應的數據為 undefined。
允許解構賦值指定預設值,預設值可以為一個函數(惰性,用到時才調用)。
【嵌套數組賦值:】 let [a, [b, [c, d]]] = [1, [2, [3, 4]]]; console.log(a, b, c, d); // 輸出 1 2 3 4 let [head, ...tail] = [1, 2, 3, 4]; console.log(head); // 輸出 1 console.log(tail); // 輸出 (3) [2, 3, 4] let [x, y, ...z] = [1]; console.log(x); // 輸出 1 console.log(y); // 輸出 undefined console.log(z); // 輸出 [] 【部分解構:(給匹配上的變數賦值)】 let [x, y, z] = [1, 2]; console.log(x); // 輸出 1 console.log(y); // 輸出 2 console.log(z); // 輸出 undefined let [a, [b], c] = [1, [2, 3], 4]; console.log(a); // 輸出 1 console.log(b); // 輸出 2 console.log(c); // 輸出 4 【解構時使用預設值:(即若賦值失敗,可以使用預設值)】 function hello() { return "hello"; } let [x=hello(), y=hello(), z=100] = [1, 2, 3]; console.log(x); // 輸出 1 console.log(y); // 輸出 2 console.log(z); // 輸出 3 let [x2=hello(), y2=hello(), z2=100] = [, 2, ]; console.log(x2); // 輸出 hello console.log(y2); // 輸出 2 console.log(z2); // 輸出 100
(3)對象的解構賦值
對象同樣可以進行解構。與數組解構不同的是,對象解構時根據屬性名進行匹配,不需要註意順序。
屬性名不匹配時,值為 undefined。
可以自定義屬性名,使用 : 去指定。
如下例:
let {name, age} 等價於 let {name: name, age: age}
【根據屬性名匹配:】 let {name, age} = {name: "tom", age: 22}; console.log(name); // 輸出 tom console.log(age); // 輸出 22 【屬性名匹配不成功,返回 undefined:】 let {name2, age2} = {name: "tom", age: 22}; console.log(name2); // 輸出 undefined console.log(age2); // 輸出 undefined 【自定義屬性名匹配:】 let {name: name3, age: age3} = {name: "tom", age: 22}; console.log(name3); // 輸出 tom console.log(age3); // 輸出 22
4、字元串拓展
即加強了字元串處理功能。
(1)字元的 unicode 表示
JavaScript 允許使用使用 \uxxxx 的形式表示一個字元,其中 xxxx 表示 unicode 值。但是這種寫法只支持 \u0000 ~ \uffff,超出這個限制需要使用 雙位元組 進行表示。
比如:
\u1F680 會解析成 \u1F68 和 0。若想正常顯示,需使用雙位元組 \uD83D\uDE80 表示。
【舉例:】 console.log("\u0061"); // 輸出 a console.log("\u00614"); // 輸出 a4
ES6 可以使用 大括弧將 xxxxx 括起來,從而正確解讀。
【舉例:】 console.log("\u{0061}"); console.log("\u{1F680}"); console.log("\uD83D\uDE80");
(2)新增方法 -- includes()、startsWith()、endsWith()
JavaScript 中通過 indexOf() 可以確定某個字元串中是否包含另外一個字元串。
ES6 新增三個方法用於判斷字元串中是否包含另一個字元串。
includes() 返回布爾值,true 表示當前字元串中存在另一個字元串,false 表示不存在。
startsWith() 返回布爾值,true 表示當前字元串的頭部存在另一個字元串,false 表示不存在。
endsWith() 返回布爾值,true 表示當前字元串的尾部存在另一個字元串,false 表示不存在。
【舉例:】 let test = "hello world"; console.log(test.includes("wo")); // 輸出 true console.log(test.startsWith("he")); // 輸出 true console.log(test.endsWith("ld")); // 輸出 true console.log(test.includes("helloworld")); // 輸出 false
(3)模板字元串(``)
模板字元串是增強版的字元串,使用 反引號(``) 標識字元串,可以作為普通字元串使用,可以定義多行字元串,內部使用 ${} 可以嵌入變數、函數、表達式等並解析。
【舉例:】 let [name, age] = ["tom", 32]; function fun() { return "helloworld"; } let test2 = `${name}, age = ${age - 10}, say ${fun()}`; console.log(test2);
5、對象的拓展
拓展對象的用法。
(1)屬性簡寫
ES6 允許在 對象中 直接寫變數,此時 屬性為 變數名,屬性值為 變數值。即 {a} 等價於 {a: a}
【舉例:】 let a = "hello"; let b = {a}; console.log(b); // 輸出 {a: "hello"} let c = {a: a}; console.log(c); // 輸出 {a: "hello"} let d = {g: "hello"}; console.log(d); // 輸出 {g: "hello"}
對象中的方法也可以簡寫。
【舉例:】 let [name, age] = ["tom", 32]; let person = { name, age, hello() { console.log(`${name}, ${age}`); }, hello2: function() { console.log(`${name}, ${age}`); } }; person.hello(); person.hello2();
(2)新增方法 -- assign()
Object.assign() 方法用於對象的合併。
其實現的是淺拷貝,即若 源對象中的某個屬性值 仍是一個對象,那麼目標對象 中拷貝得到的是這個對象的引用,即對源對象中這個對象進行修改,會影響到目標對象。
【格式:】 Object.assign(target, source1, ...source2); 註: target 為目標對象,source1, ...source2 等都是源對象。 該方法是將 源對象 的值 複製 到 目標對象 中。 若出現同名屬性,則後者會覆蓋前者。 即 target、source1、source2 中存在同名屬性,則最後 target 的那個同名屬性為 source2 的屬性。 【舉例:】 let tom = { name: "tom", age: 32, teacher: { chinese: "rose", english: "jack" } }; let jarry = { name: "jarry", age: 33, email: "[email protected]" }; let people = Object.assign({}, tom, jarry); console.log(people); tom.teacher.chinese = "rick"; console.log(people);
如何實現深拷貝嘞:
有一種解決辦法:(具體原因沒有仔細深究)
先將對象轉為 json 字元串,再將 字元串轉為對象。
將上例 let people = Object.assign({}, tom, jarry); 改為 let people = Object.assign({}, JSON.parse(JSON.stringify(tom)), jarry);
(3)新增對象遍歷方法 -- keys()、values()、entries()
Object.keys() 獲取對象 key 形成的數組。
Object.values() 獲取對象 value 形成的數組。
Object.entries() 獲取對象 key - value 形成的 二維數組。
【舉例:】 let people = { name: "tom", age: 22 }; console.log(Object.keys(people)); console.log(Object.values(people)); console.log(Object.entries(people));
(4)擴展運算符(...)
用於取出對象、數組的參數並拷貝到當前對象中。
若數據重覆,會覆蓋,等同於 Object.assign()。
【舉例:】 let people = { name: "tom", age: 22 }; let people2 = { name: "jarry", age: 33 }; console.log({people, people2}); console.log({...people, ...people2}); let a = [3, 1, 2]; console.log(a); console.log(...a);
6、函數的拓展
(1)函數參數預設值
可以在定義函數的同時,指定函數的預設值,調用時,若未傳遞參數,則使用預設值。
【舉例:】 function test (x, y) { y = y || "hello"; console.log(x + "======" + y); } test("tom"); // 輸出 tom======hello test("tom", "helloworld"); // 輸出 tom======helloworld function test2 (x, y = "hello") { console.log(x + "======" + y); } test2("tom"); // 輸出 tom======hello test2("tom", "helloworld"); // 輸出 tom======helloworld
(2)rest 參數
rest 參數 ,形式為 ...變數名, 用於接收多個參數,保存在數組中。
若有多個參數,則 rest 必須放在最後,否則會報錯。
【舉例:】 function test (...values) { // for of 每次獲取的是數組的值 for (let j of values) { console.log(j); } } test(8, 7, 9); function test2 (...values) { // for in 每次獲取的是數組的下標 for (let j in values) { console.log(values[j]); } } test2(8, 7, 9); function test3 (...values, y) { // 報錯,SyntaxError: Rest parameter must be last formal parameter for (let j in values) { console.log(values[j]); } }
(3)箭頭函數
ES6 支持 使用 箭頭 => 定義函數。
【使用箭頭函數定義 函數:】 var f = v => v; console.log(f(3)); // 3 // 等價於 var f = function(v){ return v; } console.log(f(3)); // 3
如果沒有參數、或者有多個參數,需使用圓括弧 () 代替參數部分。
如果方法體(代碼塊)只有一條語句,則 return 可以省略。
【使用 () 代替參數:】 var f = () => 5; // 等價於 var f = function () { return 5 }; var sum = (num1, num2) => num1 + num2; // 等價於 var sum = function(num1, num2) { return num1 + num2; };
如果方法體(代碼塊)存在多條語句,則需要使用大括弧 {} 括起來,並使用 return 返回值。
【舉例:】 var fun = () => { let num = 7; let num2 = num + 3; return num + num2; }; console.log(fun()); // 17 // 等價於 var fun = function() { let num = 7; let num2 = num + 3; return num + num2; }; console.log(fun()); // 17
若返回的是一個對象,則必須在對象外面加上圓括弧 (),否則 {} 會被當成代碼塊被解析。
【舉例:】 var getPeopleItem = id => ({ id: id, name: "Temp" }); console.log(getPeopleItem(3)); // {id: 3, name: "Temp"} var getPeopleItem = id => { id: id, name: "Temp" }; console.log(getPeopleItem(3)); // SyntaxError: Unexpected token :
解構與箭頭函數可以一起使用:
【舉例:】 let people = { name: "tom", age: 22 }; let fun = (param) => { console.log(param.name + "==========" + param.age); }; fun(people); let fun2 = ({name, age}) => { console.log(name + "==========" + age); }; fun2(people);
7、數組常用方法
參考:https://www.cnblogs.com/l-y-h/p/12150578.html
(1)新增方法 -- reduce()
Array.reduce(callback[, initialValue]) 用於給數組的每一個元素執行一個回調函數。
其中 :
initialValue 為第一次執行 callback 時的參數值,可以省略。
callback 有四個參數,callback(previousValue, currentValue, index, array).
previousValue 指上一次執行回調的函數值,或者初始值。
currentValue 指數組當前被處理的元素
index 指當前數組元素的下標
array 指當前的數組
【舉例:】 let arr = [4, 6, 5]; let newArr = arr.reduce((previousValue, currentValue, index, array) => { console.log("上一次處理的值為: " + previousValue); console.log("當前處理的值為: " + currentValue); console.log("當前元素下標為: " + index); console.log("當前數組元素為: " + array[index]); return currentValue * 2; }); console.log(newArr);
8、Promise 對象
(1)什麼是 Promise ?
Promise 是一個非同步編程的一種解決方案。可以理解為一個容器,裡面保存著未來才會結束的某個操作(非同步操作)的結果,通過 Promise 對象可以獲取非同步操作的消息。
(2)Promise 特點
特點一:對象的狀態不受外界影響。
Promise 有三種狀態,Pending (進行中)、Resolved (解決)、Rejected (失敗)。只有非同步操作的結果能決定 Promise 處於哪種狀態,其餘操作無法改變該狀態,無法中途取消操作。
特點二:狀態改變後,不再變化。
狀態改變的情況,Pending -> Resolved 、 Pending -> Rejected。
一旦狀態改變,其不會再改變。
(3)Promise 缺點
無法取消Promise,一旦新建便會執行,無法中途取消。
如果不設置回調函數,Promise內部的錯誤不會向外拋出。
處於Pending時,無法判斷該操作是剛開始還是即將完成。
(4)如何使用?
需要使用 new 去實例化一個 Promise 對象,參數為 一個函數。
函數的參數為 resolve、reject ,參數為兩個函數,由 JavaScript 引擎提供。
resolve 函數是改變狀態, Pending -> Resolved ,即非同步操作成功後調用,並將非同步操作的成功結果作為參數向外傳遞。
reject 函數也是改變狀態, Pending -> Rejected,即非同步操作失敗後調用,並將非同步操作的失敗結果作為參數向外傳遞。
使用 then 方法可以處理 resolve、reject 傳遞出來的結果。其接受兩個回調函數作為參數。第一個回調函數用來處理 resolve 傳遞的結果,第二個回調函數用來處理 reject 傳遞的結果。第二個回調函數可選。
一般情況下,使用 catch 方法處理 reject 傳遞出來的結果。其作用等價於 then 方法中的第二個回調函數。
【格式:】 var promise = new Promise((resolve, reject) => { if(非同步操作成功) { resolve(data); } else { reject(error); } }); promise.then((data) => { // 成功的操作 }).catch((error) => { // 失敗的操作 });
9、模塊化
(1)什麼是模塊化?
模塊化就是把代碼進行拆分,方便重覆利用。類似於 Java 中的各種 jar 包。
模塊化兩個主要命令:export、import。
export:用於規定模塊的對外介面,即通過 export 可以獲取模塊的內容。
import:用於導入模塊,即通過 import 可以與其他模塊建立起聯繫。
(2)export 命令
通常一個模塊就是一個文件,該文件內部的變數、數組、對象 等外界都不能獲取。需要使用 export 將其導出。
export 可以導出 基本類型變數、函數、數組、對象等。
【導出方式一:】 export var test = "hello"; 【導出方式二:】 var a = "hello"; var b = [1, 2, 3]; export {a, b}; 導出方式一、導出方式二 對外暴露的介面名 為 變數名、函數名等。 使用 import 要填寫正確的介面名,才能正常引用模塊,否則會出錯。 【導出方式三:(使用 as 自定義介面名)】 var a = "hello"; var b = [1, 2, 3]; export { a as AA, b as BB }; 【導出方式四:(使用 default 可以忽略介面名,此時 import 可以自定義介面名)】 export default { var a = "hello"; var b = [1, 2, 3]; }
(3)import 命令
export 定義了模塊對外的介面後,可以使用 import 導入相應的模塊功能。
【導入方式一:(使用 {} 可以一次接受多個模塊介面名)】 import {a, b} from 'xx/xx.js'; 【導入方式二:(使用 as 取名)】 import {a as AA, b as BB} from 'xx/xx.js'; 【導入方式三:(對於 export default 的模塊,可以任意取名)】 import test from 'xx/xx.js';