「ES6 是什么?它和 JavaScript 什么关系?」很多人能说个大概,却说不清「标准」和「实现」的区别,也答不上「为什么叫 ES 不叫 JS」。
一、是什么
1.1 ECMAScript 是标准,JavaScript 是实现
这是最核心的一句话:
- ECMAScript(简称 ES)是一份语言规范,由 ECMA International 的 TC39 委员会维护,标准编号 ECMA-262。它只规定纯语言部分:语法、类型、运算符、内置对象(
Object、Array、Promise……)的行为。 - JavaScript 是对这份规范的实现。我们写的 JS 代码,最终由某个 JS 引擎执行,比如 Chrome/Node 的 V8、Firefox 的 SpiderMonkey、Safari 的 JavaScriptCore。
打个比方:ECMAScript 是图纸,JavaScript 是按图纸盖出来的房子。
关系可以用一张图表示:
所以严格来说:
JavaScript = ECMAScript(语言核心) + 宿主环境 API(DOM/BOM 或 Node API)
Array.prototype.map 属于 ECMAScript;document.querySelector 属于浏览器 Web API;fs.readFile 属于 Node API。后两者不属于 ECMAScript 标准,它们由 W3C / WHATWG 或 Node 社区定义。
1.2 ES6 就是 ES2015
「ES6」是 ECMAScript 第 6 版的简称,正式名称为 ECMAScript 2015(ES2015),2015 年发布。这两个名字指的是同一个东西。
版本命名的演变:
| 版本 | 发布年份 | 备注 |
|---|---|---|
| ES3 | 1999 | 长期作为事实标准 |
| ES4 | 未发布 | 争议太大,夭折 |
| ES5 | 2009 | 加入严格模式、JSON、map/filter 等 |
| ES6 / ES2015 | 2015 | 史上最大更新 |
| ES2016 | 2016 | 起改为每年一发 |
| ES2017 | 2017 | 加入 async/await |
| ES2020 | 2020 | 可选链 ?.、空值合并 ?? |
| … | … | 按年份持续迭代 |
| ESNext | — | 泛指「下一版 / 最新提案」 |
ES6 带来的标志性特性:let / const、箭头函数、class、模板字符串、解构赋值、默认参数、剩余/展开运算符、import / export 模块、Promise、Symbol、Map / Set、生成器、迭代器。
日常说「用 ES6 写 JS」,准确意思是:用符合 ES2015 及之后规范的语法来写 JavaScript。
二、为什么
2.1 为什么需要一份标准
1995 年,Netscape 的 Brendan Eich 用十天时间做出了 JavaScript,为了蹭 Java 热度起了这个名字。随后微软在 IE 里搞了个几乎兼容但不完全一样的 JScript。各家浏览器各写各的,开发者苦不堪言——同一段代码在 A 浏览器能用,在 B 就报错。
于是 Netscape 把语言提交给 ECMA 做标准化,1997 年诞生了 ECMA-262。从此各大引擎都以它为基准,网页才能「写一次,到处运行」。
2.2 为什么叫 ECMAScript,不叫 JavaScript
因为 「JavaScript」是商标,当时归 Sun(后被 Oracle 收购)所有。ECMA 作为中立的标准组织,不能用一个受商标保护的名字来命名标准,于是采用了 ECMAScript。
所以不是「JS 改名成了 ES」,而是同一样东西,商标名和技术规范名不同。
2.3 为什么 ES6 是分水岭
ES5(2009)之后,标准曾长期停滞,ES4 又因各方分歧而流产。直到 2015 年 ES6 落地,一次性补齐了现代语言该有的语法糖和异步方案,JS 才真正从「玩具脚本语言」变成能撑起大型工程的语言。
此后 TC39 改用每年发布的节奏,用一个渐进流程管理提案:
| 阶段 | 名称 | 含义 |
|---|---|---|
| Stage 0 | Strawman | 初步想法 |
| Stage 1 | Proposal | 正式提案,确定方向 |
| Stage 2 | Draft | 语法基本定型 |
| Stage 3 | Candidate | 进入实现,等待反馈 |
| Stage 4 | Finished | 完成,纳入下一年标准 |
这就是为什么新特性(如装饰器)会先以「Stage 3 提案」出现——它还没进正式规范,但引擎和转译器可能已提前支持。
2.4 为什么要区分「标准」和「实现」
因为标准发布 ≠ 所有引擎都支持。规范是纸面约定,引擎实现有先后快慢:
- 新语法(如可选链)在旧浏览器里会直接语法报错;
- 新内置 API(如
Array.prototype.flat)在旧环境里是undefined。
正因为这种分离,才催生了 Babel、core-js 这些工具,也才有了「语法转译」和「API 补丁」两条不同的兼容路线。这就是下一节要讲的内容。
三、怎么做
知道了 ES 是标准、JS 是实现,实践中就要解决一个问题:如何让用新语法/新 API 写的代码,在旧引擎上也能跑?
关键是把问题拆成两类,用不同手段解决:
| 问题类型 | 例子 | 解决手段 |
|---|---|---|
| 语法不被支持 | 箭头函数、class、可选链 ?. | 转译(transpile),如 Babel |
| 内置 API 不存在 | Promise、Array.flat、Object.fromEntries | polyfill 打补丁,如 core-js |
3.1 语法降级:Babel 转译
Babel 把新语法改写成等价的老语法。例如:
// 输入(ES2015+)
const add = (a, b = 0) => a + b;
// 输出(转译后,可被旧引擎解析)
var add = function add(a, b) {
if (b === undefined) b = 0;
return a + b;
};
在 webpack 里用 babel-loader 接入:
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', { targets: '> 0.5%, last 2 versions, not dead' }],
],
},
},
},
],
},
};
3.2 API 补齐:core-js
转译只改语法,改不了「运行时不存在的函数」。Promise 在老浏览器里没有,Babel 不会替你实现,需要 core-js 注入:
// 手动引入(不推荐,体积大)
import 'core-js/stable';
// 推荐:交给 preset-env 按需注入
3.3 自动按目标环境处理:@babel/preset-env
@babel/preset-env 会根据你声明的目标浏览器,自动决定需要哪些语法转换和 polyfill:
// babel.config.js
module.exports = {
presets: [
['@babel/preset-env', {
// 目标环境:从 browserslist 读取,也可显式写
targets: '> 0.5%, last 2 versions, not dead',
// 按实际用到的 API 自动注入 polyfill
useBuiltIns: 'usage',
corejs: 3,
}],
],
};
targets 一般写在 package.json 或 .browserslistrc 的 browserslist 里,让 Babel、Autoprefixer 等工具共享同一份目标:
// package.json
{
"browserslist": ["> 0.5%", "last 2 versions", "not dead"]
}
3.4 目标不同,策略不同
目标越新,需要转译的东西越少,产物体积越小、性能越好。
- 做现代浏览器应用:可以把目标调新,甚至只发 ESM,减少 polyfill;
- 做组件库:通常用 Babel 只转译语法,把 polyfill 留给使用方决定;
- Node 项目:直接按
package.json的engines指定 Node 版本作为 target,无需考虑浏览器。
同样的思路也适用于 TypeScript,它通过 compilerOptions.target 和 lib 控制输出语法与可用的内置类型。
四、常见误区
- 「ES6 是 JavaScript 的第六版」:不严谨。ES6 是 ECMAScript 第 6 版,JavaScript 没有「版本」一说,它只是实现。
- 「ES6 就是新 JS 的全部」:ES6 特指 ES2015。可选链、
??、at()等属于后续版本(ES2020、ES2022……),统称「现代 JS」更准确。 - 「Babel 会补齐所有新 API」:不会。Babel 默认只管语法,API 要靠 core-js。
- 「浏览器支持 ES6 就能跑所有现代语法」:Web API 和语言规范各自演进,要分开查。
- 「ESNEXT 是 ES6 之后的下一个版本」:
ESNext是滚动概念,指「最新提案集合」,不是一个固定版本。
五、小结
- 是什么:ECMAScript 是标准(ECMA-262,TC39 维护),JavaScript 是它的实现;
JavaScript = ECMAScript + 宿主 API;ES6 就是 ES2015。 - 为什么:为终结浏览器碎片化而标准化;因商标问题取名 ECMAScript;ES6 是语言现代化的分水岭,此后每年迭代。
- 怎么做:区分「语法」与「API」两类兼容问题——语法用 Babel 转译,API 用 core-js 补丁,用
@babel/preset-env+ browserslist 按目标环境自动处理。
记住那个最关键的区分:规范定义「应该是什么样」,引擎决定「实际能跑什么」。理解了它,Babel、polyfill、browserslist 这些工具存在的意义就都顺理成章了。