JavaScript

JavaScript（通常缩写为JS）是一门基于原型和头等函数的多范式高级解释型编程语言，它支持面向对象程式设计、指令式编程和函数式编程。它提供方法来操控文本、数组、日期以及正则表达式等。不支持I/O，比如网络、存储和图形等，但这些都可以由它的宿主环境提供支持。它由Ecma通过ECMAScript实现语言的标准化。目前，它被世界上的绝大多数网站所使用，也被世界主流浏览器（Chrome、Firefox、Safari和Opera）所支持。

JavaScript与Java在名字和语法上都很相似，但这两门编程语言从设计之初就有很大不同。JavaScript在语言设计上主要受到了Self（一种基于原型的编程语言）和Scheme（一门函数式编程语言）的影响，在语法结构上它和C语言很相似（如if条件语句、switch语句、while循环和do-while循环等）。

对于客户端来说，JavaScript通常被实现为一门解释语言，但如今它已经可以被即时编译（JIT）。随着HTML5和CSS3语言标准的推行，它还可以用于游戏、桌面和移动应用程序的开发，以及在服务器端网络环境运行（如Node.js）。

## 历史

### 肇始于网景

1993年，国家超级电脑应用中心（NCSA）发表了NCSA Mosaic，这是最早流行的图形使用者界面网页浏览器，它在万维网的普及上发挥了重要作用。1994年，Mosaic的主要开发人员创立了Netscape公司，并雇用了许多原来的NCSA Mosaic开发者用来开发Netscape Navigator，该公司的目标是取代NCSA Mosaic成为世界第一的网页浏览器。在四个月内，已经占据了四分之三的浏览器市场，并成为1990年代互联网的主要浏览器。

在网络发展的这些年，网页只能是静态的，缺乏在浏览器中载入网页后的动态行为能力。公司的创始人马克·安德森认为HTML需要一种胶水语言，让网页设计师和兼职程序员可以很容易地使用它来组装图片和插件之类的元件，且程式码可以直接编写在网页标记中。1995年，网景招募了布兰登·艾克，目标是把Scheme语言嵌入到Netscape Navigator浏览器中。但更早之前，网景已经跟昇阳合作，计划在Netscape Navigator中嵌入Java语言，这时网景内部产生激烈的争论。

网景公司管理层很快决定，最佳的方案是由艾克设计一种新的语言，其语法类似于Java，而不像Scheme或其他现存的脚本语言。为了在其他竞争提案中捍卫JavaScript这个想法，公司需要有一个可以运作的原型。艾克在1995年5月仅花了十天时间就把原型设计出来了。最初命名为Mocha，1995年9月在Netscape Navigator 2.0的Beta版中改名为LiveScript，同年12月在Netscape Navigator 2.0 Beta 3中部署时又被重命名为JavaScript，这是因为当时网景公司与昇阳电脑公司组成的开发联盟为了让这门语言搭上Java这个编程语言“热词”，所以才将其临时改名为JavaScript，这成了日后这成为大众对这门语言有诸多误解的原因之一。同年昇阳电脑公司在1995年12月1日正式申请注册JavaScript的商标，于2000年7月28日获批，但此后昇阳电脑公司和之后收购它的甲骨文公司从未使用该商标制造过产品，给JavaScript社区带来了不确定性，阻碍了JavaScript社区的自由发展，直到几十年后，1.5万多名JavaScript社区成员于2024年11月22日联名向美国专利商标局申请终止该商标，甲骨文公司于2025年2月3日以Node.js产品回复了该申请案，终止流程仍待决。

### 微软采纳

微软公司于1995年首次推出Internet Explorer，引发了与Netscape的浏览器大战。微软对Netscape Navigator直译器进行了逆向工程，创建了JScript，以与处于市场领导地位的网景产品同台竞争。JScript也是一种JavaScript实作，这两个JavaScript语言版本在浏览器端共存意味着语言标准化的缺失。发展初期，JavaScript的标准并未确定，同期就有网景的JavaScript和微软的JScript。除此之外，微软也在网页技术上加入了不少专属物件，使不少网页使用非微软平台及浏览器无法正常显示。这导致在浏览器大战期间网页设计者通常会把“用Netscape可达到最佳效果”或“用IE可达到最佳效果”的标志放在首页。

