ES6(ECMAScript 2015)是 JavaScript 的一次重大更新,引入了許多新的特性,使 JavaScript 代碼更加簡潔、可讀和高效。以下是 ES6 的主要新特性及其原理
1. let 和 const 關鍵字
原理解析
1.1 作用域
-
var
關鍵字的作用域:在 ES5 及之前,JavaScript 只有函數作用域(Function Scope),即var
聲明的變量只在函數內部可見,但在塊級{}
內仍然可以訪問:if (true) {var x = 10; } console.log(x); // 10,x 仍然可訪問
由于
x
是var
聲明的,它的作用域擴展到整個函數或全局,而非if
代碼塊內部。 -
let
和const
關鍵字的作用域:let
和const
是 塊級作用域(Block Scope),即它們聲明的變量只在當前代碼塊{}
內有效。- 這避免了
var
可能導致的全局污染問題。
if (true) {let y = 20; } console.log(y); // ReferenceError: y is not defined
1.2 暫時性死區(Temporal Dead Zone, TDZ)
-
let
和const
不會像var
一樣變量提升(Hoisting),而是進入暫時性死區,直到執行到變量聲明的位置,變量才可用。console.log(a); // ReferenceError: Cannot access 'a' before initialization let a = 10;
解釋:
- 代碼在執行時,會先創建變量的作用域。
- 但是
let
聲明的變量在作用域創建后,直到真正聲明前,處于“暫時性死區”。 - 只有
let a = 10;
執行后,a
才能被訪問。
1.3 const 的特性
const
聲明的變量不可被重新賦值:const pi = 3.14; pi = 3.14159; // TypeError: Assignment to constant variable.
- 但是對象類型(數組、對象)可以修改其內容:
解釋:const obj = { name: "Alice" }; obj.name = "Bob"; // 允許修改對象的屬性 console.log(obj); // { name: "Bob" }
const
只是確保obj
這個變量的引用地址不會變,但對象的內部屬性仍然可以修改。
2. 模板字符串(Template Literals)
原理解析
2.1 變量插值
- 傳統字符串拼接需要
+
號,而模板字符串可以使用${}
插入變量:let name = "Alice"; let greeting = `Hello, ${name}!`; console.log(greeting); // "Hello, Alice!"
2.2 多行字符串
- 傳統的字符串換行必須使用
\n
:let str = "Hello\nWorld";
- 但模板字符串可以直接換行:
let str = `Hello World`; console.log(str);
3. 箭頭函數(Arrow Functions)
原理解析
3.1 this
綁定機制
-
普通
function
的this
由調用者決定,但箭頭函數的this
由 定義時的作用域 決定:function normalFunction() {console.log(this); // this 取決于調用方式 }const arrowFunction = () => {console.log(this); // this 由定義時決定 };
-
在 事件回調、定時器、
map
/filter
中,箭頭函數可以避免this
綁定問題: -
const obj = {value: 10,method: function () {setTimeout(() => {console.log(this.value); // 10}, 1000);} }; obj.method();
解釋:
setTimeout
里的回調是箭頭函數,this
保持method
里的this
,即obj
。
3.2 語法簡化
- 省略
function
關鍵字:const add = (a, b) => a + b;
- 單個參數可省略括號:
const square = x => x * x;
4. 解構賦值(Destructuring Assignment)
原理解析
4.1 數組解構
- 直接提取數組元素:
let [a, b, c] = [1, 2, 3]; console.log(a, b, c); // 1, 2, 3
- 跳過某些元素:
let [first, , third] = [10, 20, 30]; console.log(first, third); // 10, 30
4.2 對象解構
- 提取對象屬性:
let { name, age } = { name: "Alice", age: 25 }; console.log(name, age); // "Alice", 25
- 給解構變量賦別名:
let { name: userName } = { name: "Alice" }; console.log(userName); // "Alice"
4.3 默認值
- 如果解構的值
undefined
,可提供默認值:let { x = 10 } = {}; console.log(x); // 10
5. 默認參數(Default Parameters)
原理解析
5.1 傳統方式
- 過去需要手動檢查參數:
function greet(name) {name = name || "Guest"; // 傳統方式console.log(`Hello, ${name}!`); } greet(); // "Hello, Guest!"
5.2 ES6 語法
- 直接在參數定義時提供默認值:
function greet(name = "Guest") {console.log(`Hello, ${name}!`); } greet(); // "Hello, Guest!"
- 默認參數只在傳
undefined
時生效,null
仍會覆蓋默認值:greet(null); // "Hello, null!"
6. 擴展運算符(Spread Operator, ...)
原理解析
6.1 用于數組
-
展開數組元素:使用
...
運算符將數組的每一項展開,可以合并數組、拷貝數組,甚至將數組插入到另一個數組中。let arr1 = [1, 2, 3]; let arr2 = [...arr1, 4, 5]; console.log(arr2); // [1, 2, 3, 4, 5]
-
拷貝數組:創建一個新數組,避免修改原數組:
let arr1 = [1, 2, 3]; let arr2 = [...arr1]; arr2.push(4); console.log(arr1); // [1, 2, 3] console.log(arr2); // [1, 2, 3, 4]
6.2 用于對象
-
展開對象的屬性:
let obj1 = { name: "Alice", age: 25 }; let obj2 = { ...obj1, city: "New York" }; console.log(obj2); // { name: "Alice", age: 25, city: "New York" }
-
合并對象:
let obj1 = { name: "Alice" }; let obj2 = { age: 25 }; let obj3 = { ...obj1, ...obj2 }; console.log(obj3); // { name: "Alice", age: 25 }
6.3 與函數參數結合使用
- 直接展開數組作為函數參數:
function sum(a, b, c) {return a + b + c; } let nums = [1, 2, 3]; console.log(sum(...nums)); // 6
7. 對象增強語法(Enhanced Object Literals)
原理解析
7.1 屬性簡寫
-
如果對象字面量中的鍵名與變量名相同,可以省略鍵名:
let name = "Alice"; let person = { name }; console.log(person); // { name: "Alice" }
7.2 方法簡寫
-
定義對象方法時,不再需要
function
關鍵字:let person = {greet() {console.log("Hello!");} }; person.greet(); // "Hello!"
7.3 動態屬性名
-
對象的屬性名可以動態設置,使用方括號
[]
:let propName = "age"; let person = {[propName]: 25 }; console.log(person.age); // 25
8. for...of
迭代器
原理解析
8.1 迭代器(Iterator)
-
for...of
用于迭代對象,尤其適用于數組、字符串、Set
、Map
等可迭代對象。let arr = [1, 2, 3]; for (let value of arr) {console.log(value); // 1, 2, 3 }
8.2 與 for...in
的區別
-
for...in
遍歷對象的屬性名,而for...of
遍歷對象的值。let arr = [10, 20, 30];for (let key in arr) {console.log(key); // 0, 1, 2(索引) }for (let value of arr) {console.log(value); // 10, 20, 30(元素值) }
8.3 可迭代對象
- 迭代對象必須實現
[Symbol.iterator]()
方法,如Array
,String
,Map
,Set
等。let str = "Hello"; for (let char of str) {console.log(char); // H, e, l, l, o }
9. Map
和 Set
原理解析
9.1 Map
-
Map
是一個鍵值對集合,支持任意類型的鍵(不僅僅是字符串),并且有序(按插入順序存儲)。let map = new Map(); map.set("name", "Alice"); map.set(1, "one"); console.log(map.get("name")); // Alice console.log(map.get(1)); // one
-
Map
還支持直接遍歷:for (let [key, value] of map) {console.log(key, value); // name Alice, 1 one }
9.2 Set
-
Set
是一個值的集合,其中每個值都是唯一的,不允許重復。let set = new Set([1, 2, 3, 3, 4]); console.log(set); // Set { 1, 2, 3, 4 }
-
Set
可以用來自動去重:let arr = [1, 2, 3, 3, 4]; let uniqueArr = [...new Set(arr)]; console.log(uniqueArr); // [1, 2, 3, 4]
10. 類(Class)
原理解析
10.1 類的定義
-
ES6 引入了更直觀的
class
語法,進行面向對象編程:class Person {constructor(name, age) {this.name = name;this.age = age;}greet() {console.log(`Hello, I'm ${this.name}`);} }let p = new Person("Alice", 25); p.greet(); // "Hello, I'm Alice"
10.2 類的繼承
-
class
還支持繼承,使用extends
關鍵字:class Student extends Person {constructor(name, age, grade) {super(name, age);this.grade = grade;}study() {console.log(`${this.name} is studying`);} }let student = new Student("Bob", 20, "A"); student.greet(); // "Hello, I'm Bob" student.study(); // "Bob is studying"
-
super()
調用父類構造函數。
11. Promise
和異步操作
原理解析
11.1 Promise
的構造
-
Promise
是用于處理異步操作的一種機制,可以簡化回調函數的使用,避免回調地獄:let promise = new Promise((resolve, reject) => {let success = true;if (success) {resolve("Operation successful");} else {reject("Operation failed");} });promise.then((message) => {console.log(message); // "Operation successful" }).catch((error) => {console.log(error); // "Operation failed" });
11.2 Promise
的鏈式調用
-
then()
返回一個新的Promise
,因此可以鏈式調用多個異步操作:let promise = new Promise((resolve, reject) => resolve(10));promise.then(value => {return value * 2; }).then(value => {console.log(value); // 20 });
12. 模塊化(Modules, import/export
)
原理解析
12.1 導出(export
)
-
使用
export
將函數、變量或類導出,使其可以在其他文件中使用:// math.js export const pi = 3.14; export function add(a, b) {return a + b; }
12.2 導入(import
)
-
使用
import
從其他模塊導入:// main.js import { pi, add } from './math.js'; console.log(pi); // 3.14 console.log(add(2, 3)); // 5
13. Symbol
類型
原理解析
13.1 唯一性
-
Symbol
是一個 唯一的原始數據類型,每個Symbol
值都是唯一的。let sym1 = Symbol("desc"); let sym2 = Symbol("desc"); console.log(sym1 === sym2); // false
13.2 用作對象的私有屬性
-
Symbol
可以用于創建私有對象屬性,防止外部訪問:const secret = Symbol("secret"); let obj = {[secret]: "hidden" }; console.log(obj[secret]); // "hidden"
j