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