0. 前言
1、名称由来
说起JavaScript相关的历史,还得追溯到计算机及其编程语言诞生之初的那段时间。
在1959年,欧洲的计算机产业已经出现蓬勃发展的迹象了,现代意义的计算机也越来越多地开始在科研机构,政府等社会场所被使用,使用的人多了,计算机操作技术实现标准化就被推上了日程,例如编程,以及输入和输出代码规范等。
行业标准对于行业发展的重要性不言而喻。为了协调标准制定工作,欧洲数据处理领域历史最悠久的公司(Compagnie des Machines Bull、IBM World Trade Europe Corporation 和 International Computers and Tabulators Limited)的负责人向所有已知的计算机发出了联名信。欧洲制造商邀请这些公司派代表参加会议。这次会议于 1960 年 4 月 27 日在布鲁塞尔举行;决定成立一个制造商协会,称为欧洲计算机制造商协会(European Computer Manufacturers Association
)或简称 ECMA
,并提名了一个委员会来准备该协会的成立并制定章程和规则。随后的 1961 年 5 月 17 日,协会正式成立。
这便是ECMA
这个名字的来源。
ECMA
走向全球后,在1994 年更名为:Ecma International
。其商标“ECMA”由于历史原因被保留了下来,沿用至今。
2、ECMA-262标准,梦开始的地方
EMCA
标准容纳了大量通信、计算机及其相关行业的标准规范。我们找一下本文的主角。
在官网中扒一张图:
可以看到,第262号标准又叫ECMAScript
,是ECMA定义的一套脚本语言编程规范。
ECMAScript 通常缩写为 ES。
正是因为有这个标准的出现,才有了 JavaScript 的蓬勃发展!感谢大佬赏饭吃!!
ECMA-262是一种规范(官网中可以查到各个版本的PDF提案),自然会有满足这种规范的具体的语言被创造出来。最著名的就是JavaScript (现在商标属于Oracle),在1995年由Netscape公司的Brendan Eich,在Netscape导航者浏览器上首次设计实现而成。因为Netscape与Sun合作,Netscape管理层希望它外观看起来像Java,因此取名为JavaScript。但实际上它的语法风格与Self及Scheme较为接近。其设计思路如下:
- 借鉴 C 语言的基本语法。
- 借鉴 Java 语言的数据类型和内存管理。
- 借鉴 Scheme 语言,将函数提升到“第一等公民”(first class)的地位。
- 借鉴 Self 语言,使用基于原型(prototype)的继承机制。
JavaScript的是一门动态弱类型、基于原型的一种解释性计算机脚本语言。
在没有 JavaScript 的年代,一些简单的表单验证(如常见的 密码复杂度验证)都是由服务端负责的,而且当时的网络速度是非常慢的,这种验证方式会导致网站的响应速度变得更慢,于是便有了这门语言的诞生。
其次还有微软自研的一套语言,叫JScript
,其声称完全遵循ECMA-262
规范。下面列举一下二者的区别:
特点 | JavaScript | JScript |
---|---|---|
运行平台 | 客户端,服务端 | 客户端,服务端 |
解释环境 | Netscape Livewire、V8(Chrome + node)、TraceMonkey等 | WSH、ASP、.Net |
支持浏览器 | 所有主流浏览器 | 主要是IE3.0+ |
出现时间 | 比较早,甚至早于ECMA-262 | VBScript竞争失败后的产物 |
1. JavaScript 1.0 (96年)
发布于1996年,适用于Netscape Navigator2.0浏览器,初步确定了完整的JavaScript的三个构成要素:
-
ECMAScript(姑且这么叫,1.3版本才正式完全兼容ECMA规范)。JavaScript 的核心部分,它规定了 JavaScript 的语法、类型、语句、 关键字、保留字、运算符和对象。
-
DOM(Document Object Model)。通过把整个页面映射成一个树结构的文档,提供 了一套可以访问 HTML 和 XML(eXtensible Markup Language)节点的 API(Application Programming Interface),开发者可以利用它轻松地删除、添加、替换或修改任何节点。
-
BOM(Browser Object Model)。提供了访问浏览器窗口的方法,开发者可以控制浏 览器窗口进行一些诸如移动窗口之类的操作。BOM 部分的有关定义,零散分布在各种标 准中,主要位于 HTML 标准。其他比如 WebRTC 标准等内也定义了一些接口。
1.0里有一些以后很少用的冷门特性。比如函数不但有call,apply方法,还有name和length,还有
arguments
,caller
(在1.2版移除);Arguments是个对象而不是数组;对象可以用数字下标访问属性;没有===的强类型判定;对不支持script标签的浏览器做特殊注释兼容等。
2. JavaScript 1.1(96年)
发布于1996年下半年,适配了Netscape Navigator3.0 和 IE3.0。完善数组对象和方法。
3. JavaScript 1.2(97年)
发布于1997年,适配了Netscape Navigator4.0,但不支持 IE 环境。增加了===的判断方式。
4. JavaScript 1.3 与 ECMAScript 1/2(98年)
发布于1998年。前边说JavaScript出现时间早于ECMA-262,指的就是这个版本的事情。这一年,ECMA标准化了JavaScript1.1的基本特性,同时JavaScript也升级到1.3,并添加了一些新特性(没有标准化switch语句和正则表达式)。至此,JavaScript才逐步遵循ECMA规范进行升级换代。
ECMAScript 2标准是对v1的一个维护版本,只添加了说明,也在同一年发布。
5. JavaScript 1.4(99年)
适用于Netscape server的专用版本。
6. JavaScript 1.5 与 ECMAScript 3(00年)
发布于2000年底,开始支持 Mozilla Firefox 和 Opera 浏览器,并标准化了switch语句、异常处理( try/catch)和正则表达式。
7. JavaScript 1.6(05年)
2005年发布。逐步增加了数组的扩展方法、增加了Array和String泛型、支持XML扩展(E4X)。开始支持Safari 3.0。同年,Ajax技术发布。
新增的数组扩展如下(部分扩展在ES5中逐步规范化):
新增扩展 | 描述 |
---|---|
indexOf() | 返回指定项首次出现的index |
lastIndexOf() | 返回指定项最后一次出现的index |
every() | 在数组中的每一个项运行一个函数。如方法可用遍历每一个项都返回的true,则返回true |
filter() | 在数组中的每一个项运行一个函数,并将这个函数返回true的项作为一个数组返回 |
forEach() | 在数组中的每一个项运行一个函数 |
map() | 在数组中的每一个项运行一个函数,并将所有的结果作为数组返回 |
some() | 在数组中的每一个项运行一个函数,如果这个方法访问的任何一个项返回true,则返回true |
泛型(非TS的那种)使用:
function isLetter(character) {
return (character >= "a" && character <= "z");
}
// 在字符串中使用数组的方法
if (Array.every(str, isLetter))
alert("The string '" + str + "' contains only letters!");
8. JavaScript 1.7(07年)
2007年发布。新特性如下:
- 学习了Python的语法,并引入了Pythonic generators(generator函数)、
- 引入解构赋值
- Iterators(迭代器)
- let 声明
这仍然是ES3的内容,不要认为是ES6才有的。
- 开始支持 Google Chrome1.0。
9. ECMAScript 4
2008年被废弃的标准。死亡原因
10. JavaScript 1.8 (08-10年) 与 ECMAScript 5 (09年)
包含很多patch版本。
2008年发布 1.8.0
新特性:
新特性 | 描述 |
---|---|
表达式闭包 | 见下面代码 |
生成器表达式 | 个新特性使你可以使用类似数组概念的语法来创建一个独立的生成器句柄,见代码 |
表达式闭包:
// JavaScript 1.7 以及更老的版本:
function(x) { return x * x; }
// JavaScript 1.8:
function(x) x * x;
生成器表达式
// 使用1
let it = (i + 3 for (i in someObj));
try {
while (true) {
document.write(it.next() + "<br>\n");
}
} catch (err if err instanceof StopIteration) {
document.write("End of record.<br>\n");
}
// 使用2
function handleResults( results ) {
for ( let i in results )
...
}
handleResults( i for ( i in obj ) if ( i > 3 ) );
同年发布 1.8.1
新特性:
新特性 | 描述 |
---|---|
Object.getPrototypeOf() | 返回指定对象的原型对象,见示例 |
Firefox 3.5 原生支持 JSON | - |
String对象新增方法 | String 增加 trim() , trimLeft() , 和 trimRight() |
其他一些次要更新 | - |
2009年发布 1.8.2
一些次要更新。
2010年发布 1.8.5,并遵循ECMAScript 5 (2009年发布)规范
一次大的更新,将之前JS各个版本的方法进行统一汇总整理,并新加入一批API,一起写入规范,使得ES和JS的贴合程度更深。
新特性 | 描述 |
---|---|
添加了“严格模式” | use 'strict' |
规范this关键字 | - |
对 JSON支持 | - |
字符串方法 | 见下列表 |
数组方法 | 见下列表 |
各种对象方法 | 见下列表 |
- 严格模式
- 不允许省略var定义变量
- 不允许函数形参同名
- 不允许普通函数中的this代表window
- this规范
- 全局this、普通函数的this 、 自调用函数的this都代表window
- 事件函数中的this代表事件源
- 对象方法中的this代表当前对象
- 普通函数被谁调用,函数中this指向谁
- ES5 新增数组方法/规范(部分方法js1.6中已经有过实现,在ES5正式写入规范)
- Array.isArray()
- Array.forEach()
- Array.map()
- Array.filter()
- Array.reduce()
- Array.reduceRight()
- Array.every()
- Array.some()
- Array.indexOf()
- Array.lastIndexOf()
- ES5新增字符串规范
- String.trim()
- charAt()
- 可对字符串进行属性访问
- 允许多行字符串,每一行使用反斜杠结尾
- String.fromCharCode() 用于从 Unicode 码点返回对应字符
- ES5 新增各种对象方法
- JSON.parse()
- JSON.stringify()
- Date.now()
- 属性 Getter 和 Setter
- 新的对象属性和方法规范,见下表
新的对象属性和方法规范 | 描述 |
---|---|
Object.defineProperty(object, property, descriptor) | 添加或更改对象属性 |
Object.defineProperties(object, descriptors) | 添加或更改多个对象属性 |
Object.getOwnPropertyDescriptor(object, property) | 访问属性 |
Object.getOwnPropertyNames(object) | 将所有属性作为数组返回 |
Object.keys(object) | 将可枚举属性作为数组返回 |
Object.hasOwnProperty(key) | 是否为对象自身属性 |
Object.getPrototypeOf(object) | 获取对象原型 |
Object.preventExtensions(object) | 防止向对象添加新的属性 |
Object.isExtensible(object) | 如果可以将属性添加到对象,则返回 true |
Object.seal(object) | 防止更改对象属性(而不是值)对象不可以新增属性和删除 但是可以修改 |
Object.isSealed(object) | 如果对象被密封,则返回 true |
Object.freeze(object) | 防止对对象进行任何更改 不可以新增属性和删除也不可修改 完全锁死 |
Object.isFrozen(object) | 如果对象被冻结,则返回 true |
允许保留字作为属性名称 | var obj = {name: "Bill", new: "yes"} |
- ES5 语法更改
- 对字符串的属性访问 [ ]
- 数组和对象字面量中的尾随逗号(JSON除外)
- 多行字符串字面量
- 作为属性名称的保留字
- ES5 浏览器支持
Chrome 23、IE 10 和 Safari 6 是第一批完全支持 ECMAScript 5 的浏览器:
Chrome23 | IE10/Edge | Firefox21 | Safari6 | Opera15 |
---|---|---|---|---|
2012年9月 | 2012年9月 | 2013年4月 | 2012年7月 | 2013年7月 |
11. ECMAScript 6 (2015年)
2015年发布ES6,又是一次重大更新:
新的特性和扩展 | 描述 |
---|---|
变量声明 | let: js1.7中已经有过实现,这里重提,说明块级作用域的重要性;const:常量;变量解构赋值:let \[a, b, c] = \[1, 2, 3]; let { foo, bar } = { foo: 'aaa', bar: 'bbb' }; |
幂 (**) | 指数运算符, Math.pow() ? |
默认参数值 | 函数声明时可以制定默认参数 |
字符串扩展 | 见下面列表 |
正则扩展 | 见下面列表 |
数组扩展 | 见下面列表 |
对象扩展 | 见下面列表 |
新的数字属性 | ES6 将以下属性添加到 Number 对象: EPSILON、MIN_SAFE_INTEGER、MAX_SAFE_INTEGER |
新的数字方法 | Number.isInteger() 、Number.isSafeInteger(), Math也新增了17个静态方法 |
新的全局方法 | isFinite()、isNaN() |
函数扩展 | 1、引入箭头函数:没有自己的this、没有递归,事件绑定,解绑定、没有构造器、不可具名化、书写简单 2、引入rest参数 3、增加Function.prototype.toString() |
Symbol | 新的原始数据类型, 表示独一无二的值 |
新增数据结构 | Set和Map |
Proxy | 对象代理,详见阮老师的解释 |
Reflect | 为了操作对象而提供的新 API |
前端异步策略 | Promise(通常与ES2017的async一起使用) |
迭代器 | 将Iterator 、 for...of 循环 和 Generator函数写入规范 |
Class声明 | 增加继承、接口等概念 |
ESModule | js原生支持模块化 |
新增提案 | 装饰器Decorator、do 表达式、throw 表达式、函数的部分执行、管道运算符、Math.signbit()、双冒号运算符、Realm API、#!命令、import.meta、JSON 模块 |
新增规格文件 | - |
- 字符串扩展
- 字符串Unicode标识法,并允许直接输入字符
- 引入字符串遍历器
- JSON.stringify改造
- 引入模板字符串、标签模板渲染
- 新增一些方法,见下表
字符串方法 | 描述 |
---|---|
String.fromCodePoint() | 可以识别大于0xFFFF 的字符 |
String.raw() | 返回一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串,往往用于模板字符串的处理方法 |
codePointAt | 能够正确处理 4 个字节储存的字符,返回一个字符的码点。 |
normalize | 将字符的不同表示方法统一为同样的形式,这称为 Unicode 正规化。 |
startsWith | 返回布尔值,表示参数字符串是否在原字符串的头部。 |
endsWith | 返回布尔值,表示参数字符串是否在原字符串的尾部。 |
repeat | 返回一个新字符串,表示将原字符串重复n 次。 |
at | 接受一个整数作为参数,返回参数指定位置的字符,支持负索引(即倒数的位置)。 |
- 正则扩展
- RegExp构造函数增加第二个参数
- ES6 出现之前,字符串对象共有 4 个方法,可以使用正则表达式:
match()
、replace()
、search()
和split()
。ES6中RegExp集成了这四个方法。 u
修饰符- 新增RegExp.prototype.unicode 属性
- 新增RegExp.prototype.sticky 属性
- 新增RegExp.prototype.flags 属性
- 数组扩展
- 引入扩展运算符
- Array.from()
- Array.of()
- 实例方法:copyWithin()
- 实例方法:find() 和 findIndex()
- 实例方法:fill()
- 实例方法:entries(),keys() 和 values()
- 数组的空位定义
- Array.prototype.sort() 的排序稳定性问题:常见的排序算法之中,插入排序、合并排序、冒泡排序等都是稳定的,堆排序、快速排序等是不稳定的。不稳定排序的主要缺点是,多重排序时可能会产生问题。在ES2019才解决。
- 对象扩展
- key和变量是同一个单词时,可简写
- 属性名表达式写法:
obj['a' + 'bc'] = 123;
- 方法有name属性
- Object.assign()
- 对象属性遍历方式:for...in、Object.keys(obj)、Object.getOwnPropertyNames(obj)、Object.getOwnPropertySymbols(obj)、Reflect.ownKeys(obj)
- super关键字
- 对象扩展
...
- Object.is():ES5 比较两个值是否相等,只有两个运算符:相等运算符(
==
)和严格相等运算符(===
)。它们都有缺点,前者会自动转换数据类型,后者的NaN
不等于自身,以及+0
等于-0
。JavaScript 缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。
- ES6 浏览器支持
Safari 10 和 Edge 14 是首先完全支持 ES6 的浏览器:
Chrome 58 | Edge 14 | Firefox 54 | Safari 10 | Opera 55 |
---|---|---|---|---|
Jan 2017 | Aug 2016 | Mar 2017 | Jul 2016 | Aug 2018 |
ES6之后没有小数字版本的ECMAScript了,取而代之的是以年份为代号的版本,因为每一年都会有一个版本。网传的ES7、ES8什么的,其实官方没有这种叫法。
12. ECMAScript 2016
2016年发布。
新特性 | 描述 |
---|---|
求幂赋值 | let x = 5; x **= 2; // 结果是 25 |
Array.prototype.includes | - |
装饰器 | 动态 |
- ES2016 浏览器支持
所有现代浏览器都支持 Array.prototype.includes:
Chrome 47 | Edge 14 | Firefox 43 | Safari 9 | Opera 34 |
---|---|---|---|---|
2015 年 12 月 | 2016 年 8 月 | 2015 年 12 月 | 2015 年 10 月 | 2015 年 12 月 |
最近的版本快速浏览:
13. ECMAScript 2017
2017年发布。
新特性 | 描述 |
---|---|
异步声明 | async/await |
对象支持尾逗号 | { a: 1, b: 2, } |
字符串扩展 | String.padStart 和 String.padEnd,增加字符串前后缩进 |
对象扩展 | Object.values、Object.entries、Object.getOwnPropertyDescriptors() |
14. ECMAScript 2018
2018年发布。
新特性 | 描述 |
---|---|
异步迭代 | ES2018引⼊异步迭代器,允许 for...of 循环可以和 await ⼀起使⽤,以串⾏的⽅式运⾏异步操作 |
Promise扩展 | Promise.finally() |
Rest参数的Spread属性 | 展开运算符:Math.max(...values) // values = [1,2,3,4] |
正则表达式命名捕获组 | ES2018允许命名捕获组使⽤符号?<name> ,在打开捕获括号(后⽴即命名 |
正则表达式反向断言 | ?<= 、?\<! |
正则表达式 s 修饰符:dotAll 模式 | 正则扩展 |
正则表达式Unicode转义 | 正则扩展 |
- 异步迭代示例:
// 不起作用
async function process(array) {
for (let i of array) {
await doSomething(i);
}
}
// 起作用了
async function process(array) {
for await (let i of array) {
doSomething(i);
}
}
15. ECMAScript 2019
2019年发布。
新特性 | 描述 |
---|---|
字符串扩展 | String.prototype.trimStart() , String.prototype.trimEnd() ,原来的trimLeft和trimRight被废弃 |
Object.fromEntries() | Object.entries的逆运算 |
数组拍平 | Array.prototype.flat() , Array.prototype.flatMap() |
catch 的参数改为可选 | 可以省略catch绑定的参数和括号 |
Symbol.description | - |
JSON Superset 超集 | 即js兼容所有JSON支持的文本。原始的JSON 内容可以正常包含 U+2028行分隔符 与 U+2029段分隔符,而之前版本的ECMAScript 却不行 |
JSON.stringify() 转化特殊字符 | 修复了对于一些超出范围的 Unicode 展示错误的问题 |
Array.sort | 变为稳定算法 |
Function.prototype.toString()增强 | 可以返回注释和空格 |
String.prototype.matchAll | 返回一个包含所有匹配正则表达式及分组捕获结果的迭代器 |
16. ECMAScript 2020
2020年发布。
新特性 | 描述 |
---|---|
Promise.allSettled | 当所有promise都resolve或reject后,返回resolve或reject的结果集合数组 |
链判断运算符 | const firstName = message?.body?.user?.firstName || 'default'; |
空值合并运算符 | var level = user.level ?? '暂⽆等级'; |
动态引入 | import(/test.js ).then((module) => |
globalThis对象 | globalThis 提供了一个标准的方式来获取不同环境下的全局 this 对象(也就是全局对象自身)。不像 window 或者 self 这些属性,globalThis 确保可以在有、无窗口的各种环境下正常工作 |
引入bigint | Number有取值范围 [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER], 超过这个范围会精度丢失,这就需要大数类型:var bigIntNum = 9007199254740993n; |
17. ECMAScript 2021
2021年发布。
新特性 | 描述 |
---|---|
Promise.any | 接收一个Promise数组,只要其中任意一个resolve,便通过,否则拒绝 |
replaceAll | 返回⼀个全新的字符串,所有符合匹配规则的字符都将被替换掉 |
WeakRefs | 在一般情况下,对象的引用是强引用的,这意味着只要持有对象的引用,它就不会被垃圾回收。只有当该对象没有任何的强引用时,垃圾回收机制才会销毁该对象并且回收该对象所占的内存空间。而 WeakRef 允许保留对一个对象的弱引用,而不会阻止被弱引用的对象被垃圾回收。 |
数字声明时可增加分隔符 | const num = 1_000_000_000 //1000000000 |
新的逻辑运算符和表达式 | 见下面代码 |
a ||= b
//等价于
a = a || (a = b)
a &&= b
//等价于
a = a && (a = b)
a ??= b
//等价于
a = a ?? (a = b)
18. ECMAScript 2022
2022年发布。
新特性 | 描述 |
---|---|
class新特性 | 1、增加private属性, class A { #a = 1 } ; 2、增加class的static成员方法 |
top-level await | 可以使用type='module'的script标签引入一个js文件,js里使用await获取异步数据 |
数组实例方法at(index) | a = [1,2,3]; const value = a.at(1) |
error处理新方式 | throw new Error('fail', { cause: 123 }); 在catch里获取:catch(e) { console.log(e.cause) } |
Object.hasOwn(obj, propKey) | 同hasOwnProperty,但支持所有对象类型的判断 |
19. ECMAScript 2023
- 从头到尾搜索数组:
findLast()
、findLastIndex()
- Hashbang 语法
- 通过副本更改数组:
toReversed()
、toSorted()
、toSpliced()
、with()
- Symbol 作为 WeakMap 的键
20. 后记
随着ECMAScript版本的更新,文章会持续保持更新~~
参考资料