### 增长和标准化

1996年11月，网景正式向ECMA（欧洲计算机制造商协会）提交语言标准。1997年6月，ECMA以JavaScript语言为基础制定了ECMAScript标准规范ECMA-262。JavaScript成为了ECMAScript最著名的实现之一。除此之外，ActionScript和JScript也都是ECMAScript规范的实现语言。尽管JavaScript作为给非程式人员的脚本语言，而非作为给程式人员的程式语言来推广和宣传，但是JavaScript具有非常丰富的特性。

在21世纪初Internet Explorer占主导地位期间，客户端脚本停滞不前。这在2004年开始改变，当时Netscape的继任者Mozilla发布了Firefox浏览器。Firefox受到许多人的好评，从Internet Explorer获得了巨大的市场份额。

2005年，Mozilla加入了ECMA International，并开始研究ECMAScript for XML（E4X）标准。这导致Mozilla与Macromedia（后来被Adobe Systems收购）合作，他们正在用基于ECMAScript 4草案的ActionScript 3语言实现E4X。目标是将ActionScript 3标准化为新的ECMAScript 4。为此，Adobe Systems将Tamarin实现作为开源项目发布。然而，Tamarin和ActionScript 3与既定的客户端脚本太不同，如果没有微软的合作，ECMAScript 4从未取得成果。

与此同时，与ECMA工作无关的开源社区正在发生非常重要的发展。2005年，杰西·詹姆士·贾瑞特(Jesse James Garrett)发布了一网志，其中他创造了“AJAX”一词，并描述了一套技术，其中JavaScript是骨干，用于创建可以在后台加载数据的Web应用程序，避免了重新加载整页的需要。这引发了JavaScript的复兴时期，由开源库和围绕它们形成的社区带头。创建了许多新库，包括jQuery、Prototype、Dojo Toolkit和MooTools。

谷歌于2008年首次推出Chrome浏览器，其V8 JavaScript引擎比竞争对手更快。关键的创新是及时编译（JIT），因此其他浏览器供应商需要为JIT彻底改革他们的引擎

2008年7月，这些不同领域的相关代表在奥斯陆举行会议。这导致在2009年初达成了最终协议，将所有相关工作结合起来，推动语言向前发展。结果是2009年12月发布的ECMAScript 5标准。

### 走向成熟

关于该语言的雄心勃勃的工作持续了数年，最终随着 2015 年ECMAScript 6的发布而正式形成了广泛的补充和改进。

瑞安·达尔(Ryan Dahl)在 2009 年创建的Node.js引发了网页浏览器之外 JavaScript 使用的显着增加。Node.js结合了V8引擎、事件循环和I/O API，从而提供了独立的 JavaScript 运行时系统。截至 2018 年，Node.js 已被数百万开发人员使用，并且npm拥有世界上所有软件包管理系统中最多的模块。

ECMAScript 草案规范目前在GitHub上公开维护，并通过定期的年度快照生成版本。对语言的潜在修订通过全面的提案流程进行审查。现在，开发人员不再单独检查即将推出的功能的状态，而不是版本号。

当前的 JavaScript 生态系统拥有许多库和框架、已建立的编程实践以及在网页浏览器之外大量使用 JavaScript。另外，随着单页应用程序和其他大量使用 JavaScript 的网站的兴起，已经创建了多个转译器来帮助开发过程。

## 概论

一般来说，完整的JavaScript包括以下几个部分：

- ECMAScript，描述了该语言的语法和基本对象。
- 文档对象模型（DOM），描述处理网页内容的方法和接口。
- 浏览器对象模型（BOM），描述与浏览器进行交互的方法和接口。

JavaScript的基本特点如下：

- 是一种解释性脚本语言（代码不进行预编译）。
- 主要用来向HTML页面添加交互行为。
- 可以直接嵌入HTML页面，但写成单独的js文件有利于结构和行为的分离。

JavaScript常用来完成以下任务：

