自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

全棧必備JavaScript基礎

開發(fā) 開發(fā)工具
JavaScript 是一個具有強大生命力的語言,前端框架更是日新月異,從Angular,Vue,到React, 乃至React Native,給人以目不暇接的感覺,但是,老碼農(nóng)覺得基礎認識還是非常必要的,勿在浮沙筑高塔。

[[187649]]

1995年,誕生了JavaScript語言,那一年,我剛剛從大學畢業(yè)。在今年RedMonk 推出的2017 年第一季度編程語言排行榜中,JavaScript 排第一,Java 第二,Python 反超 PHP 排第三,PHP 第四,C# 和 C++ 并列第五。RedMonk 排名的主要依舊是各種編程語言在 Stack Overflow 和 GitHub 上的表現(xiàn),比如編程語言在 Stack Overflow 上的討論數(shù)量,在 GitHub 上的代碼量等。盡管有一定的片面性,還是說明了JavaScript 應用的廣泛性。從全棧的角度看,Javascript 是必備的一種編程語言。

 

ECMAScript 和 JavaScript 的關系

JavaScript 誕生于Netscape,但是在1996年,微軟發(fā)布了與JavaScript 兼容的JScript,面對兼容和發(fā)展的需要,網(wǎng)景公司的先賢們努力加入了 ECMA International 標準化組織,致力于JavaScript 的標準化,命名為ECMAScript。后來,由于歷史的原因, JavaScript標準的開發(fā)主體變成了Mozila基金會。

簡單地,ECMAScript 是JavaScript語言的標準規(guī)范,就像C++的標準相對于C++語言那樣。

JavaScript 是怎樣的語言

在mozilla 開發(fā)者網(wǎng)站上是這樣描述JavaScript的:

JavaScript (JS) is a lightweight interpreted or JIT-compiled programming language with first-class functions.

意思是說JavaScript 是一個輕量級解釋或即時編譯的函數(shù)式語言,里面有很多的概念,輕量、解釋、編譯、即時編譯、函數(shù)式。在老碼農(nóng)看來,簡單起見,理解為擴展語言較為方便。

一般的編程語言都有著自己相對獨立的執(zhí)行環(huán)境,但是JavaScript的執(zhí)行環(huán)境依賴在宿主環(huán)境中,宿主環(huán)境尤其是客戶端的宿主環(huán)境提供了更多統(tǒng)一的環(huán)境變量,比如瀏覽器中的window,document等。實際上,JavaScript 和DOM 是可分的,對于不同的運行環(huán)境,有著不同的內(nèi)置宿主對象。JavaScript作為擴展語言在內(nèi)置的宿主環(huán)境中運行,全局對象在程序啟動前就已經(jīng)存在了。

JavaScript的時空基礎

從空間觀的角度看,JavaScript包括數(shù)據(jù)結構,操作符,語句與表達式,函數(shù);從時間的角度看,包括作用域,處理方式,模塊與庫。

數(shù)據(jù)結構

JavaScript 中包含的六種基本類型:

  • Boolean
  • Null
  • Undefined
  • Number
  • String
  • Symbol (ECMAScript 6)

其它全是對象。值是有類型的,變量是沒有類型的,類型定義了值的行為特征,變量在沒有持有值的時候是undefined。 JavaScript對值和引用的賦值/傳遞在語法上沒有區(qū)別,完全根據(jù)值的類型來判定。

對于對象的屬性和方法而言,全局變量和全局函數(shù)是全局對象的屬性,全局對象相當于宿主對象的根對象。需要注意是屬性的屬性中那些不可變對象的實現(xiàn)方式:

  1. 對象常量: 結合writable和configurable:false 可以創(chuàng)建一個真正的常量屬性
  2. 禁止擴張:Object.preventExtensions(..)來禁止一個對象添加新屬性并保留已有屬性
  3. 密封: 在 Object.seal(..) 后不能增,刪,改 該屬性
  4. 凍結: Object.freeze(..) 會禁止對于對象本身及任意直接屬性的修改

數(shù)據(jù)類型的判定可以通過 contructor,instanceof, isPrototypeOf等方法實現(xiàn),對于鴨子類型的判定還可以使用 in 的相關操作。符號并非對象,而是一種簡單標量基本類型。

JavaScript 中的強制類型轉換總是返回基本類型值,將對象強制轉換為String 是通過ToPrimitive抽象操作完成的,而toJSON()是返回一個能夠被字符串化的安全的JSON值。

