2015 年后 ECMAScript 标准的更新转为年度发布模式,每年更新新的语言特性。
ES1
1995 年,JavaScript 由 Netscape 的工程师 Brendan Eich 发明,最初命名为 Mocha,后改为 LiveScript,最终定名为 JavaScript(为了借助 Java “一次编写,到处运行” 的热度推广,Netscape 与 Sun 达成合作,在 1995 年 12 月将其正式命名为 JavaScript)。
1997 年 6 月,JavaScript 被提交给了欧洲计算机制造商协会(ECMA)进行标准化,该组织发布了第一个版本 ECMAScript 标准(ES1)。
1. 基本语法与结构
- 变量声明:
- 使用
var关键字声明变量。仅支持函数作用域,没有块级作用域 - 允许重复声明
- 存在“变量提升”(声明会被提升到作用域的顶端,赋值保留在原位置)
- 使用
- 数据类型:
- 原始类型:
Number、String、Boolean、null、undefined - 复合类型:
Object(也包括数组、函数等) - 所有的数字均为双精度浮点数,没有单独的整数类型
- 原始类型:
- 运算符:支持
- 算术运算符(
+,-,*,\/,%) - 比较运算符(
==,!=,<,>,<=,>=) - 逻辑运算符(
&&,||,!) - 赋值运算符(
=、+=、-=、*=) - 其他运算符(
typeof、instanceof、,)
- 算术运算符(
- 控制结构:包含基本的流程控制语句,如
- 条件语句:
if...else、switch - 循环语句:
while、do...while、for - 跳转语句:
break、continue
- 条件语句:
- 注释:支持单行注释(
//)和多行注释(/* */)
2. 函数
ES1 确立了“函数是一等公民”的核心特性:
- 使用
function关键字定义函数 - 支持函数声明和函数表达式
- 可以作为参数传递或从其他函数返回
- 拥有闭包的基本能力,但由于当时使用场景有限,这一特性并没有被广泛利用
3. 对象
- 对象的创建: 可以通过对象字面量
{}或构造函数(new Object())来创建 - 原型机制:ES1 引入的是基于原型的继承机制。每一个对象都有一个内部属性
[[Prototype]](在大多数实现中可通过__proto__访问),用于实现继承。这是与基于类的传统面向对象语言(如 Java )的根本区别 - 内置对象:提供了核心的内置对象,如
Object、Function、Array、String、Boolean、Number、Date、RegExp、Error等 - 属性操作:通过
.或[]访问属性;支持动态添加、修改、删除属性(delete关键字) this关键字:在对象方法中,this指向当前调用方法的对象;全局函数中 this 指向全局对象
4. 数组
- 数组的创建:支持数组字面量(
[元素1, 元素二, ...]) 和new Array()构造函数 - 索引访问:通过数字索引(从 0 开始)访问属性元素,通过
length属性获取/修改数组长度 - 基础方法:提供
toString()、valueOf()等简单方法
5. 内置方法与全局对象
- 全局对象:在浏览器中通常是
window对象,在 Node.js (额,Node.js 是 2009 才出现的产物,可能是逻辑问题,放在这里做了比较)等环境中是global。它包含了全局函数如isNaN()、isFinite()、parseInt()、parseFloat()等 - Math 对象:提供数学常数和函数,如
Math.PI、Path.min()、Math.random()等 - 基本方法:字符串、数组提供了一些基础方法,例如
String.prototype.length、Array.prototype.length、String.prototype.charAt()、Array.prototype.push()(部分方法可能在早期实现中不一致)
6. 错误处理
- 支持
try...catch...finally语句进行异常捕获和处理 - 定义了基本的错误类型,如
Error、SyntaxError、TypeError等
7. 重大意义
- 结束了浏览器脚本语言的混乱,为 AJAX(1998 年)、jQuery(2006 年)等技术奠定基础
- 确立了 JavaScript 的设计哲学
- 动态类型:变量类型在运行时确定(额,好像 ES17 之后要变天了)
- 函数一等公民:函数可以作为参数传递或返回值,支持高阶函数模式
- 原型继承:尽管 ES6 引入了
class语法糖,但底层仍依赖 ES1 的原型链机制
- 推动了 Web 的开发标准化
8. 先天不足
- 功能缺失:
- 无原生异常处理:
try-catch机制到 ES3 才真正完善,ES1 调试依赖开发者自行实现 - 无严格模式:变量提升、全剧作用域污染等问题需手动规避
- 无模块化支持:代码组织依赖全局变量或命名空间模式
- 无原生异常处理:
- 浏览器实现差异:早期浏览器对 ES1 标准的实现存在不一致。例如, IE4 对
array方法支持不完整,导致浏览器兼容性 - 设计缺陷:
- 原型链隐式性:开发者需深入内部方法(如
、)才能操作对象继承 - 活动对象模式: ES1 的作用域机制通过活动对象(AO)实现,但边界情况下(如
Object.prototype属性继承)可能导致意外行为
- 原型链隐式性:开发者需深入内部方法(如
ES2
1998 年 6 月,推出了 ECMAScript 2.0 标准,为 ECMAScript 1 进行了进一步的规范化和错误修正。
ECMAScript 2nd Edition 并没有新的语言功能或特性,而是为了使 ECMAScript 标准与国际标准化组织(ISO/IEC)的规范保持一致,特别是 ISO/IEC 16262 标准。
主要内容
- 格式与结构的调整:为了符合 ISO 的文档格式要求,对标准文档的章节结构、编号和排版进行了调整
- 澄清与修正:对 ES1 标准中一些模糊或可能出现歧义的表述进行了澄清和修正,使规范的描述更加准确和严谨
- 明确
typeof null返回object的行为,避免不同引擎的理解偏差 - 对
==的类型转换逻辑补充了更多的规则,减少了不同实现中“隐式转换”的不一致 - 细化了
arguments对象的行为描述 - 明确了
function关键字与标识符的空格要求 - 将
method(方法)统一表述为property(属性)或function(函数)
- 明确
- 错误修复:修正了 ES1 标准文档中发现的一些非技术性错误和笔误
- 国际化支持:增加了对 Unicode 字符集的明确引用(在 ES1 中已支持),强调字符串处理需兼容多语言模型,为后续国际化特性(如 ES3 的
String.prototype.localeCompare())铺路
ES3
1999 年 12 月,发布了 ECMAScript 3 版本,这一版本为 JavaScript 的发展奠定了坚实的基础,引入了正则表达式、异常处理、更严格的错误定义、字符串处理改进等新功能特性。
2005 年,出现了 Ajax(Asynchronous JavaScript and XML),这种技术使得 Web 应用可以在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页内容。这极大地改善了用户体验,推动了 Web 2.0 时代的到来。
1. 正则表达式
ES3 正式引入了对正则表达式的一等公民的支持。使得复杂的字符串验证、搜索和替换变得高效且简洁,是表单验证、文本处理等场景的基石
提供了:
- 字面量语法:
/pattern/flags - 构造函数:
new RegExp(pattern, flags) String方法支持:新增了match()、replace()、search()、split()等方法来利用正则表达式进行字符串处理
2. 异常处理
完善了(ES1 缺乏主动抛出错误和详细错误信息的能力)结构化的错误处理机制 try..catch...finally 语句。让开发者可以更优雅的捕获和处理运行时错误,避免程序崩溃,并执行必要的清理工作,显著提高了代码的健壮性和可维护性。
throw语句:允许主动排除异常(可抛出任意类型值,推荐抛出Error对象)Error对象扩展:Error实例新增message属性(储存错误描述),便于捕获时获取详细信息
try {
// 可能出错的代码
throw new Error('莫抛错,抛错必被捉');
} catch (error) {
// 捕获并处理错误
console.error(error);
} finally {
// 无论是否出错都将执行的代码
}
3. switch 语句
(等等,不是说 ES1 就有这玩意么)
引入了 switch 语句,用于基于不同的条件执行不同的代码块。
- 支持
case、default、break等关键字
为多分支选择提供了比 if...else if...else 更清晰,更高效的语法结构
4. do...while 循环
新增了 do...while 循环语句,确保循环体至少执行一次
do {
// 循环体
} while (condition);
5. for...in 循环
新增 for...in 循环,用于遍历对象的可枚举属性(包括原型链上的属性)。它解决了早期“通过 for 循环遍历对象属性”的繁琐问题(需使用 hasOwnProperty() 判断属性是否为对象自身所有),成为现代 JavaScript 中操作对象的核心循环结构。
const role = {
name: 'Tom',
age: 86,
species: 'cat',
};
for (let prop in role) {
if (role.hasOwnProperty(prop)) {
console.log(prop.concat(': ').concat(role[prop]));
}
}
6. == 和 != 运算符改进
规范了相等(==)和不等(!=) 运算符的类型强制转换(Type Coercion)行为。
7. new 运算符的改进
增强了 new 运算符的语义,使其创建对象的过程更加明确可控。为基于构造函数的面相对象编程模式提供了更可靠的基础。
8. 内置对象的扩展和增强
String对象:新增了charAt():查找给定字符在字符串的索引下标charCodeAt():查找给定字符在字符串的索引下标indexOf():查找给定子串在字符串的位置lastIndexOf()slice():substring()toLoverCase()toUpperCase()
Array对象:新增了concat()join()pop()push()reverse()shift()slice()sort():sort()还支持传入自定义的比较函数splice()unshift()
Date()对象:增强了日期的处理能力,提供了更精准的毫秒级时间操作方法Function对象:length属性被标准化,表示函数期望的参数个个数- 全局对象:
isFinite()和isNaN()函数被明确定义和标准化
9. 函数和 this 的增强
ES3 强化了函数的灵活性,尤其是 this 执行的控制:
call()与apply()方法:允许调用函数时执行this指向,解决了函数在不同上下文执行时的 this 绑定问题arguments对象完善:明确arguments与函数的“动态绑定”(修改了arguments[i]会同步修改对应参数,反之亦然),但后续 ES5 严格模式取消了这一特性
10. 语法和语义的澄清
对 ES1 中模糊或有歧义的语法和语义进行了大量的澄清和修正,提高了标准的严谨性,较少了不同浏览器直接的差异,促进了跨平台兼容性。
11. 争议和局限性
with语句的争议:ES3 引入了with(如with(obj) { console.log(a); }),简化对象属性的访问,但由于with语句会破坏作用域链的可预测性(导致变量解析歧义),ES 5 中将其标记为“不推荐使用”,并在 ES6 的严格模式下完全禁用- 函数提升的副作用:ES3 延续了 ES1 的函数提升机制,导致了变量提升与函数提升的交互复杂。如,
foo(); function foo(){}可执行,而foo();var foo = function() {}却报错不可执行 - 性能优化缺失:ES3 未针对循环、对象访问等高频操作性能优化建议(如
for...in遍历数组的性能问题),导致早期 JavaScript 代码效率低下。这一问题直到现代浏览器(如 Chrome、Firefox )通过 JIT(即时编译)优化才得到缓解
ES4
由于种种原因,ES4 并没有。但是许多特性在后来的 ES5 和 ES6 中得到了实现。
ECMAScript 4th Edition(ES4) 是在 1999 年发布 ES3 后,微软(.NET 平台)、Mozilla(Firefox)、Adobe(Flash 平台的 ActionScript 与 JavaScript 同源,希望统一标准) 等公司希望将 JavaScript 打造成一门可以与 Java、C# 竞争、适合大型应用开发的企业级语言,试图从一门动态脚本语言转变成一门更严谨、更适合大规模开发的静态类型语言。
俗话说的好,步子迈得太大,容易扯着蛋
1. 强类型系统(Strong Typing)
- 引入类似 Java/C# 的静态类型声明,允许变量、函数参数和返回值执行数据类型
var name: String = 'Tom';
function add(a: int, b: int): int {
return a + b;
}
2. 类 (Classes)
计划引入基于类的继承语法,包括 class、extends、public、private、protected 等关键字,这将取代当时主流的基于原型的构造函数模式。
3. 模块化 (Modules)
提出了一套完整的模块系统,用于代码的组织、封装和依赖管理,支持 import 和 export
4. 包(packages) 和命名空间(Namespaces)
为了管理大型代码库,引入了 package 和 namespace 概念,以避免全局污染和命名冲突。
5. 丰富的集合类型
计划添加如 List、Dict(字典)、Set 等新的内置数据结构。
6. 迭代器(Iterators)和生成器(Generators)
虽然最终在 ES6 上实现,但这一概念最早在 ES4 上讨论中被提出。
7 其他高级特性
- 泛型(Generics)
- 多重继承(Multiple Inheritance)
- 字符串模版(String Interpolation)
- 更大的元编辑能力
8. 失败的原因
- 复杂性过高: ES4 提案过于庞大和复杂,学习曲线陡峭,违背了 JavaScript 原有的简洁和灵活性
- 社区分裂: JavaScript 社区内部存在严重分歧。以 Yahoo!、Google 和 Opera 为代表的公司(尤其是 Brendan Eich,JavaScript 的创始者)强烈反对这种激进的变革。他们认为这样会使 JavaScript 变得臃肿,失去其作为“网络胶水语言”(Web glue language)的轻量和敏捷特性
- 向后兼容性的担忧:如此巨大的变更几乎不能保证与现有海量 JavaScript 代码完全兼容
- 实现难度大:为浏览区实现这么一个复杂的引擎需要巨大的工程投入
- 路线之争:形成了两派:一派(ES4 支持者)主张大步前进;另一派主张小步快跑,先解决 ES3 中的问题并添加少量实用的新特性
9. 虽败犹荣
ES4 项目本身是失败的,但它的影响确是深远且积极的:
- 催生了 ES3.1/ES5:
- 反对 ES4 激进路线的一方提出了替代方案---“ES3.1”,即只进行必要的、不破坏的兼容性改进
- 这个方案最终胜出,并发展为 ECMAScript 5th Edition(ES5),于 2009 年发布。 ES5 包含了
strict mode、JSON支持、Array的forEach/map/filter等方法,以及对对象属性更精细控制(Object.defineProperty)等实用改进
- 许多思想在 ES6+ 中复活:
- 类(classes):ES6(2015)引入了
class语法糖,虽然底层仍是基于原型,但提供了熟悉的类式语法 - 模块化(Modules):ES6 正式引入了
import/export模块系统 let/const: 提供了块级作用域,解决了var的问题- 迭代器与生成器:ES6 实现了这些特性
- 箭头函数、结构赋值、字符串模版:这些现代 JS 特性都可以追溯到 ES4
- TypeScript 的灵感来源:微软后来推出的 TypeScript 语言,可以看作是“实现了 ES4”。它为 JavaScript 添加了可选的静态类型、类、模块等特性,完美契合了当年的 ES4 的愿景
- 类(classes):ES6(2015)引入了
ES5
2008 年,谷歌发布了 Chrome 浏览器,内置了 V8 JavaScript 引擎。V8 引擎极大地提高了 JavaScript 的执行速度,这促进了更复杂的 Web 应用的开发。
2009 年 12 月,发布了 ECMAScript5 ,主要提出了严格模式(use strict)和 JSON 支持以及数组新方法(forEach、map、filter、reduce、some、every)、新的对象方法(Object.create、Object.defineProperty、Object.keys)、新的原生方法和增强的原型链。
2009 年,Ryan Dahl 推出 Node.js ,这是一个开源、跨平台的运行环境,允许开发者使用 JavaScript 来编写服务器端代码。
1. 严格模式(Strict Mode)
通过在脚本或函数的顶部添加 "use strict"; 指令启动。
- 捕获错误:将原本静默失败的错误(如给未未声明的变量赋值)变成显式的
ReferenceError - 禁用危险语法:禁止使用
with和eval影响外部作用域等容易引发问题的特性 - 防止意外全局变量:在非严格模式下,
x = 1;会创建全局模式;严格模式下,这会抛出错误 this绑定安全:在非法调用时的函数中,this不再指向全局对象(window),而是undefined,避免了全局污染- 禁止删除不可删除的属性:如
delete Object.prototype在非严格模式下静默失败,严格模式下直接报错 - 禁止重复参数名:
function fn (a, a) { }在非严格模式下允许(后一个会覆盖前一个),严格模式下会报错 - 提高性能:引擎可以进行更多优化,因为严格模式下的代码行为更可预测
2. 内置对象的扩展与方法增强
Array对象:- 迭代方法:新增了
forEach()、map()、filter()、some()、every()、reduce()、reduceRight()等方法。这些方法支持函数式编程风格,无需修改原数组即可进行遍历、转换和聚合,代码更简洁、更易读 - 查找方法:
indexOf()和lastIndexOf()用于查找元素在数组中的引用
- 迭代方法:新增了
String对象:trim(): 移除字符串首尾的空白字符(空格、字符串、换行符等),解决了常见的字符串处理痛点
Object对象:Object.create(proto, [propertiesObject]):创建一个对象,并指定其原型。这是实现原型继承的更直接、更清晰的方式Object.defineProperty(obj, prop, descriptor)/Object.defineProperties(obj, props): 允许精确控制对象的属性的特征,如value、writable、enumerable、configurable。这是实现数据绑定、响应式的基础Object.keys(obj):返回一个由自身对象所有可枚举属性名组成的数组Object.getOwnPropertyNames(obj):返回一个由对象自身属性名(包括不可枚举属性)组成的数组Object.getOwnPropertyDescriptor(obj, prop):获取指定属性的完整描述符对象Object.seal(obj):密封对象,阻止添加新属性和删除现有属性,但允许修改现有属性的值Object.freeze(obj):冻结对象,最严格的保护,对象及其属性都不可变Object.preventExtensions(obj):阻止向对象添加新属性
3. JSON 原生支持
内置了 JSON 对象,提供了 JSON.parse() 和 JSON.stringify() 两个方法。使得数据的序列化和反系列化变得极其简单、高效和安全。这极大的促进了 AJAX 技术和 RESTful Web API 的普及,是 Web 2.0 时代的关键推动力。
const jsonStr = '{"name":"Tom","age":86}';
/** 转化为 JSON 对象 */
const obj = JSON.parse(jsonStr);
// true
console.log(jsonStr == JSON.stringify(obj));
4. 函数上下文的绑定
使用 Function.prototype.bind(thisArg[, arg1[, arg2[, ...]]]) 创建一个新的函数,该函数的 this 值将永久绑定在指定 thisArg 对象,并可以预设部分参数。
完美解决了 this 绑定混乱的经典问题,尤其是在事件处理、回调函数和异步编程中。bind 方法是现在 JavaScript 中管理函数执行上写文的核心工具。
5. Getter/Setter 属性
允许使用 get 和 set 关键字定义对象的访问器属性。当读取或设置属性时,会自动调用相应的 getter 和 setter 函数。
const obj = {
_value: 86,
get value() {
return this._value;
},
set value(value) {
this._value = value;
},
};
6. 保留字使用放宽
可以使用某些保留字(如 class、default )作为属性名而无需使用引号,提高了代码的灵活性。
7. Date.now()
静态方法,返回当前时间的时间戳(毫秒值),比 new Date().getTime() 更简洁高效。
8. Array.isArray(arr)
可靠的返回一个值是否为数组。在此之前, instanceof Array 在多窗口/框架下可能失效。
9. 分析
优点:
- 稳健可靠:所有新增的功能都经过深思熟路,旨在解决实际问题,且不破坏现有代码
- 实用性极强:
strict mode、JSON、Array的迭代方法、bind等特性都是开发者日常使用中高频使用的“刚需”功能 - 推动现代化:为现代 Web 开发(如单页面应用,AJAX )提供了必要的语言支持
- 广泛兼容: 迅速被所有现代浏览器支持,成为事实上的标准
ECMAScript 5 是 JavaScript 成熟的标志。它通过引入 strict mode、原生 JSON 支持、强大的 Object 和 Array API 和 bind 方法,极大地增强了语言的能力、安全性和开发效率。
ES5 的设计哲学-- 渐进式改进、注重实用性和向后兼容 -- 确保了它的成功。在 ES3 的基础上增补了核心功能空白(如 JSON、严格模式、精细的对象控制),同时为 ES6 的“爆发式增强”奠定了基础。也使得其成为 JavaScript 发展史上的“里程碑版本”。
ES5.1
2011 年 6 月,作为 ES5 的小更新,进行了了一些规范上的调整和错误的修正。