设想你正在搬进一套崭新的公寓:旧房子里(ES5 时代的 JavaScript)的家具都能用,但格局老旧、收纳混乱;新房子(ES6 及之后)自带一整套更聪明的储物设计。这套新房里最出名的“四件法宝”是:解构赋值——从行李箱里精准抽出护照和充电器;展开运算符——把整箱杂物哗啦倒出来铺平;模块化——给每个房间分柜贴标签、按标签取物;Class 类语法——用一张户型图纸批量复制标准房间。本文请来一位经验丰富的收纳师,以搬新家的视角,把这套现代 JavaScript 语法的每个角落逐一盘点:先看新房的整体格局,再认识门锁(let/const)、抽屉(解构)、漏斗(展开)、标签(模板字符串)、折叠凳(箭头函数)、分区柜(模块化)和户型图(Class)。学完你会发现,曾经要写十行的操作,如今一行就能讲清楚,读别人代码时也不再一脸茫然。

岗位关联

对 AI 从业者而言,ES6 就是现代 JavaScript 的“普通话”:无论读开源模型代码、写前端可视化,还是为智能体工具链补界面,看懂这门语言,你才能无障碍地走进今天的 AI 工程世界。

目录
  1. 可视化演示
  2. ES6时代背景
  3. let与const声明
  4. 解构赋值
  5. 展开与剩余
  6. 模板字符串
  7. 箭头函数
  8. 模块化
  9. Class类语法
  10. AI 应用场景
  11. Python 演示
  12. 常见误区
  13. 面试题

可视化演示

展示ES6+核心语法特性:解构赋值(数组/对象提取)、ES Module模块化(export/import/Tree Shaking)、以及Class类语法(构造函数/方法/继承)。

本节配有交互动效(进入视口后自动加载,约 3KB)。

'+captions.destruct+'

ES6时代背景

收纳师第一天进门,没有急着拆箱,而是先掏出一张户型图,带小李把新旧两套房做了整体对比:老房(ES5 时代的 JavaScript)东西都能用,但格局封闭、收纳混乱;新房(ES6)自带一整套更聪明的储物设计。ES6 是 ECMAScript 2015 的俗称,是 JavaScript 史上最大的一次版本跃迁,相当于把年久失修的老楼整体精装改造了一遍。

既然是换代,就绕不开“兼容”问题。一些老浏览器只认识旧房的老开关(ES5 语法),碰到新房的智能面板(ES6 语法)会直接罢工。于是 Babel 诞生了——它像一个万能转接器,把 ES6 新语法实时“翻译”成老浏览器能听懂的 ES5 语法。工程师写完新代码,交给 Babel 转译一遍,新旧设备都能流畅运行。

如今现代浏览器和 Node.js 已原生支持绝大部分 ES6+ 特性,转译不再是日常必需,但理解版本演进的逻辑仍然重要。ES6 带来的核心变化包括:let/const 块级作用域、解构赋值、展开运算符、模板字符串、箭头函数、模块化、Class……它们共同把 JavaScript 从“网页小脚本”升级成可以支撑大型工程的现代语言。

ES6≡ECMAScript 2015⇒Modern JavaScript\displaystyle \text{ES6} \equiv \text{ECMAScript 2015} \Rightarrow \text{Modern JavaScript}

1. ES6 名称解析:ES6 是 ECMAScript 6 的缩写,代表第六版语言标准。

ES6=ECMAScript 6\displaystyle \text{ES6} = \text{ECMAScript 6}

2. 版本年份映射:官方按年份命名为 ECMAScript 2015,两者指向同一版本。

ES6=ECMAScript 2015\displaystyle \text{ES6} = \text{ECMAScript 2015}

3. 现代基座定位:从这一版起语言每年迭代,现代框架与工具全部以此为地基。

ES6⇒Modern JS Foundation\displaystyle \text{ES6} \Rightarrow \text{Modern JS Foundation}

户型图看完,小李心里有了底。接下来的每一件收纳工作,都在这张“新版户型图”的框架下展开——先认识新房的隔断和门锁,再谈怎么把旧家具一件件安置妥当。

let与const声明

