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!" );