操作符

操作符是空間元素連接的紐帶之一,JavaScript操作符包括算術,連接,相等,比較,邏輯,位,類型判斷,條件,new,delete, void,",", ".", "[]"等。

在JavaScript中以操作符進行操作往往都附帶著類型轉換。

一元運算符+ 是顯式強制類型轉換,而~是先轉換為32位數(shù)字,然后按位反轉。|| 和&& 更應該算是選擇器運算符,其返回值不一定是布爾值,而是兩個操作數(shù)其中的一個值。一般先對第一個操作數(shù)進行toBoolean強制類型轉換,然后再執(zhí)行條件判斷。例如:a||b 理解成a?a:b 更通俗。對&& 而言,如果第一個是真值,則把第二個作為返回值,a&&b 理解成a?b:a 。

== 和=== 都會對操作數(shù)進行類型檢查,并執(zhí)行隱性類型轉換,需要注意的是:

  • 如果兩邊的值中有true或false,千萬不要使用==
  • 如果兩邊有[],””或者0,盡量不要使用==

這里是Github上關于各種相等性的矩陣:

 

語句與表達式

操作符與變量/常量等連接形成了語句和表達式,例如表達式a+1中的null 被強制轉換為0。 語句包括聲明與塊,控制語句有判斷,循環(huán),break,continue,return,異常等。每個語句都有一個結果值,哪怕是undefined。

正則表達式是非常重要的一類表達式,主要使用RegExp類,執(zhí)行方法test效率高,exec 會得到一個結果對象的數(shù)組。

逗號運算符可以把多個獨立的表達式串聯(lián)成一個語句,{ }在不同情況下的意思不盡相同,作為語句塊,{ ..} 和for/while循環(huán)以及if條件語句中代碼塊的作用基本相同。{a,b} 實際上是{a:a,b:b}的簡化版本。

try..catch..finally 中,如果finally中拋出異常,函數(shù)會在此處終止。需要注意的是,如果此前try中已經(jīng)有return設置了返回值,則該值會被丟棄。finally中的return也會覆蓋try和catch中的return的返回值。

函數(shù)與作用域

函數(shù)就是具有運算邏輯的對象,匿名函數(shù)不利于調(diào)試,回調(diào)函數(shù)是一種控制反轉。所有的函數(shù)(對象)都具有名為prototype的屬性,prototype屬性引用的對象是prototype對象;所有的對象都含有一個隱式鏈接,用以指向在對象生成過程中所使用的構造函數(shù)的prototype對象。

匿名函數(shù)沒有name 標識符,具有如下缺陷:

  1. 代碼更難理解
  2. 調(diào)試棧更難追蹤
  3. 自我引用(遞歸,事件(解除)綁定,等)更難

如果function是聲明的第一個詞,那就是函數(shù)聲明,否則就是函數(shù)表達式。立即執(zhí)行函數(shù)表達式形如:(function …)( )

時空密不可分,作用域是時空連接的紐帶之一。作用域包括全局,函數(shù),塊級作用域。作用域是根據(jù)名稱查找變量的一套規(guī)則,遍歷嵌套作用域鏈的規(guī)則簡單:引擎從當前執(zhí)行作用域逐級向上查找。閉包可以理解為具有狀態(tài)的函數(shù)。

函數(shù)作用域指屬于這個函數(shù)的全部變量都可以在整個函數(shù)的范圍內(nèi)使用或復用。塊作用域形如 with, try/catch, ES6 引入了let,const等。

動態(tài)作用域并不關心函數(shù)和作用域是如何聲明以及在何處聲明的,只關心它們從何處調(diào)用的。詞法作用域是定義在詞法分析階段的作用域,詞法作用域查找會在第一個匹配的標識符時停止。作用域鏈是基于調(diào)用棧的,而不是代碼中的作用域嵌套。ReferenceError 是與作用域判別失敗相關,而TypeError則是作用域判別成功,但是對結果的操作非法或不合理。

this 提供了一種優(yōu)雅方式來隱式“傳遞”一個對象引用。 this 即沒有指向函數(shù)的自身,也沒有指向函數(shù)的作用域,是在函數(shù)被調(diào)用時發(fā)生的綁定,它指向什么完全取決于函數(shù)在哪里被調(diào)用。如果分析this綁定的話,可以使用調(diào)試工具得到調(diào)用棧,然后找到棧中的第二個元素,就是真正的調(diào)用位置。

