JavaScript(通常縮寫為JS)是一種進階的、直譯的程式語言。JavaScript是一門基於原型、函式先行的語言,是一門多範式的語言,它支援物件導向程式設計,指令式程式設計,以及函式語言程式設計。它提供語法來操控文字、陣列、日期以及正規表示式等,不支援I/O,比如網路、儲存和圖形等,但這些都可以由它的宿主環境提供支援。它已經由ECMA(歐洲電腦製造商協會)透過ECMAScript實作語言的標準化。它被世界上的絕大多數網站所使用,也被世界主流瀏覽器(Chrome、IE、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 Communications的公司在加州山景城成立了,並雇用了許多原來的NCSA Mosaic開發者用來開發Mosaic Netscape,該公司的目標是取代NCSA Mosaic成為世界第一的網頁瀏覽器。第一個版本的網頁瀏覽器Mosaic Netscape 0.9於1994年底發布。在四個月內,已經佔據了四分之三的瀏覽器市場,並成為1990年代網際網路的主要瀏覽器。為避免NCSA的商標所有權問題,該瀏覽器於同年更名為Netscape Navigator,該公司命名為Netscape Communications。網景預見到網路需要變得更動態。公司的創始人馬克·安德森認為HTML需要一種膠水語言,讓網頁設計師和兼職程式設計師可以很容易地使用它來組裝圖片和外掛程式之類的元件,且程式碼可以直接編寫在網頁標記中。
1995年,網景招募了布蘭登·艾克,目標是把Scheme語言嵌入到Netscape Navigator瀏覽器當中。但更早之前,網景已經跟昇陽合作在Netscape Navigator中支援Java,這時網景內部產生激烈的爭論。後來網景決定發明一種與Java搭配使用的輔助手稿語言並且語法上有些類似,這個決策導致排除了採用現有的語言,例如Perl、Python、Tcl或Scheme。為了在其他競爭提案中捍衛JavaScript這個想法,公司需要有一個可以運作的原型。艾克在1995年5月僅花了十天時間就把原型設計出來了。
最初命名為Mocha,1995年9月在Netscape Navigator 2.0的Beta版中改名為LiveScript,同年12月,Netscape Navigator 2.0 Beta 3中部署時被重新命名為JavaScript,當時網景公司與昇陽電腦公司組成的開發聯盟為了讓這門語言搭上Java這個程式語言「熱詞」,因此將其臨時改名為JavaScript,日後這成為大眾對這門語言有諸多誤解的原因之一。
微軟採納
JavaScript推出後在瀏覽器上大獲成功,微軟公司在不久後就為Internet Explorer 3瀏覽器推出了JScript,以與處於市場領導地位的網景產品同台競爭。JScript也是一種JavaScript實作,這兩個JavaScript語言版本在瀏覽器端共存意味著語言標準化的缺失,發展初期,JavaScript的標準並未確定,同期有網景的JavaScript,微軟的JScript雙峰並峙。除此之外,微軟也在網頁技術上加入了不少專屬物件,使不少網頁使用非微軟平台及瀏覽器無法正常顯示,導致在瀏覽器大戰期間網頁設計者通常會把「用Netscape可達到最佳效果」或「用IE可達到最佳效果」的標誌放在首頁。隨著Internet Explorer 4的發布,微軟引入了動態HTML的概念,但語言實現和不同專有化的文件物件模型的差異仍然存在,成為網路上普及使用JavaScript的阻礙。
標準化
1996年11月,網景正式向ECMA(歐洲電腦製造商協會)提交語言標準。1997年6月,ECMA以JavaScript語言為基礎制定了ECMAScript標準規範ECMA-262。JavaScript成為了ECMAScript最著名的實現之一。除此之外,ActionScript和JScript也都是ECMAScript規範的實作語言。儘管JavaScript作為給非程式人員的手稿語言,而非作為給程式人員的程式語言來推廣和宣傳,但是JavaScript具有非常豐富的特性。
概論
一般來說,完整的JavaScript包括以下幾個部分:
JavaScript的基本特點如下:
JavaScript常用來完成以下任務:
特性
不同於伺服器端手稿語言,例如PHP與ASP,JavaScript主要被作為客戶端手稿語言在用戶的瀏覽器上運行,不需要伺服器的支援。所以在早期程式設計師比較青睞於JavaScript以減少對伺服器的負擔,而與此同時也帶來另一個問題:安全性。而隨著伺服器變得強大,現在的程式員更喜歡運行於伺服器端的指令碼以保證安全,但JavaScript仍然以其跨平台、容易上手等優勢大行其道。同時,有些特殊功能(如AJAX)必須依賴JavaScript在客戶端進行支援。隨著引擎如V8和框架如Node.js的發展,及其事件驅動及異步IO等特性,JavaScript逐漸被用來編寫伺服器端程式。且在近幾年中,Node.js的出世,讓JavaScript也具有了一定的伺服器功能。
以下是ECMAScript通常實作所共有的特性。
指令式與結構化
JavaScript從支援許多C語言的結構化編程語法(例如if條件語句、while迴圈、switch語句、do-while迴圈等)。但作用域是一個例外:JavaScript在過去只支援使用var關鍵字來定義變數的函式作用域。ECMAScript 2015加入了let關鍵字來支援塊級作用域[20]。意味著JavaScript現在既支援函式作用域又支援塊級作用域。和C語言一樣,JavaScript中的表達式和語句是不同的。有一點格式上的不同,JavaScript支援自動在語句末添加分號,因此允許忽略語句末尾的分號。
弱型別
Javascript是弱型別的,這意味著變數可以被隱式地轉換為另一個類型。
+
會把兩個運算元轉換為字串,除非兩個運算元都為數字類型。這是因為+也表示字串連接操作。-
會把兩個運算元轉換為數字類型。+
和-
,都會把運算元轉換為數字。下列為變數轉換為字串的例子:
類型的隱藏轉換,是JavaScript受到批評的原因之一,因為其增加了規則的複雜度與發生錯誤的可能性。
動態化
類型
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 ES6加入了對class、extends關鍵字的支援,使得類的定義與繼承行為更加類似於其它物件導向語言,同時也更加容易使用。
函數式
在JavaScript中,函式是一等的,函式也被認為是物件。因此,函式也可以有屬性與方法,例如call()和bind等。巢狀函式指定義於其它函式內部的函式,在外部函式被呼叫時,巢狀函式會被建立。另外,巢狀函式是一個閉包,在外部函式的作用域(包括常數,局部變數和參數)都成為內部函式狀態的一部分,甚至在外部函式執行完畢後,內部函式的狀態依然保留。JavaScript同時也支援匿名函式。
其它
執行時環境
JavaScript通常依賴於執行時環境(例如瀏覽器)來提供物件和方法,指令碼可以透過這些物件和方法與環境(例如網頁DOM)進行互動。它還依賴於執行時環境來提供包含/匯入指令碼(例如HTML的<script>元素)的功能。這本身不是語言功能,但是在大多數JavaScript實作中很常見。
非同步
JavaScript一般來說是單執行緒的。為了並行地處理事件,JavaScript程式輸入/輸出是使用事件和回呼函式執行的。例如,這意味著JavaScript可以在等待資料庫查詢返回資訊時處理滑鼠單擊。ECMAScript ES6引入了Promise用於優雅地處理非同步事件,其可以使得傳統的基於回呼的非同步代碼更加清晰與簡單。
變長參數
JavaScript中函式的參數長度是可變的,在函式內部可以透過arguments物件存取這些參數。
編程
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語言,以編譯語言編寫的程式在執行之前,必須經過編譯,將程式碼編譯為機器碼,再加以執行。
範例
以下是一個簡單的JavaScript Hello World︰
<!DOCTYPE HTML>
<html>
<head>
<title>簡單的JavaScript Hello World</title>
<script type="text/javascript">
document.write("Hello, world!"); // 於瀏覽器視窗內直接顯示
alert("Hello, world!"); // 開啟對話視窗顯示
console.log("Hello, world!"); // 於console裡顯示,需要先開啟開發工具控制台
</script>
</head>
<body>
HTML內容……
</body>
</html>
或是在瀏覽器的網址列中使用javascript:,以互動方式表示:
javascript:alert("Hello world!");
JavaScript引擎是一個專門處理JavaScript指令碼的虛擬機器,一般會附帶在網頁瀏覽器之中。
在2008年到2009年的第二次瀏覽器大戰之前,JavaScript引擎僅簡單地被當作能閱讀執行JavaScript原始碼的直譯器。
第一個JavaScript引擎由布蘭登·艾克在網景公司開發,用於Netscape Navigator網頁瀏覽器中。引擎的名字叫做SpiderMonkey,由C++實作。它自JavaScript 1.5升級以符合ECMA-262版本3。Rhino引擎,由網景公司的諾里斯·博伊德(Norris Boyd)開發,由Java實作。像SpiderMonkey一樣,Rhino符合ECMA-262版本3。JavaScript引擎的應用例子還包括:Apple Safari 4的Nitro,Google Chrome的V8和Mozilla Firefox 3.5的TraceMonkey。
目前為止,最通用的JavaScript宿主環境是網頁瀏覽器。網頁瀏覽器一般使用公共的API建立「宿主物件」以便於在JavaScript中支援DOM。
一個典型的瀏覽器有一個圖形引擎和一個獨立的JavaScript引擎。這樣JavaScript引擎能夠被更方便的測試、重新生成或者在另一些專案中使用。例如:Carakan被用在Presto中,Nitro被用在WebKit中,SpiderMonkey被用在Gecko中,KJS被用在KHTML中,Rhino預設不包含任何布局引擎。但還有其他組合,例如:V8與WebKit被用於Google Chrome中。JavaScript引擎能為程式設計師提供部分操作瀏覽器的功能(網路、DOM、外部事件、HTML5影片、canvas和儲存)。
Sunspider是一個JavaScript實用基準測試通過一系列關於JavaScript語言的指令碼測試JavaScript引擎的速度。Sunspider不測試無關於JavaScript的特性(不使用HTML、CSS和無網路情況下)。
近幾年來,在瀏覽器開發者之間展開了一場開發更快的JavaScript引擎的競賽。2008年,Google Chrome因它的JavaScript效能而倍受稱讚,但是其它瀏覽器馬上使用了更快的JavaScript引擎。之後,Google Chrome在效能上獲得領先,其長處在於它高速的效能和JavaScript的處理速度,這些都由許多網站在主要的瀏覽器之間的速度測試中得到驗證[1][2][3]。隨著WebKit的Squirrelfish Extreme和Mozilla的TraceMonkey JavaScript虛擬機器出現,Chrome的JavaScript執行速度就被超越了[4][5][6][7]。Google丹麥在Chrome 2中開發了更快速的V8引擎。
2008年6月2日,WebKit開發團隊發布了SquirrelFish[8]——一個能極大地提升Safari解釋指令碼速度的新的JavaScript引擎[9]。該引擎是Safari 4其中一個新特性,在2008年6月11日發布給程式設計師使用;最終此JavaScript引擎被稱為Nitro。2009年6月30日,在Firefox 3.5發布的最新技術能「使某些情況下的速度提升20到40倍」[10]。
仍在持續開發中的著名引擎
JavaScript是一種ECMAScript方言,在許多程式中得以實作,特別是在網頁瀏覽器。這些方言通常擴充了語言,或者標準庫和相關API,例如W3C定義的DOM。這意味著以一種方言實作的程式不相容於另一種方言的實作,除非程式使用了方言中的公共子集所具有的特性和API。
在實作和方言中存在著一些差別。一種語言的方言有一些與語言不同的地方。實作能夠執行該語言或方言編寫的程式。
Node.js允許通過JavaScript和一系列模組來編寫伺服器端應用和網路相關的應用。核心模組包括檔案系統I/O、網路(HTTP、TCP、UDP、DNS、TLS/SSL等)、二進位資料流、加密演算法、資料流等等。Node模組的API形式簡單,降低了編程的複雜度。
使用框架可以加速開發。常用的框架有Express.js、Socket.IO和Connect等。Node.js的程式可以在Microsoft Windows、Linux、Unix、Mac OS X等伺服器上執行。Node.js也可以使用CoffeeScript(一種旨在簡化JavaScript的替代語言,其代碼可按照一定規則轉化為合法的JavaScript代碼)、TypeScript(微軟開發的強化了資料類型的JavaScript變體)、Dart語言,以及其他能夠編譯成JavaScript的語言編程。
Node.js主要用於編寫像Web伺服器一樣的網路應用,這和PHP和Python是類似的。但是Node.js與其他語言最大的不同之處在於,PHP等語言是阻塞的(只有前一條命令執行完畢才會執行後面的命令),而Node.js是非阻塞的(多條命令可以同時被執行,通過回呼函式得知命令已結束執行)。
Node.js是事件驅動的。開發者可以在不使用執行緒的情況下開發出一個能夠承載高並行的伺服器。其他伺服器端語言難以開發高並行應用,而且即使開發出來,效能也不盡如人意。Node.js正是在這個前提下被創造出來。Node.js把JavaScript的易學易用和Unix網路編程的強大結合到了一起。
Node.js使用Google V8 JavaScript引擎,因為:
Node.js已經有數十萬模組,它們可以通過一個名為npm的管理器免費下載。Node.js開發社群主要有兩個郵寄清單、一個在freenode的名為#node.js的IRC頻道。社群集中在NodeConf。