收纳师带小李参观的第一个设计,是每个房间都装了独立的门和锁。ES5 时代的 var 就像没有隔断的大通铺,变量在函数里到处乱窜,一不小心就互相覆盖;ES6 的 let 和 const 则给变量划定了严格的活动范围——最近的这对花括号 {},就是它的房间,关上门谁也别想乱跑。

let 和 const 的区别,好比“可以随意换家具的房间”和“不能动结构的承重墙”。let 声明的变量可以重新赋值,适合计数、开关等会变化的量;const 声明的常量绑定后不能再改变,适合配置项、固定引用,写错了会立刻抛错。此外还有“暂时性死区”(TDZ):变量在声明语句之前被访问会直接报错,不再像 var 那样提着灯笼提前飘上去、给你一个 undefined。

实际开发有一条默认纪律:优先 const,只有确定需要重新赋值时才改用 let。这样读代码的人一眼就能区分“这个值不该变”和“这个值会变”,很多隐藏 bug 在写代码阶段就被拦下来了。

let x=1; x=2; ✓const y=2; y=3; ×\displaystyle \text{let } x = 1;\ x = 2;\ \checkmark \qquad \text{const } y = 2;\ y = 3;\ \times

1. let 可重赋值:用 let 声明后,后续可以对变量重新赋值,适合会变化的状态。

let x=1;x=2;✓\displaystyle \text{let } x = 1;\quad x = 2;\quad \checkmark

2. const 不可重绑:const 声明后重新赋值会直接抛错,适合固定配置与引用。

const y=2;y=3;×\displaystyle \text{const } y = 2;\quad y = 3;\quad \times

3. 块级作用域边界:let/const 只在最近的一对花括号内有效,括号外自动失效。

{let x=1;}⇒块外访问 x 报错\displaystyle \{ \text{let } x = 1; \} \Rightarrow \text{块外访问 } x \text{ 报错}

有了门和锁,小李的东西再也不会半夜串门了。收纳师顺手拉开行李箱的侧边拉链,掏出第一件法宝——解构赋值。

解构赋值

行李箱摊开,收纳师没有把衣服一件件拎出来翻找,而是直接拉开侧边袋,精准抽出“护照”和“充电器”。解构赋值干的就是这件事:不再通过“对象。属性”一层层凿墙取物,而是把数据结构整体拆开,按清单一次性取出想要的成员。

数组解构看位置:const [a, b] = arr 把第 0、1 个元素分别装进 a、b;配合剩余参数还能写 const [first, ...rest] = arr,剩下的一次收走。对象解构看键名:const { name, age } = user,等价于同时执行 const name = user.name 和 const age = user.age;想换个变量名,写 { name: userName } 即可,像给取出的东西换个更顺手的名字。

解构还有两个实用配件:一是默认值,{ name = '匿名' } = data 在字段缺失时自动兜底;二是嵌套解构,能从 data.a.b.c 这种深层对象里一步到位。最经典的妙用是变量交换——[a, b] = [b, a] 一行完成,从此告别 temp 临时变量。

{name,age}=user\displaystyle \{ \text{name}, \text{age} \} = \text{user}

1. 花括号清单:左侧花括号里列出想一次性取出的字段名。

{name,age}\displaystyle \{ \text{name}, \text{age} \}

2. 等号右侧来源:右端是被解构的对象,相当于打开包裹。

=user\displaystyle = \text{user}

3. 自动声明变量:解构成功后就地声明 name、age 两个变量并完成赋值。

name=user.name;age=user.age\displaystyle \text{name} = \text{user.name};\quad \text{age} = \text{user.age}

小李看着行李箱被拆成一个个小抽屉,直呼过瘾。收纳师提醒:别高兴太早,接下来这件法宝跟它长得一模一样,方向却完全相反。

展开与剩余

如果说解构是把大箱子拆成小抽屉,展开运算符(……)就是把抽屉里的东西哗啦一下全倒出来摊平。[...arr] 在数组字面量中把每个元素拆成独立项;{...obj} 在对象字面量中把每个键值对复制进新对象。合并两个数组、复制一份配置对象,都靠这一招。

