ES6(ECMAScript 2015)是JavaScript语言的一次重大升级,它一次性引入了几十个新特性,让这门语言的书写体验和表达能力有了质的飞跃。如今主流浏览器和Node.js都已经全面支持ES6,掌握它已经不再是一道选择题,而是每个前端开发者的必修课。这篇文章会从环境准备开始,把ES6中最核心的特性逐一拆解,配上可运行的代码示例,最后再总结实战中容易踩的坑。

一、环境搭建:从零开始运行ES6代码
学习ES6之前,先要有一个能运行代码的环境。最简单的方式是直接打开浏览器的开发者工具控制台(按F12即可),现代浏览器基本都支持ES6语法,把代码粘贴进去就能看到结果。这种方式适合快速验证某个语法点,但不适合写完整的程序。
如果想写完整的项目,推荐安装Node.js。Node.js从较新的版本开始已经原生支持绝大部分ES6特性,安装完成后,把代码保存为.js文件,再执行node index.js就能运行。如果项目需要兼容老版本浏览器,还需要借助Babel这样的转译工具把ES6代码转换成ES5代码:
npm install --save-dev @babel/core @babel/cli @babel/preset-env # 转换单个文件 npx babel index.js --out-file compiled.js # 转换整个目录 npx babel src --out-dir lib
配置好环境后,建议创建一个练习目录,边学边敲代码。编程语言的学习没有捷径,动手写永远是掌握新语法最快的方式。
二、let、const与块级作用域
ES6之前,JavaScript只有var一种声明变量的方式,而var声明的变量存在变量提升和没有块级作用域的问题,容易造成难以察觉的bug。ES6引入的let和const很好地解决了这些痛点。
var声明的变量会提升到函数顶部,也就是说你在声明之前访问它不会报错,只会得到undefined。而let声明的变量存在所谓的暂时性死区,在声明语句之前访问会直接抛出错误,这能帮我们尽早发现问题。同时,let和const都具有块级作用域,只在花括号内有效:
function testScope() {
if (true) {
var a = 1;
let b = 2;
const c = 3;
}
console.log(a); // 1,var没有块级作用域
console.log(b); // 报错,b在块外不可访问
}
// 暂时性死区演示
function tdzDemo() {
console.log(x); // ReferenceError
let x = 10;
}const用来声明常量,需要注意的是,const限制的是变量名与内存地址的绑定关系,而不是值本身不可变。如果const指向一个对象,对象的属性仍然可以被修改:
const user = { name: '张三' };
user.name = '李四'; // 合法,修改的是对象内容
user = {}; // 报错,不能重新赋值
// 如果确实需要冻结对象,可以使用Object.freeze
const config = Object.freeze({ debug: true });
config.debug = false; // 静默失败,严格模式下会报错实际开发中有一条被广泛认可的约定:默认使用const,只有确定会被重新赋值时才用let,尽量不用var。这样能让代码意图更清晰,也能减少意外修改带来的问题。
三、箭头函数与解构赋值
箭头函数是ES6中使用频率最高的语法之一,它让函数书写变得非常简洁。当函数体只有一条返回语句时,连花括号和return都可以省略:
const numbers = [1, 2, 3, 4, 5];
// 传统写法
const doubled = numbers.map(function (n) {
return n * 2;
});
// 箭头函数写法
const doubled2 = numbers.map(n => n * 2);
// 多参数需要括号
const sum = numbers.reduce((acc, cur) => acc + cur, 0);箭头函数与传统函数最关键的区别在于this的指向。普通函数的this在调用时才确定,而箭头函数本身没有this,它的this继承自定义时的外层作用域。这个特性在回调函数中特别有用,比如在定时器里访问组件实例就不再需要额外的self变量来保存this。
不过要注意,正因为箭头函数没有自己的this,它不适合作为对象的方法。对象的花括号并不构成独立作用域,箭头函数里的this会指向全局对象,往往不符合预期。
解构赋值允许按照一定模式从数组或对象中提取值,写起来非常顺手。函数参数解构配合默认值,可以让代码具备很强的自描述性:
// 对象解构
const { name, age = 18 } = { name: '张三', age: 25 };
// 数组解构,交换变量
let x = 1, y = 2;
[x, y] = [y, x]; // x=2, y=1
// 函数参数解构,带默认值
function createUser({ name, role = 'member', enabled = true } = {}) {
return { name, role, enabled };
}
createUser({ name: '李四' }); // { name: '李四', role: 'member', enabled: true }与解构配套的还有展开运算符和剩余参数,三个点既能展开数组与对象,也能收集剩余的参数,在合并数据、拷贝对象时非常实用。需要注意展开运算符只做浅拷贝,嵌套对象仍然共享引用,深拷贝需要借助其他方案。
四、异步编程:从Promise到async和await
异步编程是JavaScript绕不开的话题。ES6之前只能依靠回调函数,层级一深就容易出现嵌套地狱。ES6正式引入了Promise,把异步操作用链式调用的方式组织起来:
function fetchUser(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id > 0) resolve({ id, name: '张三' });
else reject(new Error('无效的用户ID'));
}, 500);
});
}
fetchUser(1)
.then(user => user.name)
.then(name => console.log(name))
.catch(err => console.error(err.message));在Promise基础上,ES2017提出的async和await让异步代码看起来像同步代码,可读性大幅提升。async函数永远返回Promise,await只能用在async函数内部,它会让代码在等待结果期间暂停执行而不阻塞主线程:
async function loadUser(id) {
try {
const user = await fetchUser(id);
console.log(user.name);
return user;
} catch (err) {
console.error('加载失败:', err.message);
}
}
// 并行执行多个异步任务,用Promise.all而不是依次await
async function loadAll() {
const [a, b] = await Promise.all([fetchUser(1), fetchUser(2)]);
console.log(a, b);
}这里有一个常见的性能误区:连续写多个await会让任务串行执行。如果这些任务之间没有依赖关系,应该用Promise.all让它们并行跑,整体耗时取决于最慢的那个任务,而不是所有任务耗时之和。
五、模块化与Class语法
ES6在语言层面正式支持了模块化,通过export导出、import导入,彻底摆脱了全局变量式的脚本管理方式。模块化让代码按功能拆分,依赖关系一目了然:
// utils.js
export function formatPrice(cents) {
return (cents / 100).toFixed(2);
}
export default class Cart {
constructor() {
this.items = [];
}
add(item) {
this.items.push(item);
}
}
// main.js
import Cart, { formatPrice } from './utils.js';
const cart = new Cart();
cart.add({ name: '书', price: 5900 });
console.log(formatPrice(5900)); // 59.00Class语法让面向对象的写法更接近传统语言,但它的本质仍然是基于原型的语法糖。constructor中定义实例属性,static关键字定义静态方法,extends实现继承,super调用父类。私有属性在较新版本中可以用井号前缀表示,外部无法直接访问,这对封装性很有帮助。
需要理解的是,Class并不会改变JavaScript的原型继承机制,理解原型链依然是深入掌握这门语言绕不开的一步,Class只是让写法更友好。
六、常见问题与注意事项
初学ES6时有几个高频踩坑点值得单独说明。第一是暂时性死区,只要块级作用域内存在let或const声明,那么在声明之前访问同名变量都会报错,哪怕外层作用域有同名变量。第二是箭头函数不适合作为对象方法和构造函数,需要动态this的场景应该用普通函数。
第三是模板字符串中的嵌套问题。模板字符串支持变量插值和多行文本,但当内容里包含反引号或美元符号时,要注意转义,避免出现意外拼接。第四是默认参数的求值时机,只有传入undefined时才会启用默认值,传入null并不会触发默认值,这一点在与后端接口对接时尤其容易出问题。
最后建议在实际项目中循序渐进地使用新特性,不必追求一次性用上所有语法。先从let、const、箭头函数、模板字符串这些低风险特性开始,团队代码风格保持统一,代码评审时多交流最佳实践。把每个特性用到恰当的场景,而不是为了炫技而堆砌语法,这才是用好ES6的正确姿势。
JavaScriptES6新特性JavaScript教程修改时间:2026-09-03 09:17:00