5月28日 03:36
ES5 和 ES6 有什么区别?
ES6(ES2015)是 JavaScript 历史上最大的一次版本更新,面试中这道题考查的是你对 JS 语言演进的理解深度。回答的关键不是罗列特性,而是讲清楚每个变化解决了什么问题。
变量声明:从 var 到 let/const
ES5 只有 var,存在两大问题:
js// 问题1:变量提升 console.log(a); // undefined(不会报错,但容易出 bug) var a = 1; // 问题2:无块级作用域 for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); // 3, 3, 3 }
ES6 用 let/const 解决了这两个问题:
js// let 有块级作用域 + 暂时性死区 console.log(b); // ReferenceError(声明前访问直接报错) let b = 1; for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); // 0, 1, 2 } // const 不可重新赋值(但对象属性仍可修改) const obj = { a: 1 }; obj.a = 2; // OK obj = { a: 2 }; // TypeError
面试要点:const 保证的是绑定不可变,不是值不可变。想冻结对象用 Object.freeze()。
函数:箭头函数与 this 绑定
ES5 中 this 指向取决于调用方式,经常需要 var self = this 或 .bind(this):
js// ES5 var obj = { name: 'ES5', say: function() { var self = this; setTimeout(function() { console.log(self.name); // 必须用 self/cache }, 0); } }; // ES6 — 箭头函数继承外层 this const obj2 = { name: 'ES6', say() { setTimeout(() => { console.log(this.name); // 直接用 this }, 0); } };
注意:箭头函数没有自己的 arguments、super、new.target,不能用作构造函数。
字符串:模板字符串
js// ES5 var greeting = 'Hello, ' + name + '! You are ' + age + ' years old.'; // ES6 const greeting = `Hello, ${name}! You are ${age} years old.`;
模板字符串支持多行、变量插值、标签模板,彻底告别字符串拼接。
解构赋值与展开运算符
js// 对象解构 const { name, age } = user; // 数组解构 const [first, ...rest] = [1, 2, 3, 4]; // first=1, rest=[2,3,4] // 展开运算符 — 浅拷贝与合并 const copy = [...arr]; const merged = { ...defaults, ...config };
解构让数据提取更简洁,展开运算符替代了 Object.assign 和 concat 的大多数场景。
类与继承:class 语法
js// ES5 — 构造函数 + 原型链 function Animal(name) { this.name = name; } Animal.prototype.speak = function() { return this.name + ' makes a sound'; }; // ES6 — class 语法 class Animal { constructor(name) { this.name = name; } speak() { return `${this.name} makes a sound`; } } class Dog extends Animal { speak() { return `${this.name} barks`; } }
class 本质是原型继承的语法糖,但有行为差异:内部默认严格模式、方法不可枚举、必须用 new 调用。
模块系统:import/export
js// ES5 — CommonJS(Node.js) const module = require('./module'); module.exports = { foo }; // ES6 — ES Modules import { foo } from './module'; export const bar = 1; export default function() {}
ES Modules 是静态的,支持 Tree Shaking;CommonJS 是动态的,运行时加载。现代项目(Vite/Webpack)均以 ESM 为优先。
异步编程:Promise 与 async/await
js// ES5 — 回调地狱 getData(function(a) { getMore(a, function(b) { getEvenMore(b, function(c) { console.log(c); }); }); }); // ES6 — Promise 链式调用 getData() .then(a => getMore(a)) .then(b => getEvenMore(b)) .then(c => console.log(c)); // ES8 — async/await(同步写法) const a = await getData(); const b = await getMore(a); const c = await getEvenMore(b);
Promise 解决了回调地狱,async/await 让异步代码看起来像同步,是面试高频追问点。
新数据结构与 API
| 特性 | 用途 |
|---|---|
Map | 键值对集合,键可以是任意类型(Object 的键只能是字符串/Symbol) |
Set | 去重数组:[...new Set(arr)] |
WeakMap/WeakSet | 键是弱引用,适合缓存和关联私有数据,不阻止 GC |
Symbol | 创建唯一标识符,用于私有属性和内置协议 |
Proxy/Reflect | 拦截对象操作(Vue 3 响应式核心) |
Generator/Iterator | 可暂停函数,for...of 遍历统一接口 |
追问
ES6 之后还有什么重要的新特性?
| 版本 | 关键特性 |
|---|---|
| ES7 | Array.prototype.includes、指数运算符 ** |
| ES8 | async/await、Object.values/entries |
| ES9 | Promise.finally、异步迭代 for await...of |
| ES10 | flat/flatMap、Object.fromEntries |
| ES11 | ??(空值合并)、?.(可选链)、Promise.allSettled |
| ES12 | replaceAll、逻辑赋值 ` |
| ES13 | at()、Object.hasOwn、Top-level await |
let/const 和 var 最大的实际区别?
- 块级作用域 — 解决 for 循环闭包问题
- 暂时性死区 — 声明前访问报 ReferenceError,var 是 undefined
- 不可重复声明 — 同一作用域内 let/const 不能重复声明同名变量
- const 不可重新赋值 — 但对象/数组内容仍可修改
class 只是语法糖吗?
基本是。class 编译后就是原型链模式(构造函数 + prototype + Object.create)。但有几个行为差异:
- class 内部默认严格模式
- class 方法不可枚举(
for...in遍历不到) - 只能用
new调用(有new.target检查,直接调用报错) extends内部用Object.create设置原型链,比 ES5 手动写更规范
面试回答策略
面试官问这道题,不是让你背特性列表。推荐的回答结构:
- 一句话概括:ES6 让 JS 从脚本语言变成工程化语言
- 按类别讲 3-4 个重点,每个说清楚"ES5 什么问题 → ES6 怎么解决"
- 追问时深入:挑一个你最熟悉的特性展开(如 class 的原型链原理、Promise 的微任务机制)