天天看點

Node.js的介紹

作者:sagittarius-rev

連結:https://www.zhihu.com/question/31305968/answer/116439739

來源:知乎

著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

1. JavaScript

JavaScript(簡稱js)是一種主要運作于浏覽器中的弱類型的動态腳本語言,可以用來實作網頁上的一些進階功能,如資料驗證處理、頁面動态效果、定時任務、與使用者互動、發送/接收伺服器端資料等等。

動态語言指的是程式運作時可以改變結構,主要展現在:

① js中的變量在聲明的時候不需要指定類型,其實際類型由程式運作中的指派決定,在運作過程中變量的類型也可以改變。注:這一點是動态語言的特征,并不是弱類型語言的特征,之前的回答有誤。

② 函數可變。js允許在運作過程中使用eval動态執行字元串裡的指令,也可以通過new Function等方式由字元串動态構造函數,函數可以被建立、修改、删除,可以從已有函數構造出新函數,等等。

③ 對象的成員可變,可以動态添加、删除成員屬性或成員方法。

弱類型指的是js中的變量在參與運算的時候可以根據實際需要動态轉換類型。與之相對應的是強類型語言——變量一般不允許自動轉換類型(某些強類型語言的字元串連接配接操作除外),如果參與運算、調用時不符合要求的類型,則會在編譯階段報錯。

js是1995年由Netscape公司的Brendan Eich為自家的浏覽器Netscape Navigator開發的,當時意圖是用于網頁上的表單驗證,即驗證表單的各個輸入項是否符合預定規則,在驗證通過後才向伺服器送出表單内容,減少頁面與伺服器端不必要的頻繁互動。

js的最初版本隻用了10天就開發完成,當然不是完全從無到有,而是借鑒了其他一些語言的特性來開發。如此倉促開發,js自然有一些先天不足,但同時也具備了基于弱類型動态語言的友善靈活、對象原型繼承、函數是一種特殊的對象等優秀特性,于是越來越得到廣泛應用,而語言自身也在标準化組織的推動下不斷發展進步。

在浏覽器發展的早期,Microsoft仿造JavaScript推出了相似的腳本語言JScript,在IE浏覽器中使用,Microsoft同時推出的還有VBScript。後來為了解決不同浏覽器中腳本語言不相容的問題,在ECMA(歐洲計算機制造商協會)成立了标準化小組,由各廠商參與,共同制定JavaScript的語言規範,規範化的這門語言被命名為ECMAScript。

js也可以在浏覽器之外的其他場合使用,如伺服器端的Node.js、java的Rhino、無界面浏覽器PhantomJS等。

2. jQuery

jQuery是js的一個工具庫,由John Resig在2006年釋出。

j代表JavaScript,query是“查詢”的意思。也就是說,這個庫的意圖是基于JavaScript的查詢。

查詢的目标是什麼?答案是DOM(文檔對象模型)結構中的Node(節點)。一個網頁就是一個html文檔,而網頁上的所有内容都是節點,包括文檔節點、元素節點、文本節點、注釋節點、屬性節點等等。而jQuery的查詢最主要針對的是元素節點,如段落(p)、錨點(a)、表格(table)等,隻有少數方法可以處理文本節點與注釋節點。同時jQuery還可以用attr方法友善地對元素節點的屬性進行讀取/設定。

在jQuery出現之前,在js程式中擷取元素節點比較麻煩,例如擷取id為elem1的節點:

document.getElementById('elem1')           

複制

或者是擷取頁面上的所有checkbox元素,首先需要擷取input類型的元素:

document.getElementsByTagName('input')           

複制

然後對獲得的元素清單進行for循環處理,逐個判斷其類型是否為checkbox。

如果有更多元化的查詢要求,則對應的js代碼也會相當複雜。雖然有一些庫可以解決這方面的需求,但強大程度、易用性等方面都不太理想。

John Resig發現了一個盲點——css樣式應用到頁面上的元素時,是有一套規則的,即css選擇器,浏覽器可以通過css選擇器找到比對的元素并将指定的樣式應用到這些元素上。也就是說,通過css選擇器可以有效地進行元素查找定位,但它最初隻被用于樣式領域。于是,John Resig根據css選擇器編寫了jQuery選擇器,并對選擇器的規則進行了擴充,進而讓元素查找變得非常友善。例如,上面2個例子用jQuery可以寫為:

$('#elem1')           

複制

$(":checkbox")           

複制

同時,jQuery還有一個核心思想——鍊式操作,例如:

$('div.con')
    .height(100)
    .show();           

複制

這樣的連續調用可以讓代碼書寫更加簡潔,也就是jQuery自己的口号:write less, do more。