展开还能用在函数调用:Math.max(...nums) 把数组拆成一个个参数传进去,等价于把 nums[0]、nums[1]……依次写进括号。而它形影不离的“反面”是剩余参数(rest):同样三个点,写在函数参数列表里时角色反转——把调用方传来的零散参数收拢成一个数组。function logAll(...args),调用 logAll(1, 2, 3) 时 args 就是 [1, 2, 3]。

判断口诀只有八个字:输出展开,输入收拢。三个点出现在“输出位置”(函数调用、数组/对象字面量)就是展开,负责拆散;出现在“输入位置”(函数形参、解构左侧)就是剩余,负责聚拢。同一个漏斗,倒出来和装进去方向相反。

Math.max(...nums)f(...args)\displaystyle \text{Math.max}(...\text{nums}) \qquad f(...\text{args})

1. 调用处展开:Math.max(……nums) 把数组 nums 拆成多个独立参数传入。

Math.max(...nums)≡Math.max(n1, n2, n3)\displaystyle \text{Math.max}(...\text{nums}) \equiv \text{Math.max}(n_1,\ n_2,\ n_3)

2. 形参处收拢:f(……args) 把调用方传入的所有参数收集进 args 数组。

f(...args)⇒args=[a1, a2, a3]\displaystyle f(...\text{args}) \Rightarrow \text{args} = [a_1,\ a_2,\ a_3]

3. 位置决定身份:同样三个点,输出端是展开,输入端是剩余。

... 在输出端拆散vs... 在输入端聚拢\displaystyle ...\ \text{在输出端拆散} \quad \text{vs} \quad ...\ \text{在输入端聚拢}

小李拿来自己手头的数据练了练,合并列表、复制配置,一行搞定。收纳师接着抽出第二件法宝——模板字符串。

模板字符串

