真实面经题目 · 原创解析
ES6的新特性有什么?
ES6 是 JavaScript 现代化的关键版本,核心价值不是新增若干语法糖,而是系统性解决了旧版 JavaScript 在变量作用域、异步流程、模块组织、数据结构、面向对象表达、可迭代协议和元编程能力上的工程问题。面试回答应从语法特性切入,再说明它们分别改善了可读性、可维护性、作用域安全、异步可控性、模块边界和复杂数据建模能力。
真实面经题目 · 原创解析
ES6 是 JavaScript 现代化的关键版本,核心价值不是新增若干语法糖,而是系统性解决了旧版 JavaScript 在变量作用域、异步流程、模块组织、数据结构、面向对象表达、可迭代协议和元编程能力上的工程问题。面试回答应从语法特性切入,再说明它们分别改善了可读性、可维护性、作用域安全、异步可控性、模块边界和复杂数据建模能力。
ES6 的新特性可以按工程价值来讲。第一类是变量和函数表达能力,比如 let、const、块级作用域、箭头函数、模板字符串、解构、默认参数、展开和剩余参数,它们让代码更简洁,也减少变量提升、this 指向混乱和参数处理冗余。第二类是异步和模块化能力,比如 Promise 和 module,它们解决回调嵌套、异步状态管理和大型项目依赖组织问题。第三类是语言模型和数据结构增强,比如 class、Symbol、Map、Set、迭代器和 generator,它们让 JavaScript 更适合构建复杂工程:可以表达类、唯一标识、键值集合、去重集合、统一遍历协议和可暂停执行流程。回答时不要只背特性名称,要说明每个特性替代了什么旧写法,以及带来了哪些可维护性收益。
ES6 最基础但最重要的变化是引入 let 和 const。旧的 var 只有函数作用域,存在变量提升、重复声明、循环闭包等问题,容易让变量在不该被访问的位置被访问。let 提供块级作用域,变量只在当前代码块内有效,并且不允许在同一作用域重复声明;const 用来声明常量绑定,强调变量引用不应被重新赋值。需要注意,const 保护的是绑定关系,不是对象内部属性的绝对不可变。块级作用域让 if、for、while、try 等结构拥有更清晰的变量边界,减少了变量污染,也让循环中创建闭包时更符合直觉。工程上,优先使用 const,需要重新赋值时再使用 let,是现代 JavaScript 的常见约定。
箭头函数提供更短的函数写法,并且不会创建自己的 this、arguments、super 和 new.target。它的 this 来自外层词法作用域,因此特别适合回调、数组方法和异步任务中的上下文保持。它解决了旧代码中频繁使用 that、self 或 bind 来固定 this 的问题。但箭头函数并不适合作为对象方法、构造函数或需要动态 this 的函数。默认参数让函数可以直接为参数声明默认值,减少手动判断 undefined 的样板逻辑。剩余参数使用 ... 语义收集多余实参,替代 arguments 这种类数组对象,使参数处理更清晰,也更容易配合数组方法。
模板字符串用反引号语义支持多行字符串和表达式插值,解决了旧写法中字符串拼接冗长、换行不自然、可读性差的问题。它适合构建提示文案、URL 片段和结构化字符串,但仍应注意安全场景中的转义问题。解构赋值支持从数组和对象中提取值,可以配合默认值、重命名和嵌套结构使用。它让函数参数、接口返回值、配置对象的读取更简洁。工程上,解构可以提升可读性,但过深的嵌套解构会降低理解成本,真实项目中应在简洁和清晰之间取平衡。
展开语法使用 ... 将数组、可迭代对象或对象属性展开到新的上下文中。它可以用于数组合并、函数传参、对象浅拷贝和对象合并,替代部分 concat、apply、Object.assign 等旧写法。展开语法的工程价值在于让不可变更新更自然,例如创建新数组或新对象而不是直接修改原数据。但展开只做浅层复制,嵌套对象仍共享引用,因此处理复杂状态时不能误以为它完成了深拷贝。剩余语法和展开语法形式相同,但语义相反:剩余是收集,展开是拆开。
Promise 是 ES6 对异步结果的标准抽象,用 pending、fulfilled、rejected 表达异步任务的状态变化。它解决了传统回调写法中回调嵌套、错误处理分散、任务组合困难的问题。Promise 支持 then、catch、finally 形式的链式处理,也支持 Promise.all、Promise.race 等组合能力。工程上,Promise 的意义是把异步任务从“传入回调等待调用”变成“返回一个可组合的结果容器”。这为后续 async 和 await 的语法奠定基础。需要注意 Promise 一旦状态确定就不会再改变,错误如果没有被捕获会导致调试困难,链式调用中也要正确 return 后续 Promise,避免异步流程断链。
class 提供了更接近传统面向对象语言的类声明方式,包括 constructor、实例方法、静态方法、extends 和 super。它本质上仍然基于原型机制,不是完全不同的对象模型。class 的主要价值是提升构造函数和原型继承写法的可读性,减少手写 prototype、constructor 指向修正、父类构造调用等样板代码。工程上,class 适合表达具有稳定状态和行为的实体,例如组件基类、业务模型、服务封装等。但理解它的原型本质仍然重要,否则容易误判方法共享、实例属性初始化和 this 绑定问题。
ES6 module 是语言层面的模块系统,使用 export 暴露能力,使用 import 引入依赖。它解决了早期 JavaScript 缺少官方模块规范、全局变量污染、依赖关系不清晰的问题。ES module 是静态结构,导入导出关系可以在编译阶段分析,因此有利于 tree shaking、循环依赖检查和构建优化。默认导出适合一个模块主能力,命名导出适合暴露多个明确成员。工程上,模块化让代码边界更清楚,也让大型应用可以按功能拆分、复用和测试。
Symbol 是 ES6 新增的原始类型,用于创建唯一标识。即使两个 Symbol 的描述相同,它们的值也不相等。它常用于对象属性键,避免命名冲突,也用于定义语言内置协议,例如 Symbol.iterator。Symbol 的工程价值在于提供低冲突的扩展点,适合框架、库或底层工具在对象上挂载内部能力。它不是普通字符串,也不会被常规枚举方法轻易遍历到,因此可以降低误访问概率。但 Symbol 并不等于绝对私有,仍可以通过特定 API 获取。
Map 是键值集合,键可以是任意类型,而普通对象的键主要会转成字符串或 Symbol。Map 保留插入顺序,并提供 size、get、set、has、delete 等明确 API,适合缓存、映射表、按对象作为键存储数据等场景。Set 是值集合,成员唯一,适合去重、集合判断、交集并集差集等业务。相比用对象模拟集合,Map 和 Set 的语义更清晰,边界更少,遍历能力更一致。工程上,它们让复杂数据建模更可靠,减少了对象原型链、键名冲突和手动维护长度带来的问题。
迭代器定义了一套统一遍历协议:对象只要实现特定迭代接口,就可以被 for...of、展开语法、解构等语法消费。数组、字符串、Map、Set 等都天然支持迭代。generator 使用 function* 和 yield 创建可暂停、可恢复的执行流程,它返回一个迭代器对象。generator 的价值不只是生成序列,还在于表达惰性计算、按需取值、复杂流程控制和异步流程编排的基础思想。工程上,迭代器让“如何遍历”成为对象自己的能力,generator 则让连续流程可以被拆成多个可控步骤。
var 是函数作用域,存在变量提升和重复声明问题;let 和 const 是块级作用域,存在暂时性死区,同一作用域不能重复声明。const 声明的是不可重新赋值的绑定,如果绑定的是对象,对象内部属性仍可能被修改。
箭头函数没有自己的 this、arguments、prototype,不能作为构造函数。它的 this 来自外层词法作用域,因此适合回调函数,但不适合需要动态 this 的对象方法或构造场景。
Promise 解决的是异步结果标准化和异步流程组合问题。它让异步任务可以链式调用、统一处理成功和失败、组合多个异步任务,减少传统回调嵌套和错误分散。
ES6 module 使用 import 和 export,是静态模块结构,便于编译期分析和构建优化;CommonJS 主要使用 require 和 module.exports,通常是运行时加载。ES6 module 的绑定是动态只读引用,而不是简单值拷贝。
Map 的键可以是任意类型,包含对象、函数和基本类型;普通对象的键主要是字符串或 Symbol。Map 还有明确的 size、has、delete 和可迭代能力,适合表达纯粹的键值映射。
Set 判断唯一性基于值相等规则。基本类型去重很直接,但对象按引用判断,两个结构相同但引用不同的对象仍会被视为不同成员。
Symbol 常用于创建唯一属性键,避免属性名冲突,也用于实现语言内置协议,例如迭代协议。它适合框架或库定义内部扩展点,但它不是绝对私有机制。
普通函数调用后会连续执行直到返回;generator 可以在 yield 处暂停,并在下一次调用时继续执行。它适合表达惰性序列、分阶段流程和可控的执行状态。