- 嵌入动态文本于HTML页面。
- 对浏览器事件作出响应。
- 读写HTML元素。
- 在数据被提交到服务器之前验证数据。
- 检测访客的浏览器信息。
- 控制Cookie，包括创建和修改等。

## 特性

不同于服务器端脚本语言（如PHP和ASP），JavaScript主要被作为客户端脚本语言在用户的浏览器上运行，不需要服务器的支持。所以在早期程序员比较青睐于JavaScript以减少对服务器的负担，而与此同时在安全性上出现了问题。随着服务器变得强大，现在的程序员更喜欢运行于服务器端的脚本以保证安全，但JavaScript仍然以其跨平台、容易上手等优势大行其道。同时，有些特殊功能（如AJAX）必须依赖JavaScript在客户端提供支持。随着JavaScript引擎（如V8）和软件框架（如Node.js）的发展，以及事件驱动和异步IO等特性，JavaScript也被逐渐用来编写服务器端程式。

以下是ECMAScript通常所实现的特性。

### 指令式与结构化

JavaScript支持许多C语言的结构化编程语法（如if条件语句、while循环、switch语句和do-while循环等），但作用域是一个例外。JavaScript在过去只支持使用`var`关键字来定义变量的函数作用域，但ECMAScript 2015中加入了`let`关键字来支持块级作用域。这意味着JavaScript现在既支持函数作用域又支持块级作用域。JavaScript还支持自动在语句末添加分号，允许忽略语句末尾的分号。

### 弱类型

JavaScript是弱类型的，这意味着变量可以被隐式转换为另一个类型。

- 二元运算符`+`会把两个操作数转换为字符串，除非两个操作数都为数字类型。这是因为`+`也表示字符串连接操作。
- 二元操作符`-`会把两个操作数转换为数字类型。
- 一元操作符，包括`+`和`-`，都会把操作数转换为数字。

下列为变量转换为字符串的例子：

- 字符串类型不变。
- 数字会转换为其字符串表示。
- 数组的元素会转换为字符串，然后连接成通过逗号`,`分隔的长字符串。
- 其它对象会转换为`[object Object]`，其中`Object`中该对象的构造函数名。

类型的隐藏转换是JavaScript受到批评的原因之一，因为隐藏转换增加了规则的复杂度和发生错误的可能性。

左操作数｜操作符｜右操作数｜结果

`[]`（空数组）｜`+`｜`[]`（空数组）｜`""`（空字符串）

`[]`（空数组）｜`+`｜`{}`（空对象）｜`"[object Object]"`（字符串）

`false`（布尔值）｜`+`｜`""`（空字符串）｜`"false"`（字符串）

`"123"`（字符串）｜`+`｜`1`（数字）｜`"1231"`（字符串）

`"123"`（字符串）｜`-`｜`1`（数字）｜`122` （数字）

### 动态化

#### 类型

JavaScript是动态类型的语言，其类型与值而不是与变量相关联。例如变量可以为数字，随后又可被赋值为字符串。JavaScript提供了包括鸭子类型在内的方法来检测变量类型。

#### 运行时估值

JavaScript提供`eval()`函数，可以在运行时直接执行JavaScript语句。

### 基于原型的面向对象

在JavaScript中，对象是关联数组，通过原型（`prototype`，见下）进行扩充。每一个字符串键值提供对象的一个属性的名称，可以通过使用点号（`obj.x`）或使用方括号（`obj['x']`）这两种效果相同的方式来访问。属性可以在运行时添加、重定义或删除。一个对象的大多数属性（包括来自原型继承链的属性）都可以通过 `for...in`循环访问。

#### 原型

JavaScript使用原型，而许多其它面向对象程序设计使用类用于实现继承。原型使得在JavaScript中模拟基于类的面向对象特征变成可能。

#### 函数作为对象构造器

函数在JavaScript中兼作为对象构造函数。在函数调用前加上`new`会创建一个原型的实例，并继承来自构造函数的属性和方法（包括来自`Object`原型）。ECMAScript 5提供`Object.create`方法，可以显式地创建实例而不是自动从`Object`继承。构造函数的`prototype`属性决定了用于新对象的内部原型。可以通过修改构造函数的原型的方法来为对象添加新的方法，也可以修改JavaScript的内部对象的原型（如`Array`或`Object`）。尽管可以这么做，但对`Object`原型进行修改并不是好的做法。因为大多数JavaScript对象都会从`Object`继承，且不会希望其原型被修改。

