ES6+ 新特性详解

小飞兽 JavaScript 240 次阅读 2026-06-17

ES6+ 新特性详解

一、Introduction(简介)

ES6(ECMAScript 2015)是 JavaScript 语言自 2009 年 ES5 标准化以来最重要的版本更新。ES6 引入大量新语法和新特性,从根本上提升了 JavaScript 的表达能力和开发体验。此后每年发布的 ECMAScript 新版本(ES2016/ES2017/ES2018...)统称为 ES6+,不断为这门语言注入新的活力。

本篇文章将系统梳理 ES6 及后续版本中最常用的新特性,涵盖变量声明、函数增强、箭头函数、模板字符串、解构赋值、Class 语法、模块系统、Promise、async/await、Symbol、Map/Set 等核心内容。通过大量代码示例,帮助读者快速掌握并运用到实际项目中。

二、基础语法

2.1 let 与 const 变量声明

ES6 之前,JavaScript 只有 var 一种声明变量的方式,存在变量提升(hoisting)和函数级作用域的局限。ES6 引入了 letconst,弥补了这些缺陷。

    • let:块级作用域(block scope),不存在变量提升,在声明前访问会抛出 ReferenceError(暂时性死区 TDZ)。
    • const:块级作用域,声明时必须初始化,且不能重新赋值(但对对象/数组内部属性修改仍然允许)。
    // var 的问题:变量提升导致意外行为
    console.log(a); // undefined(变量提升,但值未定义)
    var a = 10;
    

    // let:块级作用域,不存在变量提升
    {
    let b = 20;
    console.log(b); // 20
    }
    // console.log(b); // ReferenceError: b is not defined

    // const:声明常量
    const PI = 3.14159;
    // PI = 3.14; // TypeError: Assignment to constant variable

    // const 对象可以修改属性
    const USER = { name: 'Alice', age: 25 };
    USER.age = 26; // 允许
    // USER = {}; // 不允许,重新赋值
    console.log(USER); // { name: 'Alice', age: 26 }

    2.2 箭头函数(Arrow Functions)

    箭头函数是 ES6 最受欢迎的语法糖之一,用 => 符号定义函数,语法简洁且不绑定自己的 this

    // 传统函数
    function sum(a, b) {
        return a + b;
    }
    

    // 箭头函数
    const sum = (a, b) => a + b;

    // 单参数可省略圆括号
    const double = x => x * 2;

    // 多行函数体需要 return
    const process = (x) => {
    const temp = x * 2;
    return temp + 1;
    };

    // 箭头函数不绑定 this
    function Timer() {
    this.time = 0;
    // 传统写法需要用 that / bind(this)
    setInterval(function() {
    this.time++; // this 指向 window(浏览器)或 undefined(严格模式)
    console.log(this.time);
    }, 1000);

    // 箭头函数继承外层 this
    setInterval(() => {
    this.time++;
    console.log(this.time);
    }, 1000);
    }

    2.3 模板字符串(Template Literals)

    模板字符串使用反引号(</code>)界定字符串,支持多行文本和嵌入表达式(<code>${}</code>),告别字符串拼接的噩梦。</p>

    <pre><code>const name = 'Bob';
    const age = 30;

    // 字符串拼接(ES5 方式)
    const bio = 'My name is ' + name + ', I am ' + age + ' years old.';

    // 模板字符串(ES6)
    const bio2 =
    My name is ${name}, I am ${age} years old.;

    // 多行文本
    const html =

    <div class="card">
    <h2>${name}</h2>
    <p>Age: ${age}</p>
    </div>
    ;

    // 支持表达式
    const a = 10, b = 5;
    console.log(
    a + b = ${a + b}); // a + b = 15

    // 支持函数调用
    const toUpper = s => s.toUpperCase();
    console.log(
    Hello ${toUpper('world')}); // Hello WORLD
    </code></pre>

    <h3>2.4 解构赋值(Destructuring)</h3>
    <p>解构赋值允许从数组或对象中提取值,赋值给变量,语法优雅且功能强大。</p>

    <pre><code>// 数组解构
    const [first, second, third] = ['apple', 'banana', 'cherry'];
    console.log(first); // apple

    // 交换变量(无需临时变量)
    let x = 1, y = 2;
    [x, y] = [y, x];
    console.log(x, y); // 2 1

    // 对象解构
    const person = { name: 'Alice', age: 28, city: 'Shanghai' };
    const { name, age } = person;
    console.log(name); // Alice

    // 重命名变量
    const { name: userName, age: userAge } = person;

    // 函数参数解构
    function greet({ name, age }) {
    return
    Hello, ${name}! You are ${age}.;
    }
    console.log(greet(person)); // Hello, Alice! You are 28.

    // 默认值
    const { hobby = 'reading' } = person;
    console.log(hobby); // reading

    // 嵌套解构
    const config = {
    server: { host: 'localhost', port: 8080 },
    db: { name: 'mydb' }
    };
    const { server: { host, port } } = config;
    console.log(host, port); // localhost 8080
    </code></pre>

    <h3>2.5 Class 类语法</h3>
    <p>ES6 引入 <code>class</code> 关键字,提供更清晰的面向对象编程语法,本质上是构造函数(Constructor Function)的语法糖。</p>

    <pre><code>// 定义类
    class Animal {
    // 构造函数
    constructor(name, sound) {
    this.name = name;
    this.sound = sound;
    }

    // 实例方法
    speak() {
    console.log(
    ${this.name} says: ${this.sound});
    }

    // 静态方法(类本身调用)
    static info() {
    console.log('This is an Animal class');
    }
    }

    // 继承
    class Dog extends Animal {
    constructor(name) {
    super(name, 'Woof!'); // 调用父类构造函数
    this.type = 'Dog';
    }

    // 方法重写
    speak() {
    console.log(
    ${this.name} (a ${this.type}) barks: ${this.sound});
    }

    // 子类独有方法
    fetch() {
    console.log(
    ${this.name} fetches the ball!`);
    }
    }

    const dog = new Dog('Buddy');
    dog.speak(); // Buddy (a Dog) barks: Woof!
    dog.fetch(); // Buddy fetches the ball!
    Animal.info(); // This is an Animal class

    // getter / setter
    class Circle {
    constructor(radius) {
    this._radius = radius;
    }

    get diameter() {
    return this._radius * 2;
    }

    set diameter(value) {
    this._radius = value / 2;
    }

    get area() {
    return Math.PI * this._radius ** 2;
    }
    }

    const c = new Circle(5);
    console.log(c.diameter); // 10
    console.log(c.area); // 78.539...
    c.diameter = 20;
    console.log(c._radius); // 10

    2.6 模块系统(import / export)

    ES6 原生支持模块化编程,通过 importexport 实现模块的导入导出,取代了之前依赖第三方库(如 CommonJS、RequireJS)的局面。

    // ---- math.js ----
    // 命名导出
    export const PI = 3.14159;
    export function add(a, b) {
        return a + b;
    }
    export function subtract(a, b) {
        return a - b;
    }
    

    // 默认导出(每个文件只能有一个 default)
    // export default function multiply(a, b) { return a * b; }

    // ---- main.js ----
    // 导入命名导出
    import { PI, add, subtract } from './math.js';
    console.log(add(PI, 10)); // 13.14159

    // 导入时重命名
    import { add as sum } from './math.js';

    // 导入所有命名导出
    import * as math from './math.js';
    console.log(math.PI);

    // 导入默认导出
    // import multiply from './math.js';
    // console.log(multiply(3, 4)); // 12

    // 动态导入
    // const module = await import('./math.js');
    // console.log(module.add(1, 2));

    2.7 Promise 与 async/await

    Promise 是异步编程的基础,async/await 则是 ES2017 引入的 Promise 语法糖,让异步代码看起来像同步代码。

    // Promise 基本用法
    const fetchData = () => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                const success = true;
                if (success) {
                    resolve({ id: 1, name: 'Alice' });
                } else {
                    reject(new Error('Fetch failed'));
                }
            }, 1000);
        });
    };
    

    fetchData()
    .then(data => console.log(data))
    .catch(err => console.error(err))
    .finally(() => console.log('Done'));

    // async/await(ES2017)
    async function getData() {
    try {
    const data = await fetchData();
    console.log(data);
    } catch (err) {
    console.error(err);
    }
    }
    getData();

    // Promise.all 并行执行
    const p1 = Promise.resolve(1);
    const p2 = Promise.resolve(2);
    const p3 = Promise.resolve(3);

    Promise.all([p1, p2, p3])
    .then(results => console.log(results)); // [1, 2, 3]

    2.8 Symbol、Map、Set、WeakMap、WeakSet

    // Symbol:唯一标识符
    const sym1 = Symbol('id');
    const sym2 = Symbol('id');
    console.log(sym1 === sym2); // false(即使描述相同也不相等)
    

    // Symbol 用于对象属性(不冲突)
    const obj = {
    [sym1]: 'value1',
    [sym2]: 'value2'
    };

    // Map:键可以是任意类型
    const map = new Map();
    map.set('name', 'Alice');
    map.set(42, 'answer');
    map.set({}, 'object key'); // 对象作为键(普通对象不行)
    console.log(map.get(42)); // answer
    console.log(map.size); // 3
    map.forEach((v, k) => console.log(k, v));
    map.delete('name');
    // map.clear();

    // Set:无重复值的集合
    const set = new Set([1, 2, 2, 3, 3, 3]);
    console.log([...set]); // [1, 2, 3](自动去重)
    set.add(4);
    set.has(2); // true
    set.delete(2);

    // WeakMap / WeakSet:不阻止垃圾回收,适合存储对象私有数据
    const wm = new WeakMap();
    const obj2 = {};
    wm.set(obj2, 'private data');
    // 当 obj2 被回收时,WeakMap 自动清除对应条目

    2.9 扩展运算符(Spread)与剩余参数(Rest)

    // 扩展运算符:将可迭代对象展开
    const arr1 = [1, 2, 3];
    const arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]
    

    // 对象展开(ES2018)
    const obj1 = { a: 1, b: 2 };
    const obj2 = { ...obj1, c: 3 }; // { a: 1, b: 2, c: 3 }

    // 函数剩余参数
    function sum(...numbers) {
    return numbers.reduce((acc, n) => acc + n, 0);
    }
    console.log(sum(1, 2, 3, 4)); // 10

    // 剩余参数配合解构
    const [first, ...rest] = [10, 20, 30, 40];
    console.log(first); // 10
    console.log(rest); // [20, 30, 40]

    三、运行效果

    将上述代码片段保存为 es6-demo.js,使用 Node.js(v6+)或现代浏览器控制台运行:

    $ node es6-demo.js
    apple
    2 1
    My name is Bob, I am 30 years old.
    Buddy (a Dog) barks: Woof!
    Buddy fetches the ball!
    13.141592653589793
    [1, 2, 3]
    false
    answer
    [1, 2, 3]
    10
    

    在浏览器中打开任意现代网页(Chrome、Firefox、Safari、Edge),按 F12 打开开发者工具,切到 Console 标签,粘贴代码即可看到输出结果。ES6 模块在浏览器中需要通过 <script type="module"> 引入。

    四、常见问题

    Q1:let/const 到底比 var 好在哪里?

    答: 主要有三大区别:

    1. 作用域var 是函数级作用域,let/const 是块级作用域。在 iffor{} 块中,var 的变量会泄露到函数级,而 let/const 严格限定在块内。
  • 变量提升var 声明会被提升(hoisting),导致在声明前访问得到 undefinedlet/const 存在暂时性死区(TDZ),在声明前访问直接抛出 ReferenceError
  • 重复声明var 允许重复声明,let/const 不允许,在编译阶段就会报错。
// var 的坑
for (var i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 100); // 输出 3, 3, 3(而非 0, 1, 2)
}

// let 解决了这个问题
for (let j = 0; j < 3; j++) {
setTimeout(() => console.log(j), 100); // 输出 0, 1, 2
}

Q2:箭头函数能替代所有普通函数吗?

答: 不能。箭头函数有以下局限性:

    • 没有自己的 thisargumentssuper,不适合作为对象方法。
    • 不能用作构造函数(new ArrowFunc() 会报错)。
    • 不能使用 arguments 对象(可以用剩余参数替代)。
    • 没有 prototype 属性。
    // 箭头函数不能作为构造函数
    const MyClass = () => {};
    // new MyClass(); // TypeError: MyClass is not a constructor
    

    // 箭头函数作为对象方法的陷阱
    const counter = {
    count: 0,
    // 这里的 this 指向外层(window/undefined),不是 counter
    inc: () => this.count++,
    };
    // 普通函数更适合:
    counter2 = {
    count: 0,
    inc() { this.count++; }
    };

    Q3:const 对象的属性可以修改吗?

    答: 可以。const 保证的是变量引用(指针)不变,而不是对象内容不可变。因此可以修改对象的属性、数组的元素。

    const arr = [1, 2, 3];
    arr.push(4);       // 合法
    arr[0] = 100;      // 合法
    // arr = [5, 6];   // 不合法,重新赋值
    

    // 想冻结对象?使用 Object.freeze()
    const frozen = Object.freeze([1, 2, 3]);
    // frozen.push(4); // TypeError: Cannot add property 0, object is not extensible

    Q4:import 是静态还是动态导入?有什么影响?

    答: ES6 的 import 是静态导入,在编译阶段就完成解析。这意味着:

    • import 必须在模块顶层,不能在条件语句或函数内使用。
    • 静态导入能帮助打包工具(如 Webpack、Rollup)进行 Tree Shaking,消除未使用的代码。
    • 如果需要动态导入(按需加载),使用 import() 函数,它返回一个 Promise。

    五、延伸阅读

    1. MDN Web Docs - ES6https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/New_in_JavaScript — Mozilla 官方文档,最权威的 ES6 参考。
  • ES6 入门教程(阮一峰)https://es6.ruanyifeng.com/ — 中文社区最受欢迎的 ES6 教程书籍(免费在线阅读)。
  • Babel 官网https://babeljs.io/ — 将 ES6+ 代码转译为 ES5 的工具,让旧浏览器也能运行新语法。