this 的綁定規(guī)則有:

  1. 默認綁定:獨立的函數(shù)調(diào)用,嚴格模式不能將全局對象用于默認綁定
  2. 隱式綁定:把函數(shù)調(diào)用中的this 綁定到函數(shù)引用中的上下文對象
  3. 顯式綁定:通過call()和apply()方法可以直接指定this的綁定對象。其中,硬綁定是一種顯式的強制綁定,ES5中提供了內(nèi)置方法Function.prototype.bind, API中調(diào)用的上下文和bind的作用一樣。
  4. new 綁定,構造函數(shù)只是一些使用new操作符調(diào)用的函, 使用new 來調(diào)用函數(shù)的操作過程大致如下:
  • 創(chuàng)建一個全新的對象
  • 這個新對象會被執(zhí)行[[Prototype]]鏈接
  • 這個新對象會綁定到函數(shù)調(diào)用的this
  • 如果函數(shù)沒有返回其他對象,那么new表達式中的函數(shù)調(diào)用會自動返回這個新對象

如果同時存在多種綁定,那么綁定的優(yōu)先級大致如下:

  1. 由new調(diào)用綁定到新創(chuàng)建的對象
  2. 由call 或者apply(或bind)調(diào)用綁定到指定的對象
  3. 由上下文對象調(diào)用綁定到那個上下文對象
  4. 默認在在嚴格模式下綁定到undefined,否則綁定到全局對象

更安全地使用this 綁定的做法是傳入一個特殊的對象,把this 綁定到這個對象。需要注意的是,箭頭函數(shù)不使用this的4種規(guī)則,而是根據(jù)外層(函數(shù)或全局)作用域來決定this。

還要注意一點,eval 和 with 會導致作用域變化而引起性能下降,盡量不要使用。eval() 函數(shù)中的字符串是代碼,用來執(zhí)行動態(tài)創(chuàng)建的代碼,嚴格模式有自己的作用域,還存在安全隱患;with 是重復引用一個對象中的多個屬性的快捷方式,通過將一個對象的引用當作作用域來處理,會改變作用域范圍。

處理和執(zhí)行方式

JavaScript引擎本身沒有時間概念,只是一個按需執(zhí)行任意代碼片段的環(huán)境,事件調(diào)度總是由包含它的宿主環(huán)境來執(zhí)行。一旦有事件需要運行,事件循環(huán)隊列就會運行,直到隊列清空,用戶交互、IO和定時器等事件源會向事件隊列加入事件。

由于JavaScript的單線程特性,很多函數(shù)的代碼具有原子性。

回調(diào)函數(shù)封裝了程序的延續(xù)性,常見設計是分離回調(diào)(一個用于成功通知,一個用于出錯通知)。另一種回調(diào)模式是“error-first”,可能受到防御式編程的影響,NodeJS API 采用了此類的風格,如果成功的話,這個參數(shù)就會被清空。需要注意的是,回調(diào)函數(shù)的嵌套往往稱為回調(diào)地獄。

Deferred是一種將異步處理串聯(lián)書寫并執(zhí)行的機制,Deferred對象是一種具有unresolved,resolved,rejected 中某一種狀態(tài)的對象。Deferred內(nèi)部機制是先注冊回調(diào)函數(shù),Deferred對象狀態(tài)發(fā)生變化時執(zhí)行該函數(shù),是一種提高代碼可讀性的機制。

Deferred對象的狀態(tài)遷移只能發(fā)生一次,以then(),done(),fail(),always(),pipe()指定后續(xù)函數(shù)的方法,通過when()來并行處理,將Deferred 對象中的一部分方法刪除后得到是Promise對象,對狀態(tài)的管理由最初創(chuàng)建該Deferred對象的所有者來執(zhí)行。

Promise 封裝了依賴于時間的狀態(tài),從而使得本身與時間無關,Promise 可以按照可預測的方式進行,而不用關心時序或底層的結果。一旦Promise決議完成,就成為了不變值,可以安全地吧這個值傳遞給第三方,并確保不會改變。

Promise 是一種在異步任務中作為兩個或更多步驟的流程控制機制,時序上的this-then-that。 不僅表達了多步異步序列的流程控制,還是一個從一個步驟到下一個步驟傳遞消息的消息通道。事件監(jiān)聽對象可以當成是對promise 的一種模擬,對控制反轉的恢復實現(xiàn)了更好的關注點分離。