老办法拼接字符串,像做手工标签:先裁纸、再涂胶、贴上去还得担心贴歪。ES6 的模板字符串就是“自带打印功能的预制标签”:用反引号(`)包住整段文字,想插变量直接在 ${} 里写,字面长什么样,结果就是什么样。

它最香的能力有两个。一是变量插值:Hello, ${name}! 里的花括号可以放任意表达式,比如 ${a + b}、${user.name},甚至套一个模板字符串,做到“标签里还有标签”。二是多行文本:反引号内直接换行,写 HTML 片段、拼 SQL、生成报告时,再不用堆一串 '\n' 和加号。

Hello, ${name}!⇒Hello, Alice!\displaystyle \text{Hello, }\$\{name\}\text{!} \Rightarrow \text{Hello, Alice!}

1. 反引号作边界:模板字符串用反引号代替普通引号,反引号内的内容整体视为字符串。

0\displaystyle \text{0}

2. 花括号插值:${} 内写变量或表达式,计算后的值直接拼进字符串。

${name}⇒value of name\displaystyle \$\{name\} \Rightarrow \text{value of name}

3. 整体求值输出:占位符全部替换后,得到最终的完整字符串。

Hello, Alice!\displaystyle \text{Hello, Alice!}

标签纸备好,收纳师开始处理最耗体力的重复劳动——每个柜子都要写样板代码。能不能做把折叠凳,随取随用?于是箭头函数登场了。

箭头函数

箭头函数就是那把折叠凳:平时不占地方,需要时一打开就能坐。对比一下:function(a, b) { return a + b; } 和 (a, b) => a + b,后者把 function、return、大括号全省了,只剩参数、箭头、表达式,一眼看懂。

但折叠凳有一个鲜明特征:朝向固定。普通函数的 this 像活动转椅,谁调用就指谁;箭头函数不创建自己的 this,而是沿作用域链“继承”定义处外层函数的 this。这带来两个后果:回调函数里不再需要 const self = this 的补丁写法;同时箭头函数不能当构造函数、也没有自己的 arguments。

适合箭头函数的场景:短小的回调、无需动态 this 的纯计算函数、数组的 map/filter/reduce。适合普通函数的场景:需要自己绑定 this 的事件监听、需要 arguments、需要 new 的构造函数。折叠凳和转椅各有各的座位。

(x, y)⇒x+y\displaystyle (x,\ y) \Rightarrow x + y

1. 参数列表:左边括号里声明形参,只有一个参数时可省略括号。

(x, y)\displaystyle (x,\ y)

2. 箭头连接:=> 把参数和函数体连接起来,读作“输入什么、产出什么”。

⇒\displaystyle \Rightarrow

3. 隐式返回:右侧表达式的结果自动作为返回值,等价于 return。

(x, y)⇒x+y≡function(x, y) {return x+y; }\displaystyle (x,\ y) \Rightarrow x + y \equiv \text{function}(x,\ y)\ \{ \text{return } x + y;\ \}

折叠凳收进柜子,最后一项大工程开始了:给整套房子做分区、贴标签,让每件物品都有自己明确的去处——这正是模块化的思路。

模块化

家里东西一旦多了,最怕满屋乱放。模块化就是给屋子划分功能区:厨房归厨房、书房归书房,每个区域一个独立文件,各管各的一摊。ES6 用 export 和 import 两个关键字,为模块之间搭起一座有标签的桥。

export 是“贴出货标签”:把函数、变量、类标记为对外可见。可以单个导出(export const add = ……),也可以在文件末尾统一导出(export { add, sub }),还能用 export default 导出一个默认值,让导入方自己起名字来接。import 是“按标签取货”:import { add } from './math.js' 取命名成员;import add from './math.js' 接默认值。

模块化的价值在于“显式依赖”:谁依赖谁,扫一眼 import 就清楚;作用域相互隔离,再不用靠全局变量传话;配合打包工具还能按需加载,减少首屏体积。如今的工程化项目里,几乎每个文件都是一个模块——组件、工具、配置,各归各柜。

export {add,sub}⇒import {add} from ’./math.js’\displaystyle \text{export } \{ \text{add}, \text{sub} \} \quad \Rightarrow \quad \text{import } \{ \text{add} \} \text{ from } \text{'./math.js'}

1. 导出贴标:export {} 把模块内选中的成员公开出去,供外部取用。

export {add,sub}\displaystyle \text{export } \{ \text{add}, \text{sub} \}

2. 导入取货:import {} from 后面跟文件路径,按名字取出所需成员。

import {add} from ’./math.js’\displaystyle \text{import } \{ \text{add} \} \text{ from } \text{'./math.js'}

3. 依赖显性化:导出与导入一一对应,模块之间的依赖关系一目了然。

export↔import\displaystyle \text{export} \leftrightarrow \text{import}

房间分区完毕,物品各就各位。收纳师拿出最后一张王牌——户型图纸,让同款房间可以批量施工。

Class类语法

如果每个房间都单独设计家具摆放,神仙也累死。Class 就是一张户型图纸:定义一次,便能批量建造结构相同的实例。class Animal {} 里的 constructor 是施工队,负责搭骨架、初始化属性;其余的方法则是每个房间的统一标配,负责行为。

继承是“在图纸上加盖侧翼”:class Dog extends Animal 之后,Dog 自动拥有 Animal 的属性和方法,还能在自己的 constructor 里先调用 super(……) 复用父类的施工队,再加独家设计。static 修饰的成员则是整栋楼的公共设施——挂在类身上,不归任何一个单间,直接用类名调用,比如 Animal.create()。

本质上 class 是“构造函数 + 原型链”的语法糖,但它让面向对象的意图一眼可辨。在 AI 工程里,用它描述领域模型非常顺手:Dataset、Model、Trainer 各成一个类,继承关系就是模型家族的族谱。

class Dog extends Animal {⋯ }\displaystyle \text{class } \text{Dog} \ \text{extends} \ \text{Animal} \ \{ \cdots \}

1. 定义类:class 后跟类名,大括号里写构造函数和方法。

class Dog {⋯ }\displaystyle \text{class } \text{Dog} \ \{ \cdots \}

2. 继承扩展:extends 让 Dog 自动拥有 Animal 的全部属性和方法。

class Dog extends Animal\displaystyle \text{class } \text{Dog} \ \text{extends} \ \text{Animal}

3. 父类施工:子类构造函数中先调用 super(……) 执行父类初始化。

super(name)⇒先执行父类的 constructor\displaystyle \text{super}(\text{name}) \Rightarrow \text{先执行父类的 constructor}

四件法宝全部到位。小李站在焕然一新的家里,看着拆好的抽屉、贴好的标签、分区明确的柜子和按图纸批量建成的房间,终于领悟:ES6+ 不是多了几个花哨玩具,而是一套让代码更贴近人类直觉的“收纳哲学”——先想清楚物品该放哪个柜子,再动手写代码。

AI 应用场景

场景一:AI 对话组件的参数解构

阶段:AI 应用前端开发

解决的问题:传统方式要从多层配置对象里逐个取值,代码冗长且容易写错层数;用对象解构 + 默认值,组件入参一次取齐,缺省配置自动兜底,代码可读性和健壮性同时提升。

具体案例:开发一个 LLM 对话组件,接收 config = { model, temperature, maxTokens }。组件开头写 const { model = 'gpt-4o', temperature = 0.7, ...rest } = config,一行完成默认值设置与剩余参数透传,后续 API 调用直接使用 model 与 temperature。

场景二:大模型返回数据的归一化解析

阶段:数据预处理

解决的问题:LLM 接口返回的 JSON 往往嵌套四五层(choices → message → content),老写法要连写四五层判空,每一步都可能炸;用嵌套解构 + 默认值,一次解出目标字段,数据结构变化时只需改一处。

具体案例:解析 OpenAI 风格响应:const { choices: [{ message: { content = '' } }] } = response,直接拿到对话正文;再用 { ...response, usage } 展开复制,产出标准化记录对象。

场景三:AI 训练平台前端的模块化改造

阶段:模型训练基础设施

解决的问题:单文件脚本越写越长,全局变量互相打架;用 ES6 模块把 API 封装、图表渲染、任务状态管理拆成独立模块,export/import 显式声明依赖,多人协作不再互相踩脚。

具体案例:将一个 2000 行的训练监控页面拆成 api.js(请求封装)、metrics.js(指标计算)、charts.js(图表绘制)、main.js(入口组装),各自 export 公开接口,main.js 按需 import 组装,出问题定位到文件即可。

Python 演示

python53 行
"""Understand ES6 features with equivalent Python patterns."""  def es6_destruct(obj, keys):    """Simulate ES6 object destructuring by key names."""    result = {}    for key in keys:        result[key] = obj.get(key, "default")  # 相当于解构默认值    return result  def spread_merge(*arrays):    """Simulate ES6 spread operator that flattens and merges arrays."""    merged = []    for arr in arrays:        merged.extend(arr)  # extend 相当于 [...arr]    return merged  class Animal:    """Simulate ES6 class with constructor and instance method."""     def __init__(self, name):        self.name = name     def speak(self):        return f"{self.name} makes a sound"  class Dog(Animal):    """Simulate ES6 extends inheritance."""     def speak(self):        return f"{self.name} barks"  def main():    user = {"name": "Alice", "age": 18, "city": "Shanghai"}    picked = es6_destruct(user, ["name", "age"])    print("destruct result:", picked)     nums = spread_merge([1, 2], [3, 4], [5])    print("spread result:", nums)     dog = Dog("Wangcai")    print("class inheritance:", dog.speak())     first, second, *rest = [1, 2, 3, 4, 5]    print(f"array destruct: first={first}, second={second}, rest={rest}")  if __name__ == "__main__":    main()
目的:用 Python 的既有语法一一对应 ES6 的解构、展开、Class 继承三大特性,帮学习者借助熟悉语言快速建立直觉。
  1. def es6_destruct(obj, keys):函数模拟 ES6 对象解构:传入原始字典和“取件清单” keys。
  2. result[key] = obj.get(key, "default")字典的 get 自带默认值,对应解构语法里的默认值兜底。
  3. def spread_merge(*arrays):星号参数就是 Python 版剩余参数,把多个传入数组收进一个元组。
  4. merged.extend(arr)extend 把数组元素逐个展开追加,等价于 ES6 的 [……] 展开合并。
  5. class Dog(Animal):类名后括号写父类即继承,对应 ES6 的 extends 关键字。
  6. first, second, *rest = [1, 2, 3, 4, 5]Python 原生支持“解构+rest”,一行完成取首、取次、收余。
  7. print(f"array destruct: first={first}, second={second}, rest={rest}")f-string 嵌入表达式,对应模板字符串的 ${} 插值。

常见误区

误区const 声明的数组和对象是锁死的,push、改属性、改下标都会报错,所以保存复杂数据时千万别用 const。

正解记住一句话:const 锁的是“绑定关系”,不是“内部内容”。const arr = [1] 之后,arr = [2] 会报错,但 arr.push(2)、arr[0] = 9 都合法。就像把整个箱子打包托运不行,但往箱子里加衣服完全允许。想彻底锁死内容,需要额外调用 Object.freeze。

为何常见:“常量”这个词容易让人联想到“不可变”,初学者把“不能重新赋值”误读成“内容不能变”。

误区解构赋值是高端优化技巧,用了它代码运行速度会变快,是性能利器。

正解解构只是语法糖,转译后的代码就是普通属性访问,性能与传统写法几乎无差别,某些老环境下甚至略慢。它的核心价值是“可读性与安全性”:输入结构清晰、默认值兜底、减少重复取值。真正的性能优化要在缓存、算法层面发力。

为何常见:新特性常被包装成“更高级 = 更高效”,加上转译工具的存在,容易让人误以为语法糖自带性能红利。

误区箭头函数里没有 this,所以箭头函数绝对不能访问 this,否则会报错。

正解箭头函数不是“没有 this”,而是“不创建自己的 this”。它访问 this 时,会沿作用域链找到定义时外层函数的 this,所以这个 this 是“借来的”。它的代价是没有自己的 arguments、不能当构造函数。折叠凳不是没有朝向,而是朝向固定在定义处。

为何常见:教程常说“箭头函数不绑定 this”,“不绑定”被听成“不存在”,其实意思是“不自己造一个,用祖传的”。

误区把代码拆成几十个小文件,再用 script 标签挨个引入,这就是 ES6 模块化了。

正解真正的模块化核心是“显式依赖 + 作用域隔离”。文件再碎,如果通过全局变量互相通信、靠 script 加载顺序约定,那还是“多文件的全局脚本”。ES6 模块要求每个文件独立作用域,export 声明对外接口,import 声明依赖来源,依赖图一目了然。分柜子 + 贴标签 + 按标签取物,缺一不可。

为何常见:大家对模块化的直观印象就是“拆分文件”,忽略了模块是“带边界的封装单元”而非单纯的文件个数。

误区三个点 …… 就是展开运算符,把任何东西都摊开,函数参数里也能展开。

正解同样的三个点有两个身份,判断标准是“位置”:在“输出端”(数组/对象字面量、函数调用)叫展开运算符,负责拆散;在“输入端”(函数形参、解构赋值左侧)叫剩余参数,负责聚拢。Math.max(……arr) 是展开,function f(……args) 里的 ……args 是剩余。同一个漏斗,倒出来和装进去方向相反。

为何常见:教程常分别讲“展开运算符”和“剩余参数”,且都写着 ……,初学者记住符号却忘了记位置,就把两者混为一谈。

面试题

Q1. 关于 let 和 const,下列说法正确的是?

单选题 · easy

  • A.var 声明的变量有块级作用域
  • B.let 声明的变量可以重新赋值← 正确答案
  • C.const 声明的数组可以重新赋值
  • D.以上三种说法都完全正确

答案:B。var 是函数作用域而非块级作用域,A 错;const 锁死重新赋值,C 错;D 因前两项有错而不成立。let 像可换家具的房间,const 则是不能拆的承重墙。

Q2. 模板字符串中插入变量的正确写法是?

单选题 · easy

  • A.使用 %name% 形式占位
  • B.使用 ${name} 形式占位← 正确答案
  • C.使用 #{name} 形式占位
  • D.使用 {{name}} 形式占位

答案:B。模板字符串用反引号包裹,${} 是唯一占位语法,里面可放任意表达式。A 是 Python 老式格式化,C 是注释符号的联想,D 是 Mustache 模板语法,都不是 JS 模板字符串。

Q3. 下列哪个关键字用于导出模块成员?

单选题 · easy

  • A.使用 import 关键字导出
  • B.使用 export 关键字导出← 正确答案
  • C.使用 require 关键字导出
  • D.使用 from 关键字导出

答案:B。export 负责把成员“贴标签呈出”,import 负责按标签取用。require 是 CommonJS 的导入方式,from 仅用于 import 语句中指定路径,均不能导出。

Q4. 执行 const [a, b, ...rest] = [1, 2, 3, 4] 后,rest 的值是?

单选题 · mid

  • A.得到数组 [3, 4]← 正确答案
  • B.得到数组 [3]
  • C.得到数字 3
  • D.得到数字 4

答案:A。解构按位置取值:a=1、b=2,……rest 收集剩余全部元素组成新数组,故为 [3, 4]。B 漏了 4,C/D 混淆了数组与数字。rest 就是那个把剩下东西全收走的收纳盒。

Q5. 下列哪个操作会让 const 声明的数组 arr 报错?

单选题 · mid

  • A.调用 arr.push 添加元素
  • B.直接修改 arr[0] 的值
  • C.将 arr 重新赋值为新数组← 正确答案
  • D.把 arr 的 length 设为 0

答案:C。const 锁的是绑定关系。push、下标赋值、改 length 都是修改内部内容,合法;只有重新赋值触发 TypeError。整个箱子不能换,但箱内东西可以随便整理。

Q6. 在 let 声明语句之前访问该变量,会发生什么?

单选题 · mid

  • A.自动得到 undefined 值
  • B.直接抛出 ReferenceError← 正确答案
  • C.拿到全局同名变量的值
  • D.静默失败并返回 null

答案:B。这是暂时性死区(TDZ)——在块内声明前访问,引擎直接抛错。var 才有“提升 + undefined”的老毛病;TDZ 相当于给变量装上门禁,没开门前谁都不能进。

Q7. 关于普通函数与箭头函数的 this,下列说法正确的是?

单选题 · hard

  • A.普通函数的 this 始终固定
  • B.箭头函数继承外层 this← 正确答案
  • C.箭头函数有自己的 arguments
  • D.两种函数的 this 完全一样

答案:B。箭头函数不创建自身 this,运行时沿作用域链继承外层 this,B 正确。普通函数 this 由调用方式决定,A 错;箭头函数没有 arguments,C 错;D 显然错误。折叠凳朝向固定,转椅看人坐。

Q8. 下列场景中,适合使用展开运算符的有哪些?

多选题 · mid

  • A.合并多个数组生成新数组← 正确答案
  • B.将数组拆成多个参数调用← 正确答案
  • C.复制对象并追加新的属性← 正确答案
  • D.声明一个块级作用域变量

答案:ABC。[……] 与 {……} 展开用于合并/复制,f(……arr) 用于拆参数,都是展开的典型场景。声明块级变量是 let/const 的职责,与展开无关。倒出来的活儿归漏斗,砌墙是隔断的事。

Q9. 关于 Class 继承中的 super,下列说法正确的是?

多选题 · mid

  • A.子类构造里先调用 super()← 正确答案
  • B.super() 调用父类构造函数← 正确答案
  • C.子类构造里先 super 才能用 this← 正确答案
  • D.super 与 this 毫无关联

答案:ABC。子类构造在 super() 之前不能访问 this,这是规则;super() 本质是调用父类构造函数完成“基础施工”。D 错在 super 与 this 的生命周期息息相关,子类正是通过 super 完成 this 的初始化。

Q10. 下列关于对象展开 { ...obj, c: 3 } 的说法,正确的有?

多选题 · hard

  • A.展开复制不会改动原对象← 正确答案
  • B.assign 调用会改原对象← 正确答案
  • C.后者属性覆盖前者同名属性← 正确答案
  • D.展开能复制原型上的方法

答案:ABC。对象展开只复制自身可枚举属性,不复制原型链方法,D 错。展开生成全新对象不改原对象;Object.assign 的第一个参数会被就地修改;同名属性后者覆盖前者,类似新标签压旧标签。