此外,jQuery還提供了浏覽器相容、樣式讀寫、事件綁定與執行、動畫等特性,後來又加入了ajax、promise等,再加上友善的插件編寫機制,對整個js的生态圈産生了重大的影響,可以說是js曆史上影響力最大的一個庫。其中選擇器引擎後來被單獨剝離出來成為sizzle,供其他的js庫調用。這部分的工作還影響了官方,在jQuery成功之後,浏覽器才有了querySelector與querySelectorAll方法。時至今日,雖然有了querySelector與querySelectorAll,但jQuery的選擇器仍然有少部分特性是前2者所無法替代的。

3. ajax

ajax全稱Asynchronous JavaScript and XML(異步的JavaScript與XML),是網頁無需重新整理頁面、使用js與伺服器進行互動的一種技術。

有時候會有這樣一種需求:隻希望更改頁面上的一個區域。然而在從前的技術架構内隻能重新整理整個頁面,帶來的後果是:①需要重新傳輸整個頁面,伺服器端與用戶端的流量消耗都會比較大;②如果是動态頁,伺服器端需要重新生成整個頁面,即使是那些客戶原本不想要重新整理的區域,增大了伺服器的負擔。

Google的Jesse James Garrett在2005年初發表了一篇文章,提供了解決這種需求的技術方案,也就是ajax。實際上這是一種實踐先行的技術,該方案的技術依賴之一XMLHTTP在1998年就已經被Microsoft開發出來了,而Google在若幹年後使用這項技術開發Google Maps等産品之後,才發表了相應的文章并對其進行了命名。

ajax的基本流程可以概括為:頁面上js腳本執行個體化一個XMLHttpRequest對象,設定好伺服器端的url、必要的查詢參數、回調函數之後,向伺服器送出請求,伺服器在處理請求之後将處理結果傳回給頁面,觸發事先綁定的回調函數。這樣,頁面腳本如果想要改變一個區域的内容,隻需要通過ajax向伺服器擷取與該區域有關的少量資料,在回調函數中将該區域的内容替換掉即可,不需要重新整理整個頁面。

XMLHttpRequest在發送請求的時候,有兩種方式:同步與異步。同步方式是請求發出後,一直到收到伺服器傳回的資料為止,浏覽器程序被阻塞,頁面上什麼事也做不了。而異步方式則不會阻塞浏覽器程序,在服務端傳回資料并觸發回調函數之前,使用者依然可以在該頁面上進行其他操作。ajax的核心是異步方式,而同步方式隻有在極其特殊的情況下才會被用到。

XMLHttpRequest在早期IE浏覽器裡是使用ActiveX來實作的,并不是浏覽器自身的對象。後來其他各家浏覽器也都實作了XMLHttpRequest對象,而高版本IE也把XMLHttpRequest改為了浏覽器的内建對象。

4. JSON

JSON全稱JavaScript Object Notation(js對象标記法),由Douglas Crockford在2002年發現并制定了标準。從名稱上就可以看出來,JSON是基于JavaScript的,是JavaScript的一個子集。JSON是用JavaScript文法來表示資料的一種輕量級語言。

雖然Douglas在2002年就注冊了http://json.org,并且為各種語言編寫了解析與構造JSON資料的庫,但在最開始的幾年JSON一直沒有得到足夠的重視。情況一直延續到ajax的出現。

從ajax的命名中我們就可以看到,資料交換是通過XML格式進行的。在ajax剛出現的時候,絕大多數應用都是采用XML格式,也有少數使用純文字的。但是XML格式有一個缺點,就是文檔構造複雜,需要傳輸比較多的位元組數。在這種情況下,JSON的輕便性逐漸得到重視,後來替代XML成為ajax最主要的資料傳輸格式。可以舉個簡單的例子感受一下二者的差別:

<?xml version="1.0" encoding="utf-8"?>
<root>
  <article>
    <title>Article Title1</title>
    <content>content1</content>
  </article>
  <article>
    <title>Article Title2</title>
    <content>content2</content>
  </article>
</root>           

複制

{
  "article" : [
    {
      "title": "Article Title1",
      "content": "content1"
    },
    {
      "title": "Article Title2",
      "content": "content2"
    }
  ]
}           

複制

XML規範實際上是比較複雜的,單純作為資料傳輸來說它太重了。在ajax領域中JSON取代XML的過程,是一個很好的“用腳投票”的範例。

而JSON的影響力在此後還繼續擴大,有些軟體将其作為配置檔案的格式,有些程式設計語言也吸納了JSON的優點。例如c#,在高版本裡可以這樣寫:

Dictionary<int, string> dict = new Dictionary<int, string>{
    {1, "a"},
    {2, "b"}
};           

複制

但是如果c# 2.0這樣寫,可是會報錯的。在2.0裡隻能寫成下面這種形式:

Dictionary<int, string> dict = new Dictionary<int, string>();
dict.Add(1, "a");
dict.Add(2, "b");           

複制

等價于

Dictionary<int, string> dict = new Dictionary<int, string>();
dict[1] = "a";
dict[2] = "b";           

複制

比較一下兩種寫法的差別,不僅有便捷性的差距,而且前一種寫法可以在聲明變量的同時為變量指派,後一種寫法則不行,這會影響到類屬性的初始化操作:在c# 2.0中,隻能把針對Dictionary之類複雜對象的初始化代碼寫在函數裡,而不能直接寫在類屬性的聲明處。

感覺上是c#受了JSON(或者說js)的影響。但此處是我個人的感覺,如有錯誤請指出。

回到js自身,對于對象構造有兩種方法:基于對象的完整寫法,字面量表示法。前者如:

var obj = new Object();
obj.title = "title1";
obj.content = "content1";           

複制

而與之對應的字面量表示法則寫為:

var obj = {
    title: "title1",
    content: "content1"
};           

複制

可以明顯看出字面量表示法要簡潔得多。而JSON基本就是字面量表示法的一個子集,除了強制要求鍵與字元串類型的值必須用雙引号包起之外,它剔除了undefined、function等類型,也不包括浏覽器内置對象類型(如Date、RegExp等),是基于文本的、比較純粹的資料表示方法。是以說,Douglas是“發現”了JSON,而不是“發明”。标準的JSON不包含注釋,但後來因為實際需求而出現了能夠處理注釋的JSON庫。

5. Node.js

Node.js是Ryan Dahl在2009年釋出的、主要用于伺服器端的Javascript運作環境,也可以用于個人電腦。

Ryan Dahl此前一直在尋找一種事件驅動型的、異步的伺服器端架構,實際上,js并不是他的首選。他是在嘗試了幾種語言之後,才發現js的函數回調與單線程特性正好契合他的要求,于是Node.js應運而生。

js的異步回調在ajax的部分已經提過:在調用異步方法的時候,可以将後續的處理函數作為參數傳入,在調用相應的異步接口之後,程式會将線程的控制權讓出,允許其他代碼執行;在接口傳回處理結果後,再執行後續處理函數(即回調函數)。實際上,因為js是單線程語言,回調函數并不是立刻被執行的,而是會被送入任務隊列,線上程空閑、并且隊列前方沒有其他任務的情況下,才會被執行。

使用者在向伺服器送出請求的時候,如果處理比較費時,傳統的伺服器端架構會導緻處理線程被阻塞。而js的特性使得異步任務在執行的時候讓出線程的控制權,在處理完成後再進行正确的回調,進而能夠獲得比較好的高并發處理能力。

js本身是一門嚴格的單線程語言,而Node.js為了充分發揮伺服器的處理能力,在運作環境級别上增加了對于多線程的支援(child process)。但Node.js的多線程與正常的多線程有很大差別——正常語言的多線程允許多個線程共享資料,或者調用其他線程暴露出來的公開方法,而Node.js的多線程隻能用消息機制進行通訊。這樣,Node.js就規避了正常多線程的資料同步、線程鎖(線程同步/互斥)等複雜問題,規避了一些潛在風險。

Node.js使用的V8引擎實際上就是Google的Chrome浏覽器使用的Javascript引擎(因為V8引擎是開源的),并進行了子產品擴充。例如遵循CommonJS标準的子產品定義,适合伺服器需求的多線程、叢集、HTTP/HTTPS,檔案系統,等等。Node.js中的很多方法都同時提供了異步版本與同步版本,從函數的命名上可以簡單區分。

得益于其子產品特性,Node.js的子產品擴充變得相當友善,用于Node.js包管理的npm得到了廣泛的使用,但也曾經引起“是否過度使用依賴包”的争論。

Node.js不僅可用于伺服器端,因為其安裝完成之後可以用指令行方式友善地調用,是以在個人電腦中也逐漸得到廣泛應用。例如為代碼編輯器提供插件、用于桌面的Node.js App等。另外還有一個重要的應用領域就是前端自動化,包括代碼的預編譯/轉換(如使用Babel将ECMAScript 6的代碼轉換為低版本的es代碼,将sass/less的樣式表檔案編譯為傳統的css檔案)、文法檢查、代碼檔案或圖像檔案的合并、代碼的混淆/壓縮、自動分發、自動測試等,還可以監視開發檔案夾,在内容改變時自動執行上述操作,并自動重新整理浏覽器頁面。這樣使得前端領域的開發方式得到了大大進化。

js雖然因為有着一些先天不足而被人诟病,但這些年來卻越發展越壯大。這不僅僅是因為依托于浏覽器這個宿主環境,更是因為其自身具備的一些優秀特性,Node.js的出現與發展就是一個很好的例證。