判斷是否是Promise 值的示例代碼如下:

  1. if( 
  2.     p !==null && 
  3.     ( typeof p ===“object” || typeof p ===“function”) && typeof p.then===“function”) 
  4.     { 
  5.         console.log(“thenable”); 
  6.     } 
  7. else
  8.     console.log(“not thenable”); 

生成器是一類特殊的函數(shù),可以一次或多次啟動和停止,并不非的一定要完成,生成器把while true 帶回了Javascript的世界。其中,yield 委托的主要目的是代碼組織,以達到與普通函數(shù)調(diào)用的對稱。從生成器yield出一個Promise, 并且讓這個Promise 通過一個輔助函數(shù)恢復這個生成器,這是通過生成器管理異步的好方法之一。

需要注意的是,如果在Promise.all([..]) 中傳入空數(shù)組,會立即完成, 而Promise.race([..]) 則會掛住。 在各種Promise庫中,finally ( .. ) 還是會創(chuàng)建并返回一個新Promise的。

模塊與庫

模塊和庫是JavaScript 時空中的另一紐帶,提高了代碼的復用性和開發(fā)效率。

模塊充分利用了閉包的強大能力,從模塊中返回一個實際的對象并不是必須的,也可以直接返回一個內(nèi)部函數(shù),例如:jQauery 和 $標識符就是jQuery 模塊的公共API。

模塊有兩個必要條件:

  1. 必須有外部的封閉函數(shù),該函數(shù)必須至少被調(diào)用一次
  2. 封閉函數(shù)必須返回至少一個內(nèi)部函數(shù),這樣內(nèi)部函數(shù)才能在私有作用域中形成閉包,并且可以訪問或修改私有的狀態(tài)

import 可以將一個模塊的一個或多個API導入到當前作用域中,并分別綁定在一個變量上;module 則將整個模塊的API 導入并綁定到一個變量上, export 將當前模塊的一個標識符導出為公共API。

大多數(shù)模塊所依賴的加載器/管理器本質(zhì)上是將這種模塊定義封裝進一個API。基于函數(shù)的模塊并不是一個能被靜態(tài)識別的模式(編譯器),API定義只有在運行時考慮進來。但是ES6 模塊的API 是靜態(tài)的,必須被定義在獨立的文件中。

JavaScript 中的庫浩如煙海,這里僅對JQuery做簡要說明。JQuery壓縮后大約31k,輕巧靈活,通過鏈式語法實現(xiàn)邏輯功能,通過CSS3選擇器及自定義選擇器獲取元素,支持插件,可擴展性高。

JQuery中 的特色函數(shù)——$ ,可以抽取與選擇器匹配的元素,或者創(chuàng)建新的DOM元素,將已有的DOM元素轉換為jQuery對象,對DOM構造完成后的事件監(jiān)聽器進行設定等等。JQuery 對DOM,樣式,AJAX 均可有效處理。

通過擴展JQuery.fn 就可以創(chuàng)建JQuery的插件,code.google.com/apis/libraries 給出了很多JQuery 的插件信息。

利用JavaScript 的時空觀,可以對這一語言有一些基本的梳理。就語言本身而言,關鍵字是不能回避的,對JavaScript 關鍵字,在StackOverFlow中有人給出了如下詩一樣的總結:

  1. Let this long package float, 
  2. Goto private class if short。 
  3. While protected with debug case, 
  4. Continue volatile interface。 
  5. Instanceof super synchronized throw, 
  6. Extends final export throws. 
  7.  
  8. Try import double enum? 
  9. -False, boolean, abstract function
  10. Implements typeof transient break! 
  11. Void static,default do, 
  12. Switch int native new, 
  13. elsedelete null public var, 
  14. In return for const, truechar
  15. …… finally catch byte. 

客戶端應用

一門語言所被使用的廣泛程度取決于使用的場景,正如PHP被廣泛采用那樣,互聯(lián)網(wǎng)應用不僅是JavaScript 的家鄉(xiāng),而且是它大展身手的最重要場所,沒有JavaScript 的Web應用幾乎絕跡了。