#### 函数作为方法

和大多数面向对象的语言不同，在JavaScript中函数定义和方法定义没有明显区别。唯一的区别在于调用时：当函数被作为方法调用时，函数的`this`会指向调用此函数的对象。

#### 传统的类定义与使用格式

ECMAScript 2015中加入了对`class`和`extends`关键字的支持，使得类的定义与继承更类似于其他的面向对象语言，同时也更易使用。

### 函数式

在JavaScript中，函数是一等的，函数也被认为是对象。因此，函数可以有属性和方法，例如`call()`和`bind`等。嵌套函数指定义于其它函数内部的函数，在外部函数被调用时，嵌套函数会被创建。另外，嵌套函数是一个闭包，在外部函数的作用域（包括常量，局部变量和参数）都成为内部函数状态的一部分，甚至在外部函数执行完毕后，内部函数的状态依然保留。JavaScript同时也支持匿名函数。

### 其它

#### 运行时环境

JavaScript通常依赖于运行时环境（例如浏览器）来提供对象和方法，脚本可以通过这些对象和方法与环境（例如网页DOM）进行交互。它还依赖于运行时环境来提供包或导入脚本（例如HTML的`<script>`元素）的功能。这本身不是语言功能，但在大多数JavaScript实现中很常见。

#### 承诺

JavaScript的“承诺”（`Promise`）是一种编程模型，它允许表示可能在未来某个时间点完成或失败的值。承诺被设计为一个解决异步编程中所遇到的问题的更简洁和一致的方法。相比于传统的回调函数，它为处理异步操作提供了一个更清晰的方式。

一个承诺有三种状态：

待定（Pending）: 初始状态，既不是成功也不是失败。

已履行（Fulfilled）: 意味着操作成功完成。

已拒绝（Rejected）: 意味着操作失败。

承诺的主要特点是它们的状态一旦改变（从待定到已履行或已拒绝），就不能再次改变。这使得承诺成为一个可靠的信息来源，无论它们是否成功。

在JavaScript中，你可以使用`then()`方法来附加回调函数，这些回调函数会在承诺达到已履行或已拒绝状态时执行。还有一个`catch()`方法，专门用于处理被拒绝的承诺。为了使链式调用更简洁，`then()`和`catch()`都会返回一个新的承诺。ES6（ECMAScript 2015）引入了原生的`Promise`对象，从此承诺成为JavaScript标准的一部分。此后的版本还提供了额外的实用方法，例如`Promise.all()`和`Promise.race()`，用于处理多个承诺。

#### 异步

JavaScript一般来说是单线程的。为了并发地处理事件，JavaScript程序输入或输出时使用事件和回调函数执行。这意味着JavaScript可以在等待数据库查询返回信息之前处理鼠标单击。ECMAScript 2015引入了`Promise`用于处理异步事件，其可以使得传统的基于回调的异步代码更加清晰简单。

#### 变长参数

JavaScript中函数参数的长度是可变的，在函数内部可以通过`arguments`对象访问这些参数。

## 编程

JavaScript是一门脚本语言，其源代码在客户端执行前不需经过编译，而是将文本格式的字符代码发送给浏览器，由浏览器解释执行。直译语言的弱点是安全性较差，而且在JavaScript中，如果一条语句执行不了，通常它下面的语言也就无法执行。解决办法是使用异常处理`try {} catch () {}`︰

```javascript
console.log("a");  //这是正确的
console.log("b");  //这是正确的
console.logg("c"); //这是错误的，并且到这里会停下来
console.log("d");  //这是正确的
console.log("e");  //这是正确的

/* 解决办法 */
try { console.log("a"); } catch (e) {}  //这是正确的
try { console.log("b"); } catch (e) {}  //这是正确的
try { console.logg("c"); } catch (e) {} //这是错误的，但是到这里不会停下来，而是跳过
try { console.log("d"); } catch (e) {}  //这是正确的
try { console.log("e"); } catch (e) {}  //这是正确的
```