總結

ajax與Node.js都使用了js的異步回調特性。

jQuery的出現解決了那個各方面标準尚未統一的混亂時代的許多問題,讓js的應用更加廣泛,并為未來某些标準的制定指明了方向。

JSON從js中脫胎而出,作為一種簡潔、擴充性好的輕量級資料表示方法,在很多領域得到了廣泛使用。

Node.js在伺服器端與開發流程中都越來越得到重視。

由于Node.js不包含BOM與DOM,是以jQuery不能直接在Node.js上使用,但可以借助jsdom、cheerio之類的庫,在構造出虛拟的dom結構後再使用。檢視github上的jQuery開發包,可以看到它使用了Node.js上的grunt來進行自動化建構、測試的工作。

以上幾項技術的共同進步,配合浏覽器的進步,此外還有硬體條件的發展,讓複雜的頁面應用越來越多,許多以前在伺服器端進行的工作可以轉到客戶的浏覽器中進行,順應了分布式處理的潮流。

@于江水

的答案存在一些錯誤或者不嚴謹之處:

1. js設計之初并不是用來彈窗或者改變頁面上内容,這些功能是随着浏覽器的發展才出現的。實際上js語言自身并不能實作這樣的需求。

完整的JavaScript包括ECMAScript、BOM(浏覽器對象模型,如window、location等對象)、DOM。其中BOM和DOM雖然是js的組成部分,但js隻規定了這兩者的接口規範,即浏覽器傳回這兩類對象時,可以用js的方式來處理(點号或者方括号來操作對象成員,可以讀取或修改屬性等)。也就是說,js腳本通過浏覽器提供的接口去操作BOM和DOM,js在其中主要進行流程控制。沒有這些接口的支援,js自身是無法完成一些功能的。而早期各家浏覽器對于接口實作的不同,也帶來了比較嚴重的相容性問題。

從js的标準化工作也能看出之間的不同。ECMAScript是由ECMA來進行标準化工作,而BOM和DOM則是由W3C(World Wide Web Consortium)來進行标準化的。

2. jQuery設計的第一要素是對于節點的查詢。解決相容性問題也是jQuery設計的重點,但不是第一位的。當時在嘗試解決相容性問題的js庫有好幾個,jQuery在這方面并不唯一。而jQuery選擇器才是jQuery劃時代的特性,從早已存在的事物(css選擇器)中發現了價值,并将其修改擴充、發揚光大。

不可否認,jQuery的流行很大程度上要歸功于解決相容性問題,但作為名稱中包含的“query”連提都不提,這答案有點偏了。

3. ajax建立之初是用于處理Gmail、Google Maps這樣的複雜頁面應用,表單送出方式的改變隻是ajax技術發展的一個副産品而已。

并且表單送出的問題并不僅僅在于填錯項目,實際面臨的問題要複雜得多,包括使用者的網絡掉線、伺服器端因為請求過多而暫時無法響應等不可抗因素。在傳統模式下,送出表單是前往一個新的動态頁,如果出現了錯誤,運氣好的時候點浏覽器的“後退”按鈕還可以傳回表單填寫界面,剛才寫的東西都還在;運氣不好的時候,隻會看見一個空白的表單。當時在一些論壇裡,總能看到“辛辛苦苦碼的字一送出就全沒了”的哭訴。

4. 對于JSON的用途描述有錯。原答案中提到送出内容簡單的時候,用字元串發送,後來改為XML與JSON。實際上,JSON用于用戶端(包括浏覽器)與服務端通訊的時候,主要指的是伺服器端傳回處理結果時送回給用戶端的資料格式是JSON。而用戶端送出給伺服器端的資料大多數并不是XML或JSON格式的。

用戶端向伺服器端送出資料的時候,使用GET或POST方法,其資料一般是簡單的“鍵/值對”表示,例如常見的GET方法在url中構造的參數:

?page=1&pagesize=20           

複制

POST方法在送出一般類型的表單時,與GET方法在資料組織形式上基本相近(除非是用multipart類型的表單發送檔案資料),但http資料包格式、浏覽器曆史記錄、以及伺服器端接收處理有所差別,這屬于http協定的内容,此處不展開。

是以,用戶端向伺服器端送出資料的時候,主要是采用字元串拼接的方式按上述例子構造,一般不會使用XML或者JSON格式,特别是在ajax出現的早期。(近年來随着頁面應用越來越複雜,用JSON格式向伺服器端傳遞資料的情況也逐漸增加)

而伺服器端傳回給用戶端的資料格式,一開始的主流就是XML,不存在以字元串為主然後過渡到XML的過程。

另外,XMLHTTP還可以用來進行伺服器間通訊,早期發送與接收資料的主流格式都是XML,但是這與ajax不是一回事。