web應用中使用JavaScript有拖拽操作,異步讀取,鍵盤訪問 和動畫效果等基本功能。對于清晰地使用JavaScript實現(xiàn)Web應用而言,理解瀏覽器網(wǎng)頁處理過程是必要的。一般地,瀏覽器先分析HTML,然后構造DOM樹,再載入外部Javascript 文件以及CSS文件,接下來載入圖像文件等外部資源,最后在分析Javascript后開始執(zhí)行至全部完成。

在HTML中加載JavaScript的方式有多種:

  • <script> 標簽,在body 結束標簽前寫
  • 讀取外部JavaScript 文件,讀取完就開始執(zhí)行,瀏覽器可以緩存
  • onload 事件加載
  • DOMContentLoaded是在完成HTML解析后發(fā)生的事件,也可以用于加載JavaScript
  • 動態(tài)載入,這樣JS在載入時不會阻斷其他操作,如
  1. var script = document.createElement(‘script’); 
  2. script.src = ‘my-javascript.js’; 
  3. document.getElementsByTagName(‘head’)[0].appendChild(script) 

window對象是JavaScript所能操作的最高層對象,其中的屬性包括navigator,location,history,screen,frames,document,parent,top,self 等。

DOM 是一種API,完成對HTML/XML 的樹形結構訪問,如標簽,元素,節(jié)點等。節(jié)點可以通過ID,標簽名,名稱和類名進行檢索,例如:

  1. var element = document.getElementById(“abel”)  
  2. var allelements = document.getElementByTagName(‘*’) 