JavaScript被归类为直译语言，因为主流的引擎都是每次执行时载入程式码并解译。V8是将所有程式码解译后再开始执行，其他引擎则是逐行解译（SpiderMonkey会将解译过的指令暂存，以提高效能，称为即时编译）。但由于V8的核心部分多数用JavaScript撰写（而SpiderMonkey是用C++），因此在不同的测试上，两者效能互有优劣。

与其相对应的是编译语言（如C语言），以编译语言编写的程式在执行之前必须经过编译，将程式码编译为机器码，才可以执行。

## 范例

以下是一个简单的JavaScript Hello World︰

```html5
<!DOCTYPE HTML>
<html>
    <head>
    <title>简单的JavaScript Hello World</title>
        <script>
            document.write("Hello, world!");   // 直接插入页面中
            alert("Hello, world!");            // 弹窗显示
            console.log("Hello, world!");      // 在控制台（console）里显示，需要先开启开发工具控制台
        </script>
    </head>
    <body>
　　　　HTML内容……
    </body>
</html>
```

或是在浏览器的地址栏中使用`javascript:`，以互动方式表示：

```javascript
javascript:alert("Hello world!");
```

## 版本

JavaScript最初开发于1996年，被使用于Netscape Navigator网页浏览器。同年微软在Internet Explorer发布了一个实作。由于商标问题，这项实作被命名为JScript。1997年，JavaScript已经被网景公司提交给ECMA制定为标准，称之为ECMAScript，标准编号ECMA-262。使用显式版本号声明对语言的引用，作为一项Mozilla的特性，已在较新版本中被移除（至少为Firefox 59）。Firefox 4是最后一个需要显式地在引用时声明明确版本号的版本（1.8.5）。

下列表格的资讯基于多个参考来源：

版本｜发布日期｜基于｜NetscapeNavigator｜MozillaFirefox｜InternetExplorer｜Opera｜Safari｜GoogleChrome

o｜1996年3月｜2.0｜3.0

o｜1996年8月｜3.0

o｜1997年6月｜4.0 - 4.05｜3

o｜1998年10月｜ECMA-262 1st + 2nd edition｜4.06 - 4.7x｜4.0｜5

o｜NetscapeServer｜6

o｜2000年11月｜ECMA-262 3rd edition｜6.0｜1.0｜5.5（JScript 5.5）6（JScript 5.6）7（JScript 5.7）8（JScript 5.8）｜7.0｜3.0-5｜1.0 - 10.0.666

o｜2005年11月｜1.5 + Array extras + Array and string generics + E4X｜1.5

o｜2006年10月｜1.6 + Pythonic generators + Iterators + Let｜2.0｜28.0.1500.95

o｜2008年6月｜1.7 + Generator expressions + Expression closures｜3.0｜11.50

o｜1.8 + native JSON support + Minor updates｜3.5

o｜2009年6月22日｜1.8.1 + Minor updates｜3.6

o｜2010年7月27日｜1.8.2 + New features for ECMA-262 5th edition compliance｜4.0

## 外部链接

- 关于JavaScript介绍 Mozilla Developer Network上的资源

## 基本资料

- 编程范型：事件驱动、函数式、指令式
- 发行时间：1995年12月4日
- screenshot caption：JavaScript源代码截图
- 设计者：创造者布兰登·艾克及ECMAScript规范的其他关键贡献者
- 类型系统：动态类型、鸭子类型
- 文件扩展名：`.js`、`.cjs`、`.mjs`
- 软件实现：V8、JavaScriptCore、SpiderMonkey、Chakra
- 受影响于：AWK、C、HyperTalk、Java、Lua、Perl、Python、Scheme、Self
- 施影响于：ActionScript、AtScript、CoffeeScript、Dart、JScript .NET、LiveScript、Objective-J、Opa、QML、Raku、TypeScript
- 扩展名：.js
- mime：application/javascript、text/javascript (obsolete)
- 统一类型标识：com.netscape.javascript-source
- 类型：脚本语言