JavaScript (通常缩写为JS )是一门基于原型 和头等函数 的多范式高级 解释型 编程语言 [ 9] [ 10] ,它支持面向对象 程式设计、指令式编程 和函数式编程 。它提供方法来操控文本、数组 、日期以及正则表达式 等。不支持I/O ,比如网络、存储和图形等,但这些都可以由它的宿主环境提供支持。它由Ecma 通过ECMAScript 实现语言的标准化[ 9] 。目前,它被世界上的绝大多数网站所使用,也被世界主流浏览器 (Chrome 、Firefox 、Safari 和Opera )所支持。
JavaScript与Java 在名字和语法上都很相似,但这两门编程语言从设计之初就有很大不同。JavaScript在语言设计上主要受到了Self (一种基于原型的编程语言)和Scheme (一门函数式编程语言)的影响[ 10] ,在语法结构上它和C语言 很相似(如if条件语句、switch语句、while循环和do-while循环等)[ 11] 。
对于客户端来说,JavaScript通常被实现为一门解释语言,但如今它已经可以被即时编译 (JIT)。随着HTML5 和CSS3 语言标准的推行,它还可以用于游戏、桌面和移动应用程序的开发,以及在服务器端网络环境运行(如Node.js )。
1993年,国家超级电脑应用中心 (NCSA)发表了NCSA Mosaic ,这是最早流行的图形使用者介面 网页浏览器,它在全球资讯网 的普及上发挥了重要作用[ 12] 。1994年,Mosaic的主要开发人员创立了Netscape 公司,并雇用了许多原来的NCSA Mosaic开发者用来开发Netscape Navigator ,该公司的目标是取代NCSA Mosaic成为世界第一的网页浏览器。在四个月内,已经占据了四分之三的浏览器市场,并成为1990年代网际网路的主要浏览器[ 13] 。
在网路发展的这些年,网页只能是静态的,缺乏在浏览器中载入网页后的动态行为能力。公司的创始人马克·安德森 认为HTML 需要一种胶水语言 ,让网页设计师和兼职程式设计师可以很容易地使用它来组装图片和外挂程式之类的元件,且程式码可以直接编写在网页标记中。1995年,网景招募了布兰登·艾克 ,目标是把Scheme 语言嵌入到Netscape Navigator浏览器中[ 14] 。但更早之前,网景已经跟昇阳 合作,计划在Netscape Navigator中嵌入Java 语言,这时网景内部产生激烈的争论[ 15] ,
网景公司管理层很快决定,最佳的方案是由艾克设计一种新的语言,其语法类似于Java,而不像Scheme或其他现存的脚本语言 。为了在其他竞争提案中捍卫JavaScript这个想法,公司需要有一个可以运作的原型。艾克在1995年5月仅花了十天时间就把原型设计出来了[ 16] [ 17] 。最初命名为Mocha ,1995年9月在Netscape Navigator 2.0的Beta版中改名为LiveScript ,同年12月在Netscape Navigator 2.0 Beta 3中部署时又被重命名为JavaScript [ 1] [ 18] ,这是因为当时网景公司与昇阳电脑公司 组成的开发联盟为了让这门语言搭上Java 这个编程语言“热词 ”,所以才将其临时改名为JavaScript,这成了日后这成为大众对这门语言有诸多误解的原因之一[ 19] 。同年昇阳电脑公司 在1995年12月1日正式申请注册JavaScript的商标,于2000年7月28日获批[ 20] ,但此后昇阳电脑公司 和之后收购它的甲骨文公司 从未使用该商标制造过产品,给JavaScript社区带来了不确定性,阻碍了JavaScript社区的自由发展,直到几十年后,1.5万多名JavaScript社区成员于2024年11月22日联名向美国专利商标局 申请终止该商标[ 21] [ 22] ,甲骨文公司 于2025年2月3日以Node.js 产品驳回了该申请案[ 23] 。
微软公司 于1995年首次推出Internet Explorer ,引发了与Netscape的浏览器大战 。微软对Netscape Navigator直译器 进行了逆向工程 ,创建了JScript ,以与处于市场领导地位的网景产品同台竞争。JScript也是一种JavaScript实作,这两个JavaScript语言版本在浏览器端共存意味著语言标准化的缺失。发展初期,JavaScript的标准并未确定,同期就有网景的JavaScript和微软的JScript。除此之外,微软也在网页技术上加入了不少专属物件,使不少网页使用非微软平台及浏览器无法正常显示[ 24] [ 25] 。这导致在浏览器大战期间网页设计者通常会把“用Netscape可达到最佳效果”或“用IE可达到最佳效果”的标志放在首页[ 24] [ 26] 。
1996年11月,网景正式向ECMA (欧洲计算机制造商协会)提交语言标准。1997年6月,ECMA以JavaScript语言为基础制定了ECMAScript 标准规范ECMA-262。JavaScript成为了ECMAScript最著名的实现之一[ 27] 。除此之外,ActionScript 和JScript 也都是ECMAScript 规范的实现语言。尽管JavaScript作为给非程式人员的脚本语言,而非作为给程式人员的程式语言来推广和宣传,但是JavaScript具有非常丰富的特性。
在21世纪初Internet Explorer 占主导地位期间,客户端脚本停滞不前。这在2004年开始改变,当时Netscape的继任者Mozilla发布了Firefox浏览器。Firefox受到许多人的好评,从Internet Explorer获得了巨大的市场份额。[ 28]
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 ( 英语 : MooTools ) 。
谷歌 于2008年首次推出Chrome浏览器,其V8JavaScript引擎 比竞争对手更快。[ 29] 关键的创新是及时编译(JIT)[ 30] ,因此其他浏览器供应商需要为JIT彻底改革他们的引擎[ 31]
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关键字来支持块级作用域[ 32] 。这意味着JavaScript现在既支持函数作用域又支持块级作用域。JavaScript还支持自动在语句末添加分号,允许忽略语句末尾的分号。[ 33]
JavaScript是弱类型 的,这意味着变量可以被隐式转换为另一个类型。[ 34]
二元运算符+会把两个操作数转换为字符串,除非两个操作数都为数字类型。[ 35] 这是因为+也表示字符串连接操作; 二元操作符-会把两个操作数转换为数字类型;[ 36] 一元操作符,包括+和-,都会把操作数转换为数字。 下列为变量转换为字符串的例子:
字符串类型不变; 数字会转换为其字符串表示; 数组的元素会转换为字符串,然后连接成通过逗号,分隔的长字符串; 其它对象会转换为[object Object],其中Object中该对象的构造函数 名。[ 37] 类型的隐藏转换是JavaScript受到批评的原因之一,因为隐藏转换增加了规则的复杂度和发生错误的可能性。[ 38]
JavaScript中的隐式转换[ 39] [ 40] : 左操作数 操作符 右操作数 结果 [](空数组)+[](空数组)""(空字符串)[](空数组)+{}(空对象)"[object Object]"(字符串)false(布尔值)+""(空字符串)"false"(字符串)"123"(字符串)+1(数字)"1231"(字符串)"123"(字符串)-1(数字)122 (数字)
JavaScript是动态类型 的语言,其类型与值而不是与变量相关联。例如变量可以为数字,随后又可被赋值为字符串。JavaScript提供了包括鸭子类型 在内的方法来检测变量类型。
JavaScript提供eval ()函数,可以在运行时直接执行JavaScript语句。[ 41]
在JavaScript中,对象是关联数组 ,通过原型(prototype,见下)进行扩充。每一个字符串键值提供对象的一个属性的名称,可以通过使用点号(obj.x)或使用方括号(obj['x'])这两种效果相同的方式来访问。属性可以在运行时添加、重定义或删除。一个对象的大多数属性(包括来自原型继承链的属性)都可以通过for...in循环访问。[ 42]
JavaScript使用原型 ,而许多其它面向对象程序设计 使用类用于实现继承。原型使得在JavaScript中模拟基于类的面向对象特征变成可能。[ 43]
函数在JavaScript中兼作为对象构造函数。在函数调用前加上new会创建一个原型的实例,并继承来自构造函数的属性和方法(包括来自Object原型)。[ 44] ECMAScript 5提供Object.create方法,可以显式地创建实例而不是自动从Object继承。[ 45] 构造函数的prototype属性决定了用于新对象的内部原型。可以通过修改构造函数的原型的方法来为对象添加新的方法,也可以修改JavaScript的内部对象的原型(如Array或Object)。尽管可以这么做,但对Object原型进行修改并不是好的做法。因为大多数JavaScript对象都会从Object继承,且不会希望其原型被修改。[ 46]
和大多数面向对象的语言不同,在JavaScript中函数定义和方法 定义没有明显区别。唯一的区别在于调用时:当函数被作为方法调用时,函数的this会指向调用此函数的对象。[ 47]
ECMAScript 2015中加入了对class和extends关键字的支持,使得类的定义与继承更类似于其他的面向对象语言,同时也更易使用。[ 48] [ 49]
在JavaScript中,函数是一等 的,函数也被认为是对象。因此,函数可以有属性和方法,例如call()和bind等。[ 50] 嵌套函数指定义于其它函数内部的函数,在外部函数被调用时,嵌套函数会被创建。另外,嵌套函数是一个闭包 ,在外部函数的作用域(包括常量,局部变量和参数)都成为内部函数状态的一部分,甚至在外部函数执行完毕后,内部函数的状态依然保留[ 51] 。JavaScript同时也支持匿名函数 [ 52] 。
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一般来说是单线程 的。[ 53] 为了并发 地处理事件,JavaScript程序输入或输出时使用事件 和回调函数 执行。这意味着JavaScript可以在等待数据库 查询返回信息之前处理鼠标 单击。ECMAScript 2015引入了Promise用于处理异步事件,其可以使得传统的基于回调的异步代码更加清晰简单。[ 54] [ 55]
JavaScript中函数参数的长度是可变 的,在函数内部可以通过arguments对象访问这些参数。[ 56]
JavaScript是一门脚本语言 ,其原始码 在客户端执行前不需经过编译,而是将文本格式的字符代码发送给浏览器,由浏览器解释执行。直译语言 的弱点是安全性较差,而且在JavaScript中,如果一条语句执行不了,通常它下面的语言也就无法执行。解决办法是使用异常处理 try {} catch () {}︰
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语言 ),以编译语言编写的程式在执行之前必须经过编译,将程式码编译为机器码,才可以执行。
以下是一个简单的JavaScriptHello World ︰
<!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 > 或是在浏览器 的地址栏 中使用#"ltr">javascript : alert ( "Hello world!" );