由于返回的是NodeList對象,性能較差,可以通過 var array = Array.prototye.slice.call(allelements)轉換為array 后處理。節(jié)點的訪問可以通過XPath 進行靈活的訪問,當然,Selector API 比XPath更簡單且同樣靈活,例如:

  1. var a_label = document.querySelector(‘#abel’)  
  2. var b_all = document.querySelectorAll(‘div’) 

如果先修改DocumentFragment,再對實際的document對象操作,DOM 的操作性能會較高一些。

事件偵聽器的設定可以制定HTML元素的屬性,也可以指定DOM元素的屬性,還可以通過EventTarget.addEventListenser()進行指定。事件的處理包括捕獲,目標處理和事件冒泡三個階段,捕獲的過程是:

  1. window -> document -> html -> body -> div -> button 

然后處理器執(zhí)行,冒泡向上傳播的過程是遍歷DOM樹,需要注意的是 focus 不會冒泡。

DOM2中的標準事件有HTMLEvent,MouseEvent,UIEvent和MutationEvent。DOM3 中的事件更多:UIEvent,F(xiàn)ocusEvent,MouseEvent, WheelEvent, TextEvent,KeyboardEvent 和compositionEvent等,還可以通document.createEvent來自定義事件。

通過JavaScript 對CSS樣式變更的方法有通過className 屬性變更class名,通過classList屬性更改class名(其中classList 是H5對DOM TokenList接口的實現(xiàn)),還可以更改Style 屬性或者直接更改樣式表。通過JavaScript可以對屏幕位置(screenX,screenY),窗口位置(clientX,clientY),文檔坐標(pageX,pageY,由瀏覽器自行實現(xiàn)的),特定元素內(nèi)的相對位置(layerX,layerY 或offsetX offsetY)進行修改。通過JavaScript可以對表單中的元素,控件和內(nèi)容進行驗證,可用于驗證的事件有submit,focus,blur,change,keydown/up/press,input。使用表單而不產(chǎn)生頁面跳轉的方式可以是指向到一個 (0,0 )的空iframe。

對于動畫而言,css的動畫性能一般要更好一些。

AJAX 在Web應用中是不可或缺的,簡單地說,是一種不發(fā)生頁面跳轉就能異步載入內(nèi)容并改寫頁面內(nèi)容的技術,主要通過 XMLHttpRequest 對象的創(chuàng)建,實現(xiàn)通/異步通信,處理超時和響應。AJAX有著跨源限制,實現(xiàn)跨源通信的方式有:JSONP,iframe hack,window.postMessage() 以及 XMLHttpRequest Level 2。

HTML5+CSS3+JavaScript的綜合使用才可能成就一個Web應用。H5中的 History API 使用了window屬性的history對象監(jiān)聽popstate事件,用于恢復頁面狀態(tài)的處理。ApplicationCache 在html標簽的manifest 屬性中指定了緩存清單文件的路徑,必須通過text/cache-manifest 這一MIME type 來發(fā)布緩存清單文件,注意清單中的CACHE,NETWORK,和FALLBACK 的區(qū)分。

通過navigator.onLine 可以獲知網(wǎng)絡狀態(tài),還可以通過online/offline事件來偵聽連接狀態(tài)的切換時機。online/offline事件是document.body 觸發(fā)的,并傳給document對象和window對象。

  1. <p> network is : <span id = “indicator”> (state unknown) </span> </p> 
  2. <script> 
  3.     function updateIndicator = document.getElementById(‘indicator’); 
  4.     indicator.textContext = navigator.online?’online’:’offline’; 
  5. document.body.onload = updateIndicator; 
  6. document.body.ononline= updateIndicator; 
  7. document.body.onoffline = updateIndicator; 
  8. </script> 

DataTransfer 是Drag Drop API 的核心,在所有拖拽事件的事件對象中,都有該屬性,主要是接收數(shù)據(jù)。拖拽文件從瀏覽器保存到桌面:event.dataTransfer.setData(‘DownloadURL’,’MIMETYPE: 文件url’)例如:

  1. <a href=“http://a.b.c/abel.pdf”  
  2.    data-downloadurl = “application/pdf:abel.pdf:http://a.b.c/abel.pdf” 
  3.    class=“dragout” draggable = “true”>download </a> 
  4.    <script> 
  5.    var files = document.querySelectorAll(‘.dragout’); 
  6.    for (var i = 0,file; file =files[i];i++) { 
  7.        file.addEventListener(‘dragstart’,function(event){ 
  8.    event.dataTransfer.setData(“DownloadURL”,this.dataset.downloadurl); 
  9.    },false); 
  10.    } 
  11.    </script> 

FileAPI 通過FileReader 讀取文件,也可以讀取dataURL,F(xiàn)ileReaderSync 用于同步讀取文件內(nèi)容,可以在Web Worker 中使用。

Web Storage 為所有源共享5M空間,localStorage 和sessionStorage 的區(qū)別在于數(shù)據(jù)的生命周期。cookie 最大4k,發(fā)請求時一起發(fā)送,保存會話等重要信息。indexedDB 可以歸為文檔型數(shù)據(jù)庫, 作為客戶端存儲又一選擇。

  1. var indexdb = window.indexDB||window.webkitIndexedDB||window.mozIndexedDB; 

Web worker 是H5 的新特性,是宿主環(huán)境(瀏覽器)的功能,JavaScript 本身是不支持多線程的。專用的worker 與創(chuàng)建它的程序之間是一對一的關系。

Web worker 能在另外的線程中創(chuàng)建新的Javascript 運行環(huán)境,使JavaScripts可以在后臺處理。主線程和工作線程分離,無法使用對方環(huán)境的變量。工作線程無法引用document對象,需要通過消息收發(fā)完成數(shù)據(jù)傳遞。 在主線程創(chuàng)建工作線程,大約向var worker = new Worker(‘work.js’)這樣 在主線程中停止worker的方式是worker.terminate(); worker 自身停止的方式是 self.close();worker 中 可以通個 importScripts 方法,在工作線程內(nèi)讀取外部的文件。

了解了這些基礎方式和方法,僅僅是Web應用中JavaScript開發(fā)的第一步吧。

服務端應用

技術系統(tǒng)總是又著向超系統(tǒng)進化的趨勢,JavaScript 也不例外。

JavaScript 應用于服務端的開發(fā)源于2009年初出現(xiàn)的CommonJS,后來成為為了服務器端javaScript的規(guī)范?;贘avaScript沒有模塊系統(tǒng)、標準庫較少、缺乏包管理工具等現(xiàn)狀,CommonJS規(guī)范希望JavaScript可以在任何地方運行,以達到Java、C#、PHP這些后臺語言具備開發(fā)大型應用的能力。CommonJS是一種思想,它的終極目標是使應用程序開發(fā)者根據(jù)CommonJS API編寫的JavaScript應用可以在不同的JavaScript解析器和HOST環(huán)境上運行,例如編寫服務端應用,命令行工具,基于GUI的桌面應用和混合應用編程等,詳情參加 www.commonjs.org 。

[[187650]]

NodeJS可以理解成CommonJS規(guī)范的一種實現(xiàn),而且是部分實現(xiàn)。NodeJS以V8作為JavaScript的實現(xiàn)引擎,通用的異步處理事件循環(huán),提供了一系列非阻塞函數(shù)庫來支持實踐循環(huán)特性。同時,NodeJS提供了高度優(yōu)化的應用庫,來提高服務器效率,例如其http 模塊是為快速非阻塞式http服務而用C語言重寫的。另外,NodeJS還有shell的命令行工具,通過包系統(tǒng)實現(xiàn)擴展,擴展列表可以詳情參見: GitHub.com/node/wiki/modules。

JavaScript 中的主要實現(xiàn)引擎包括:IE采用的JScript,F(xiàn)irefox采用的SpiderMoneky,Chrome 采用的V8,Safari采用的webkit中的 javacriptcore燈。如果要對引擎有進一步的了解,可以研讀一下javascriptcore等相關的源代碼。

V8 是NodeJS 中的核心引擎,NodeJS的系統(tǒng)架構大致如下:

與瀏覽器相對應,Node 中的全局變量可以通過 Object.keys(global); 獲得, 看一看NodeJS中的 “hello world” 程序:

  1. var http = require('http'); 
  2. http.createServer(function (req,res){ 
  3.     res.writeHead(200,{'Content-type':'text/plain'}); 
  4.     res.end('Hello Node.js \n'); 
  5. }).listen(1234,"127.0.0.1"); 
  6. console.log('Server running on http://127.0.0.1:1234/'); 

幾行代碼就實現(xiàn)一個簡單web server, 使Pythoner 們聯(lián)想到了 Tornado, 它們都走在單線程異步IO的路上。

NodeJS 提供了對https 的支持,可以通過openssl 生成證書的方式大致是:

  1. openssl req -new -x509 -keyout key.pen -out cert.perm 

使用證書的示例如下:

  1. var fs  =require(‘fs’); 
  2. var options = { 
  3.     key: fs.readFileSync(‘key.perm’); 
  4.     cert:fs.readFileSync(‘cert.perm’); 

NodeJS支持socket 和文件處理,配合系統(tǒng)擴展可以使用各種模版語言?;贜odeJS的實際在業(yè)界非常廣泛,比如面向websocket的IM系統(tǒng),各種web應用網(wǎng)站等等。

鑒于微服務架構的興起,也誕生了基于Node的微服務架構——Seneca,它使用完備的模式匹配接口來連接各個服務,從代碼中將數(shù)據(jù)傳輸抽象出來,使編寫具有高擴展性的軟件變得相當容易。Seneca 沒有使用依賴注入,但是在處理控制反轉上相當靈活,沒有關鍵字和強制的字段,只需一組鍵值對,用于模式匹配的引擎中。具體參考實現(xiàn),可以參考《node.js 微服務》一書。

基于JavaScript的全棧

如果在整個應用系統(tǒng)中主要使用JavaScript編程語言作為技術棧,那么也可以成為基于JavaScript 的全棧,關于全棧的論述可以參加《全棧的技術棧設想》和《再談< 全棧架構師>》兩篇文字。例如MEAN架構,即MongoDB + Express + Angular + Node,MEAN 技術棧代表著一種完全現(xiàn)代的 Web 開發(fā)方法:一種語言運行在應用程序的所有層次上,從客戶端到服務器,再到持久層。借助JavaScript的測試框架,比如MochaJS、JasmineJS 和 KarmaJS,可以為自己的 MEAN 應用程序編寫深入而又全面的測試套件,據(jù)說MEAN有取代LAMP/LNMP的的趨勢,但還需保持謹慎。

引擎的差異

正像Java 那樣,盡管又著虛擬機規(guī)范,但各個JVM的實現(xiàn)還是有著些許的不同,JavaScript 也是如此。JavaScript各引擎中同樣存在著少量的限制,例如:

  • 字符串常量中允許的最大字符數(shù)
  • 作為參數(shù)傳遞到函數(shù)中的數(shù)據(jù)大小(棧大小)
  • 函數(shù)聲明中的參數(shù)個數(shù)
  • 函數(shù)調(diào)用鏈的最大長度
  • 以阻塞方式在瀏覽器中運行的最大時間
  • 變量名的最大長度
  • 盡管如此,JavaScript 在瀏覽器中的表現(xiàn)還是基本上可信的。

從軟件到硬件

實際上,JavaScript已經(jīng)嵌入到了從機器人到各種家電等各種各樣的設備中。這里隆重推薦我非常敬佩的好友——周愛民老師,他在Ruff(南潮信息科技)做的事情就是JavaScript 在物聯(lián)網(wǎng)上的進一步應用。

Ruff 是一個可以讓開發(fā)者實現(xiàn)敏捷開發(fā)智能硬件的系統(tǒng)平臺。它包含了Ruff SDK、Ruff OS,Rap Registry等。從技術上講,Ruff 是一個 JavaScript 運行時,專為硬件開發(fā)而設計。Ruff 對硬件進行了抽象,使用了基于事件驅(qū)動、異步 I/O 的模型,使硬件開發(fā)變得輕量而且高效。硬件抽象層,使得操作硬件猶如普通程序庫,降低了硬件領域進入門檻。

Ruff 為開發(fā)者提供了完善的開發(fā)服務。從項目生產(chǎn)、軟件包管理、應用管理、外設管理到固件管理等一系列現(xiàn)代軟件開發(fā)方式,PC 端完成開發(fā),無需燒板子,提升開發(fā)者的開發(fā)效率。Ruff 還提供了完善的測試框架,支持 assert、test、mock 等模塊,在開發(fā)機上測試邏輯,硬件測試也能 TDD。

官網(wǎng)(ruff.io) 上給出的示例如下:

  1. $.ready(function() { 
  2.    $('#led-0').turnOn(); 
  3. }); 

打開電路板上的一個LED 燈,就是如此的簡單。目前,一個 Ruff 硬件同時只能運行一款 Ruff 應用,它將擁有自己獨立的進程,著可能也受到JavaScript自身的限制吧。

關注性能

性能是全棧關注的一個重要維度,那句“過早優(yōu)化是萬惡之源”實際上是我們對高德納先生的斷章取義,原文是這樣的:

我們應該在例如97%的時間里,忘掉小處的效率;過早優(yōu)化是萬惡之源。但我們不應該錯過關鍵的3%中的機會。

實際上是非關鍵路徑上的優(yōu)化是萬惡之源,問題在于如何確定我們的代碼是否在關鍵路徑上。不論節(jié)省的時間多么少,花費在關鍵路徑上的性能優(yōu)化都是值得的。

對于性能優(yōu)化工具,用于JavaScript源代碼壓縮有 google Closure complier,packer,YUI compressor,JSmin等。頁面的性能優(yōu)化工具有YSlow 和Page Speed等。實際上,任何有意義且可靠的性能測試都是基于統(tǒng)計學上的合理實踐。 就JavaScript 代碼本身的性能而言,benchmarkjs 是一個很好的工具,而jsperf.com 提供了對JavaScript 執(zhí)行環(huán)境的性能測試。

總之,JavaScript 是一個具有強大生命力的語言,前端框架更是日新月異,從Angular,Vue,到React, 乃至React Native,給人以目不暇接的感覺,但是,老碼農(nóng)覺得基礎認識還是非常必要的,勿在浮沙筑高塔。

【本文來自51CTO專欄作者“老曹”的原創(chuàng)文章,作者微信公眾號:喔家ArchiSelf,id:wrieless-com】

戳這里,看該作者更多好文

責任編輯:武曉燕 來源: 51CTO專欄
相關推薦

2020-07-20 08:23:04

Redis分布式系統(tǒng)

2021-06-01 07:16:21

C語言基礎代碼

2017-06-13 15:10:02

大數(shù)據(jù)Log日志

2017-06-13 08:55:29

Log日志MySQL

2013-12-09 09:42:50

JavaScript全棧式

2017-10-12 14:24:24

2017-12-18 15:33:56

Java基礎編程

2017-04-12 14:45:20

數(shù)據(jù)架構數(shù)據(jù)源

2023-12-10 20:30:51

SQL工具數(shù)據(jù)

2017-08-07 13:02:32

全棧必備貝葉斯

2018-01-09 15:35:54

Python編程基礎

2022-07-06 11:21:11

JHipsterJavaJavaScript

2015-08-17 09:27:51

全棧工程師Devops工具周期表

2018-10-15 10:22:51

2019-06-05 13:30:24

ReactJavaScript開發(fā)

2023-08-21 09:51:57

全棧軟件開發(fā)

2015-05-04 09:23:38

JavaScript全棧開發(fā)員云計算

2023-07-03 00:47:23

2017-11-10 19:00:37

華為

2017-07-05 11:09:35

華為開發(fā)云
點贊
收藏

51CTO技術棧公眾號