




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、我們應(yīng)該如何去了解JavaScript引擎的工作原理 1. 什么是JavaScript解析引擎?簡(jiǎn)單地說,JavaScript解析引擎就是能夠“讀懂”JavaScript代碼,并準(zhǔn)確地給出代碼運(yùn)行結(jié)果的一段程序。比方說,當(dāng)你寫了 var a = 1 + 1; 這樣一段代碼,JavaScript引擎做的事情就是看懂(解析)你這段代碼,并且將a的值變?yōu)?。學(xué)過編譯原理的人都知道,對(duì)于靜態(tài)語言來說(如Java、C+、C),處理上述這些事情的叫編譯器(Compiler),相應(yīng)地對(duì)于JavaScript這樣的動(dòng)態(tài)語言則叫解釋器(Interpreter)。這兩者的區(qū)別用一句話來概括就是
2、:編譯器是將源代碼編譯為另外一種代碼(比如機(jī)器碼,或者字節(jié)碼),而解釋器是直接解析并將代碼運(yùn)行結(jié)果輸出。 比方說,firebug的console就是一個(gè)JavaScript的解釋器。但是,現(xiàn)在很難去界定說,JavaScript引擎它到底算是個(gè)解釋器還是個(gè)編譯器,因?yàn)椋热缦馰8(Chrome的JS引擎),它其實(shí)為了提高JS的運(yùn)行性能,在運(yùn)行之前會(huì)先將JS編譯為本地的機(jī)器碼(native machine code),然后再去執(zhí)行機(jī)器碼(這樣速度就快很多),相信大家對(duì)JIT(Just In Time Compilation)一定不陌生吧。我個(gè)人認(rèn)為,不需要過分去強(qiáng)調(diào)JavaScript解析引擎到底
3、是什么,了解它究竟做了什么事情我個(gè)人認(rèn)為就可以了。對(duì)于編譯器或者解釋器究竟是如何看懂代碼的,翻出大學(xué)編譯課的教材就可以了。這里還要強(qiáng)調(diào)的就是,JavaScript引擎本身也是程序,代碼編寫而成。比如V8就是用C/C+寫的。2. JavaScript解析引擎與ECMAScript是什么關(guān)系?JavaScript引擎是一段程序,我們寫的JavaScript代碼也是程序,如何讓程序去讀懂程序呢?這就需要定義規(guī)則。比如,之前提到的var a = 1 + 1;,它表示:· 左邊var代表了這是申明(declaration),它申明了a這個(gè)變量· 右邊的+表示要將1和1做加法·
4、; 中間的等號(hào)表示了這是個(gè)賦值語句· 最后的分號(hào)表示這句語句結(jié)束了上述這些就是規(guī)則,有了它就等于有了衡量的標(biāo)準(zhǔn),JavaScript引擎就可以根據(jù)這個(gè)標(biāo)準(zhǔn)去解析JavaScript代碼了。那么這里的ECMAScript就是定義了這些規(guī)則。其中ECMAScript 262這份文檔,就是對(duì)JavaScript這門語言定義了一整套完整的標(biāo)準(zhǔn)。其中包括:· var,if,else,break,continue等是JavaScript的關(guān)鍵詞· abstract,int,long等是JavaScript保留詞· 怎么樣算是數(shù)字、怎么樣算是字符串等等· 定
5、義了操作符(+,-,>,<等)· 定義了JavaScript的語法· 定義了對(duì)表達(dá)式,語句等標(biāo)準(zhǔn)的處理算法,比如遇到=該如何處理· 標(biāo)準(zhǔn)的JavaScript引擎就會(huì)根據(jù)這套文檔去實(shí)現(xiàn),注意這里強(qiáng)調(diào)了標(biāo)準(zhǔn),因?yàn)橐灿胁话凑諛?biāo)準(zhǔn)來實(shí)現(xiàn)的,比如IE的JS引擎。這也是為什么JavaScript會(huì)有兼容性的問題。至于為什么IE的JS引擎不按照標(biāo)準(zhǔn)來實(shí)現(xiàn),就要說到瀏覽器大戰(zhàn)了,這里就不贅述了,自行Google之。所以,簡(jiǎn)單的說,ECMAScript定義了語言的標(biāo)準(zhǔn),JavaScript引擎根據(jù)它來實(shí)現(xiàn),這就是兩者的關(guān)系。3. JavaScript解析引擎與瀏覽器又
6、是什么關(guān)系?簡(jiǎn)單地說,JavaScript引擎是瀏覽器的組成部分之一。因?yàn)闉g覽器還要做很多別的事情,比如解析頁面、渲染頁面、Cookie管理、歷史記錄等等。那么,既然是組成部分,因此一般情況下JavaScript引擎都是瀏覽器開發(fā)商自行開發(fā)的。比如:IE9的Chakra、Firefox的TraceMonkey、Chrome的V8等等。從而也看出,不同瀏覽器都采用了不同的JavaScript引擎。因此,我們只能說要深入了解哪個(gè)JavaScript引擎。4. 深入了解其內(nèi)部原理的途徑有哪些?搞清楚了前面三個(gè)問題,那這個(gè)問題就好回答了。個(gè)人認(rèn)為,主要途徑有如下幾種(依次由淺入深):· 看講
7、JavaScript引擎工作原理的書這種方式最方便,不過我個(gè)人了解到的這樣的書幾乎沒有,但是Dmitry A.Soshnikov博客上的文章真的是非常的贊。· 看ECMAScript的標(biāo)準(zhǔn)文檔這種方式相對(duì)直接,原汁原味,因?yàn)橐婢褪歉鶕?jù)標(biāo)準(zhǔn)來實(shí)現(xiàn)的。目前來說,可以看第五版和第三版,不過要看懂也是不容易的。· 看JS引擎源代碼這種方式最直接,當(dāng)然也最難了。因?yàn)檫€牽涉到了如何實(shí)現(xiàn)詞法分析器,語法分析器等等更加底層的東西了,而且并非所有的引擎代碼都是開源的。5. 以上幾種方式中第一種都很難看明白怎么辦?其實(shí)第一種方式中的文章,作者已經(jīng)將文檔中內(nèi)容提煉出來,用通俗易懂的方式闡述出來
8、了。如果,看起來還覺得吃力,那說明還缺少兩塊的東西:· 對(duì)JavaScript本身還理解的不夠深入如果你剛剛接觸JavaScript,或者說以前甚至都沒有接觸過。那一下子就想要去理解內(nèi)部工作原理,的確是很吃力的。首先應(yīng)該多看看書,多實(shí)踐實(shí)踐,從知識(shí)和實(shí)踐的方式來了解JavaScript預(yù)言特性。這種情況下,你只需要了解現(xiàn)象。比方說,(function()() 這樣可以直接調(diào)用該匿名函數(shù)、用閉包可以解決循環(huán)中的延遲操作的變量值獲取問題等等。要了解這些,都是需要多汲取和實(shí)踐的。實(shí)踐這里就不多說了,而知識(shí)汲取方面可以多看看書和博客。這個(gè)層面的書就相對(duì)比較多了,Profession
9、al JavaScript for Web Developers就是本很好的書(中文版請(qǐng)自行尋找)。· 缺乏相應(yīng)的領(lǐng)域知識(shí)當(dāng)JavaScript也達(dá)到一定深度了,但是,還是看不大明白,或者沒法很深入到內(nèi)部去一探究竟。那就意味著缺少對(duì)應(yīng)的領(lǐng)域知識(shí)。這里明顯的就是編譯原理相關(guān)的知識(shí)。不過,其實(shí)對(duì)這塊了解個(gè)大概基本看起來就沒問題了。要再繼續(xù)深入,那需要對(duì)編譯原理了解的很深入,比如說詞法分析采用什么算法,一般怎么處理。會(huì)有什么問題,如何解決,AST生成算法一般有哪幾種等等。那要看編譯原理方面的書,也有基本經(jīng)典的書,比如Compilers: Principles, Techniques, an
10、d Tools這本也是傳說中的龍書,還有非常著名的SICP和PLAI。不過其實(shí)根據(jù)個(gè)人經(jīng)驗(yàn),對(duì)于Dmitry的文章,要看懂它,只要你對(duì)JavaScript有一定深度的了解,同時(shí)你大學(xué)計(jì)算機(jī)的課程都能大致掌握了(尤其是操作系統(tǒng)),也就是說基礎(chǔ)不錯(cuò),理解起來應(yīng)該沒問題。因?yàn)檫@些文章基本沒有涉及底層編譯相關(guān)的,只是在解釋文檔的內(nèi)容,并且其中很多東西都是相通的,比如:context的切換與CPU的進(jìn)程切換、函數(shù)相關(guān)的的局部變量的棧存儲(chǔ)、函數(shù)退出的操作等等都是一致的。以上就是個(gè)人對(duì)這個(gè)問題的看法,除此之外,我覺得,學(xué)習(xí)任何技術(shù)都不能操之過急,要把基礎(chǔ)打扎實(shí)了,這樣學(xué)什么都會(huì)很快。編寫可維護(hù)的代碼的重要性
11、軟件bug的修復(fù)是昂貴的,并且隨著時(shí)間的推移,這些bug的成本也會(huì)增加,尤其當(dāng)這些bug潛伏并慢慢出現(xiàn)在已經(jīng)發(fā)布的軟件中時(shí)。當(dāng)你發(fā)現(xiàn)bug 的時(shí)候就立即修復(fù)它是最好的,此時(shí)你代碼要解決的問題在你腦中還是很清晰的。否則,你轉(zhuǎn)移到其他任務(wù),忘了那個(gè)特定的代碼,一段時(shí)間后再去查看這些代碼就需要:· 花時(shí)間學(xué)習(xí)和理解這個(gè)問題· 花時(shí)間是了解應(yīng)該解決的問題代碼還有問題,特別對(duì)于大的項(xiàng)目或是公司,修復(fù)bug的這位伙計(jì)不是寫代碼的那個(gè)人(且發(fā)現(xiàn)bug和修復(fù)bug的不是同一個(gè)人)。因此,必須降低理解代碼花費(fèi)的時(shí)間,無論是一段時(shí)間前你自己寫的代碼還是團(tuán)隊(duì)中的其他成員寫的代碼。這關(guān)系到底線(營
12、業(yè)收入)和開發(fā)人員的幸福,因?yàn)槲覀兏鼞?yīng)該去開發(fā)新的激動(dòng)人心的事物而不是花幾小時(shí)幾天的時(shí)間去維護(hù)遺留代碼。另一個(gè)相關(guān)軟件開發(fā)生命的事實(shí)是,讀代碼花費(fèi)的時(shí)間要比寫來得多。有時(shí)候,當(dāng)你專注并深入思考某個(gè)問題的時(shí)候,你可以坐下來,一個(gè)下午寫大量的代碼。你的代碼很能很快就工作了,但是,隨著應(yīng)用的成熟,還會(huì)有很多其他的事情發(fā)生,這就要求你的進(jìn)行進(jìn)行審查,修改,和調(diào)整。例如:· bug是暴露的· 新功能被添加到應(yīng)用程序· 程序在新的環(huán)境下工作(例如,市場(chǎng)上出現(xiàn)新的瀏覽器)· 代碼改變用途· 代碼得完全從頭重新,或移植到另一個(gè)架構(gòu)上或者甚至使用另一種語言由于這
13、些變化,很少人力數(shù)小時(shí)寫的代碼最終演變成花數(shù)周來閱讀這些代碼。這就是為什么創(chuàng)建可維護(hù)的代碼對(duì)應(yīng)用程序的成功至關(guān)重要。可維護(hù)的代碼意味著:· 可讀的· 一致的· 可預(yù)測(cè)的· 看上去就像是同一個(gè)人寫的· 已記錄謹(jǐn)慎使用全局變量全局對(duì)象的概念JavaScript通過函數(shù)管理作用域。在函數(shù)內(nèi)部聲明的變量只在這個(gè)函數(shù)內(nèi)部可用,而在函數(shù)外面不可用。另一方面,全局變量就是在任何函數(shù)外面聲明的或是未聲明直接簡(jiǎn)單使用的。每個(gè)JavaScript環(huán)境有一個(gè)全局對(duì)象,當(dāng)你在任意的函數(shù)外面使用this的時(shí)候可以訪問到。你創(chuàng)建的每一個(gè)全局變量都成了這個(gè)全局對(duì)象的屬性。在
14、瀏覽器中,方便起見,該全局對(duì)象有個(gè)附加屬性叫做window,此window(通常)指向該全局對(duì)象本身。下面的代碼片段顯示了如何在瀏覽器環(huán)境中創(chuàng)建和訪問的全局變量:myglobal = "nowamagic" / 不推薦寫法console.log(myglobal); / "hello"console.log(window.m
15、yglobal); / "hello"console.log(window"myglobal"); / "hello"console.log(this.myglobal); / "hello"謹(jǐn)慎使用全局變量全局變量的問題在于,你的JavaScript應(yīng)用程序和web頁面上的所有代碼都
16、共享了這些全局變量,他們住在同一個(gè)全局命名空間,所以當(dāng)程序的兩個(gè)不同部分定義同名但不同作用的全局變量的時(shí)候,命名沖突在所難免。web頁面包含不是該頁面開發(fā)者所寫的代碼也是比較常見的,例如:第三方的JavaScript庫、廣告方的腳本代碼、第三方用戶跟蹤和分析腳本代碼、不同類型的小組件/標(biāo)志/按鈕。比方說,該第三方腳本定義了一個(gè)全局變量,叫做result;接著,在你的函數(shù)中也定義一個(gè)名為result的全局變量。其結(jié)果就是后面的變量覆蓋前面的,第三方腳本就一下子嗝屁啦!因此,要想和其他腳本成為好鄰居的話,盡可能少的使用全局變量是很重要的。一些減少全局變量的策略,例如命名空間模式或是函數(shù)立即自動(dòng)執(zhí)行
17、,但是要想讓全局變量少最重要的還是始終使用var來聲明變量。由于JavaScript的兩個(gè)特征,不自覺地創(chuàng)建出全局變量是出乎意料的容易。首先,你可以甚至不需要聲明就可以使用變量;第二,JavaScript有隱含的全局概念,意味著你不聲明的任何變量都會(huì)成為一個(gè)全局對(duì)象屬性。參考下面的代碼:function sum(x, y) / 不推薦寫法: 隱式全局變量 result = x + y;return result;此段代碼中的result沒有聲明。代碼照樣運(yùn)作正常,但在調(diào)用函數(shù)后你最后的結(jié)果就多一個(gè)全局命名空間,這可以是一個(gè)問題的根源。經(jīng)驗(yàn)法則是始終使用var聲明變量,正如改
18、進(jìn)版的sum()函數(shù)所演示的:function sum(x, y) var result = x + y; return result;另一個(gè)創(chuàng)建隱式全局變量的反例就是使用任務(wù)鏈進(jìn)行部分var聲明。下面的片段中,a是本地變量但是b確實(shí)全局變量,這可能不是你希望發(fā)生的:/ 反例,勿使用function foo() var a = b = 0; / .此現(xiàn)象發(fā)生的原因在于這個(gè)從右到左的賦值,首先,是賦值表達(dá)式b = 0,此情況下b是未聲明的。這個(gè)表達(dá)式的返回值是0,然后這個(gè)0就分配給了通過var定義的這個(gè)局部變量a。換句話說,就好比你輸入了:var a =
19、(b = 0);如果你已經(jīng)準(zhǔn)備好聲明變量,使用鏈分配是比較好的做法,不會(huì)產(chǎn)生任何意料之外的全局變量,如:function foo() var a, b; / . a = b = 0; / 兩個(gè)均局部變量然而,另外一個(gè)避免全局變量的原因是可移植性。如果你想你的代碼在不同的環(huán)境下(主機(jī)下)運(yùn)行,使用全局變量如履薄冰,因?yàn)槟銜?huì)無意中覆蓋你最初環(huán)境下不存在的主機(jī)對(duì)象(所以你原以為名稱可以放心大膽地使用,實(shí)際上對(duì)于有些情況并不適用)。var預(yù)解析與副作用var的副作用隱式全局變量和明確定義的全局變量間有些小的差異,就是通過delete操作符讓變量未定義的能力。· 通過var創(chuàng)建的全局變量(任何
20、函數(shù)之外的程序中創(chuàng)建)是不能被刪除的。· 無var創(chuàng)建的隱式全局變量(無視是否在函數(shù)中創(chuàng)建)是能被刪除的。這表明,在技術(shù)上,隱式全局變量并不是真正的全局變量,但它們是全局對(duì)象的屬性。屬性是可以通過delete操作符刪除的,而變量是不能的:/ 定義三個(gè)全局變量var global_var = 1;global_novar = 2; / 反面教材(function () global_fromfunc = 3; / 反面教材(); / 試圖刪除delete global_var; / falsedelete global_novar; / truedelete global_fromfu
21、nc; / true/ 測(cè)試該刪除typeof global_var; / "number"typeof global_novar; / "undefined"typeof global_fromfunc; / "undefined"在ES5嚴(yán)格模式下,未聲明的變量(如在前面的代碼片段中的兩個(gè)反面教材)工作時(shí)會(huì)拋出一個(gè)錯(cuò)誤。單var形式在函數(shù)頂部使用單var語句是比較有用的一種形式,其好處在于:· 提供了一個(gè)單一的地方去尋找功能所需要的所有局部變量· 防止變量在定義之前使用的邏輯錯(cuò)誤· 少代碼(類型啊傳值
22、啊單線完成)單var形式長(zhǎng)得就像下面這個(gè)樣子:function func() var a = 1, b = 2, sum = a + b, myobject = , i, j; / function body.您可以使用一個(gè)var語句聲明多個(gè)變量,并以逗號(hào)分隔。像這種初始化變量同時(shí)初始化值的做法是很好的。這樣子可以防止邏輯錯(cuò)誤(所有未初始化但聲明的變量的初始值是undefined)和增加代碼的可讀性。在你看到代碼后,你可以根據(jù)初始化的值知道這些變量大致的用途,例如是要當(dāng)作對(duì)象呢還是當(dāng)作整數(shù)來使。你也可以在聲明的時(shí)候做一些實(shí)際的工作,例如前面代碼中的sum = a + b這個(gè)情況,另外一個(gè)例子就
23、是當(dāng)你使用DOM(文檔對(duì)象模型)引用時(shí),你可以使用單一的var把DOM引用一起指定為局部變量,就如下面代碼所示的:function updateElement() var el = document.getElementById("result"), style = el.style; / 使用el和style干點(diǎn)其他什么事.var變量預(yù)解析JavaScript中,你可以在函數(shù)的任何位置聲明多個(gè)var語句,并且它們就好像是在函數(shù)頂部聲明一樣發(fā)揮作用,這種行為稱為 hoisting(懸置/置頂解析/預(yù)解析)。當(dāng)你使用了一個(gè)變量,然后不久在函數(shù)中又重新聲明的話,就可能產(chǎn)生邏輯錯(cuò)
24、誤。對(duì)于JavaScript,只要你的變量是在同一個(gè)作用域中(同一函數(shù)),它都被當(dāng)做是聲明的,即使是它在var聲明前使用的時(shí)候。看下面這個(gè)例子:/ 反例myname = "global" / 全局變量function func() alert(myname); / "undefined" var myname = "local" alert(myname); / "local"func();在這個(gè)例子中,你可能會(huì)以為第一個(gè)alert彈出的是”global”,第二個(gè)彈出”loacl”。這種期許是可以理解的,因?yàn)樵诘谝?/p>
25、個(gè)alert 的時(shí)候,myname未聲明,此時(shí)函數(shù)肯定很自然而然地看全局變量myname,但是,實(shí)際上并不是這么工作的。第一個(gè)alert會(huì)彈 出”undefined”是因?yàn)閙yname被當(dāng)做了函數(shù)的局部變量(盡管是之后聲明的),所有的變量聲明當(dāng)被懸置到函數(shù)的頂部了。因此,為了避免這種混亂,最好是預(yù)先聲明你想使用的全部變量。上面的代碼片段執(zhí)行的行為可能就像下面這樣:myname = "global" / global variablefunction func() var myname; / 等同于 -> var myname = undefined; alert(my
26、name); / "undefined" myname = "local" alert(myname); / "local"func();為了完整,我們?cè)偬嵋惶釄?zhí)行層面的稍微復(fù)雜點(diǎn)的東西。代碼處理分兩個(gè)階段,第一階段是變量、函數(shù)聲明、以及正常格式的參數(shù)創(chuàng)建,這是一個(gè)解析和進(jìn)入上下文的階段。第二個(gè)階段是代碼執(zhí)行、函數(shù)表達(dá)式和不合格的標(biāo)識(shí)符(為聲明的變量)被創(chuàng)建。但是,出于實(shí)用的目的,我們就采用了”hoisting”這個(gè)概念, 這種ECMAScript標(biāo)準(zhǔn)中并未定義,通常用來描述行為。訪問全局對(duì)象在瀏覽器中,全局對(duì)象可以通過window屬
27、性在代碼的任何位置訪問(除非你做了些比較出格的事情,像是聲明了一個(gè)名為window的局部變量)。但是在其他環(huán)境下,這個(gè)方便的屬性可能被叫做其他什么東西(甚至在程序中不可用)。如果你需要在沒有硬編碼的window標(biāo)識(shí)符下訪問全局對(duì)象,你可以在任何層級(jí)的函數(shù)作用域中做如下操作:var global = (function () return this;();這種方法可以隨時(shí)獲得全局對(duì)象,因?yàn)槠湓诤瘮?shù)中被當(dāng)做函數(shù)調(diào)用了(不是通過new構(gòu)造),this總是指向全局對(duì)象。實(shí)際上這個(gè)病不適用于ECMAScript 5嚴(yán)格模式,所以,在嚴(yán)格模式下時(shí),你必須采取不同的形式。例如,你正在開發(fā)一個(gè)JavaScri
28、pt庫,你可以將你的代碼包裹在一個(gè)即時(shí)函數(shù)中,然后從全局作用域中,傳遞一個(gè)引用指向this作為你即時(shí)函數(shù)的參數(shù)。JavaScript探秘:for循環(huán)在for循環(huán)中,你可以循環(huán)取得數(shù)組或是數(shù)組類似對(duì)象的值,譬如arguments和HTMLCollection對(duì)象。通常的循環(huán)形式如下:/ 次佳的循環(huán)for (var i = 0; i < myarray.length; i+) / 使用myarrayi做點(diǎn)什么這種形式的循環(huán)的不足在于每次循環(huán)的時(shí)候數(shù)組的長(zhǎng)度都要去獲取下。這回降低你的代碼,尤其當(dāng)myarray不是數(shù)組,而是一個(gè)HTMLCollection對(duì)象的時(shí)候。HTMLCollection
29、s指的是DOM方法返回的對(duì)象,例如:document.getElementsByName()document.getElementsByClassName()document.getElementsByTagName()還有其他一些HTMLCollections,這些是在DOM標(biāo)準(zhǔn)之前引進(jìn)并且現(xiàn)在還在使用的。有:document.images: 頁面上所有的圖片元素document.links : 所有a標(biāo)簽元素document.forms : 所有表單document.forms0.elements : 頁面上第一個(gè)表單中的所有域集合的麻煩在于它們實(shí)時(shí)查詢基本文檔(HTML頁面)。這意味著每
30、次你訪問任何集合的長(zhǎng)度,你要實(shí)時(shí)查詢DOM,而DOM操作一般都是比較昂貴的。這就是為什么當(dāng)你循環(huán)獲取值時(shí),緩存數(shù)組(或集合)的長(zhǎng)度是比較好的形式,正如下面代碼顯示的:for (var i = 0, max = myarray.length; i < max; i+) / 使用myarrayi做點(diǎn)什么這樣,在這個(gè)循環(huán)過程中,你只檢索了一次長(zhǎng)度值。注意到,當(dāng)你明確想要修改循環(huán)中的集合的時(shí)候(例如,添加更多的DOM元素),你可能更喜歡長(zhǎng)度更新而不是常量。伴隨著單var形式,你可以把變量從循環(huán)中提出來,就像下面這樣:function looper() var i = 0, max, myarra
31、y = ; / . for (i = 0, max = myarray.length; i < max; i+) / 使用myarrayi做點(diǎn)什么 這種形式具有一致性的好處,因?yàn)槟銏?jiān)持了單一var形式。不足在于當(dāng)重構(gòu)代碼的時(shí)候,復(fù)制和粘貼整個(gè)循環(huán)有點(diǎn)困難。例如,你從一個(gè)函數(shù)復(fù)制了一個(gè)循環(huán)到另一個(gè)函數(shù),你不得不去確定你能夠把i和max引入新的函數(shù)(如果在這里沒有用的話,很有可能你要從原函數(shù)中把它們刪掉)。最后一個(gè)需要對(duì)循環(huán)進(jìn)行調(diào)整的是使用下面表達(dá)式之一來替換i+。i = i + 1i += 1JSLint提示您這樣做,原因是+和-促進(jìn)了“過分棘手(excessive trickiness)
32、”。如果你直接無視它,JSLint的plusplus選項(xiàng)會(huì)是false(默認(rèn)是default)。還有兩種變化的形式,其又有了些微改進(jìn),因?yàn)椋?#183; 少了一個(gè)變量(無max)· 向下數(shù)到0,通常更快,因?yàn)楹?做比較要比和數(shù)組長(zhǎng)度或是其他不是0的東西作比較更有效率/第一種變化的形式:var i, myarray = ;for (i = myarray.length; i-;) /使用myarrayi做點(diǎn)什么/第二種使用while循環(huán): var myarray = , i = myarray.length;while (i-) /使用myarrayi做點(diǎn)什么這些小的改進(jìn)只體現(xiàn)在性能上
33、,此外JSLint會(huì)對(duì)使用i-加以抱怨。for-in循環(huán)(for-in_Loops)for-in循環(huán)應(yīng)該用在非數(shù)組對(duì)象的遍歷上,使用for-in進(jìn)行循環(huán)也被稱為“枚舉”。從技術(shù)上將,你可以使用for-in循環(huán)數(shù)組(因?yàn)镴avaScript中數(shù)組也是對(duì)象),但這是不推薦的。因?yàn)槿绻麛?shù)組對(duì)象已被自定義的功能增強(qiáng),就可能發(fā)生邏輯錯(cuò)誤。另外,在for-in中,屬性列表的順序(序列)是不能保證的。所以最好數(shù)組使用正常的for循環(huán),對(duì)象使用for-in循環(huán)。有個(gè)很重要的hasOwnProperty()方法,當(dāng)遍歷對(duì)象屬性的時(shí)候可以過濾掉原型鏈上的屬性。思考下面一段代碼:/ 對(duì)象var man = hand
34、s: 2, legs: 2, heads: 1;/ 在代碼的某個(gè)地方,一個(gè)方法添加給了所有對(duì)象if (typeof Ototype.clone = "undefined") Ototype.clone = function () ;在這個(gè)例子中,我們有一個(gè)使用對(duì)象字面量定義的名叫man的對(duì)象。在man定義完成后的某個(gè)地方,在對(duì)象原型上增加了一個(gè)很有用的名叫 clone()的方法。此原型鏈?zhǔn)菍?shí)時(shí)的,這就意味著所有的對(duì)象自動(dòng)可以訪問新的方法。為了避免枚舉man的時(shí)候出現(xiàn)clone()方法,你需要應(yīng)用hasOwnProperty()方法過濾原型
35、屬性。如果不做過濾,會(huì)導(dǎo)致clone()函數(shù)顯示出來,在大多數(shù)情況下這是不希望出現(xiàn)的。/ for-in 循環(huán)for (var i in man) if (man.hasOwnProperty(i) / 過濾 console.log(i, ":", mani); /* 控制臺(tái)顯示結(jié)果hands : 2legs : 2heads : 1*/ 反面例子:/ for-in loop without checking hasOwnProperty()for (var i in man) console.log(i, ":", mani);/*控制臺(tái)顯示結(jié)果hand
36、s : 2legs : 2heads : 1clone: function()*/另外一種使用hasOwnProperty()的形式是取消Ototype上的方法。像這樣:for (var i in man) if (Ototype.hasOwnProperty.call(man, i) / 過濾 console.log(i, ":", mani); 其好處在于在man對(duì)象重新定義hasOwnProperty情況下避免命名沖突。也避免了長(zhǎng)屬性查找對(duì)象的所有方法,你可以使用局部變量“緩存”它。var i, hasOwn = Object.pr
37、ototype.hasOwnProperty;for (i in man) if (hasOwn.call(man, i) / 過濾 console.log(i, ":", mani); 嚴(yán)格來說,不使用hasOwnProperty()并不是一個(gè)錯(cuò)誤。根據(jù)任務(wù)以及你對(duì)代碼的自信程度,你可以跳過它以提高些許的循環(huán)速度。但是當(dāng)你對(duì)當(dāng)前對(duì)象內(nèi)容(和其原型鏈)不確定的時(shí)候,添加hasOwnProperty()更加保險(xiǎn)些。格式化的變化(通不過JSLint)會(huì)直接忽略掉花括號(hào),把if語句放到同一行上。其優(yōu)點(diǎn)在于循環(huán)語句讀起來就像一個(gè)完整的想法(每個(gè)元素都有一個(gè)自己的屬性”X”,使用”X
38、”干點(diǎn)什么):/ 警告: 通不過JSLint檢測(cè)var i, hasOwn = Ototype.hasOwnProperty;for (i in man) if (hasOwn.call(man, i) / 過濾 console.log(i, ":", mani);Prototypes強(qiáng)大過頭了擴(kuò)展構(gòu)造函數(shù)的prototype屬性是個(gè)很強(qiáng)大的增加功能的方法,但有時(shí)候它太強(qiáng)大了。增加內(nèi)置的構(gòu)造函數(shù)原型(如Object(), Array(), 或Function())挺誘人的,但是這嚴(yán)重降低了可維護(hù)性,因?yàn)樗屇愕拇a變得難以預(yù)測(cè)。使用你代碼的其他開發(fā)人員很可
39、能更期望使用內(nèi)置的 JavaScript方法來持續(xù)不斷地工作,而不是你另加的方法。另外,屬性添加到原型中,可能會(huì)導(dǎo)致不使用hasOwnProperty屬性時(shí)在循環(huán)中顯示出來,這會(huì)造成混亂。因此,不增加內(nèi)置原型是最好的。你可以指定一個(gè)規(guī)則,僅當(dāng)下面的條件均滿足時(shí)例外:· 可以預(yù)期將來的ECMAScript版本或是JavaScript實(shí)現(xiàn)將一直將此功能當(dāng)作內(nèi)置方法來實(shí)現(xiàn)。例如,你可以添加ECMAScript 5中描述的方法,一直到各個(gè)瀏覽器都迎頭趕上。這種情況下,你只是提前定義了有用的方法。· 如果您檢查您的自定義屬性或方法已不存在也許已經(jīng)在代碼的其他地方實(shí)現(xiàn)或已經(jīng)是你支持的瀏
40、覽器JavaScript引擎部分。· 你清楚地文檔記錄并和團(tuán)隊(duì)交流了變化。如果這三個(gè)條件得到滿足,你可以給原型進(jìn)行自定義的添加,形式如下:if (typeof Otoype.myMethod != "function") Otoype.myMethod = function () / 實(shí)現(xiàn). ;eval()是“魔鬼”如果你現(xiàn)在的代碼中使用了eval(),記住該咒語“eval()是魔鬼”。此方法接受任意的字符串,并當(dāng)作JavaScript代碼來處理。當(dāng)有問題的代碼是事先知道的(不是運(yùn)行時(shí)確定的),沒有理由使用eval()。如果代碼
41、是在運(yùn)行時(shí)動(dòng)態(tài)生成,有一個(gè)更好的方式不使用eval而達(dá)到同樣的目標(biāo)。例如,用方括號(hào)表示法來訪問動(dòng)態(tài)屬性會(huì)更好更簡(jiǎn)單:/ 反面示例var property = "name"alert(eval("obj." + property);/ 更好的var property = "name"alert(objproperty);使用eval()也帶來了安全隱患,因?yàn)楸粓?zhí)行的代碼(例如從網(wǎng)絡(luò)來)可能已被篡改。這是個(gè)很常見的反面教材,當(dāng)處理Ajax請(qǐng)求得到的JSON 相應(yīng)的時(shí)候。在這些情況下,最好使用JavaScript內(nèi)置方法來解析JSON相應(yīng),
42、以確保安全和有效。若瀏覽器不支持JSON.parse(),你可以使用來自JSON.org的庫。同樣重要的是要記住,給setInterval(), setTimeout()和Function()構(gòu)造函數(shù)傳遞字符串,大部分情況下,與使用eval()是類似的,因此要避免。在幕后,JavaScript仍需要評(píng)估和執(zhí)行你給程序傳遞的字符串:/ 反面示例setTimeout("myFunc()", 1000);setTimeout("myFunc(1, 2, 3)", 1000); / 更好的setTimeout(myFunc, 1000);setTimeout(f
43、unction () myFunc(1, 2, 3);, 1000);使用新的Function()構(gòu)造就類似于eval(),應(yīng)小心接近。這可能是一個(gè)強(qiáng)大的構(gòu)造,但往往被誤用。如果你必須使用eval(),你可以考慮使用new Function()代替。有一個(gè)小的潛在好處,因?yàn)樵谛翭unction()中作代碼評(píng)估是在局部函數(shù)作用域中運(yùn)行,所以代碼中任何被評(píng)估的通過var 定義的變量都不會(huì)自動(dòng)變成全局變量。另一種方法來阻止自動(dòng)全局變量是封裝eval()調(diào)用到一個(gè)即時(shí)函數(shù)中。考慮下面這個(gè)例子,這里僅un作為全局變量污染了命名空間。console.log(typeof un); / "unde
44、fined"console.log(typeof deux); / "undefined"console.log(typeof trois); / "undefined" var jsstring = "var un = 1; console.log(un);"eval(jsstring); / logs "1"jsstring = "var deux = 2; console.log(deux);"new Function(jsstring)(); / logs "2&qu
45、ot;jsstring = "var trois = 3; console.log(trois);"(function () eval(jsstring);(); / logs "3" console.log(typeof un); / numberconsole.log(typeof deux); / "undefined"console.log(typeof trois); / "undefined"eval()和Function構(gòu)造的另一個(gè)不同的是eval()可以干擾作用域鏈,而Function()更安分守己
46、些。不管你在哪里執(zhí)行 Function(),它只看到全局作用域。所以其能很好的避免本地變量污染。在下面這個(gè)例子中,eval()可以訪問和修改它外部作用域中的變量,這是 Function做不到的(注意到使用Function和new Function是相同的)。(function () var local = 1; eval("local = 3; console.log(local)"); / logs "3" console.log(local); / logs "3"();(function () var local = 1; Fu
47、nction("console.log(typeof local);")(); / logs undefined();用parseInt()進(jìn)行數(shù)值轉(zhuǎn)換使用parseInt()你可以從字符串中獲取數(shù)值,該方法接受另一個(gè)基數(shù)參數(shù),這經(jīng)常省略,但不應(yīng)該。當(dāng)字符串以”0開頭的時(shí)候就有可能會(huì)出問題,例如,在ECMAScript 3中,開頭為”0的字符串被當(dāng)做8進(jìn)制處理了,但這已在ECMAScript 5中改變了。為了避免矛盾和意外的結(jié)果,總是指定基數(shù)參數(shù)。var month = "05", year = "09"month = parseIn
48、t(month, 10);year = parseInt(year, 10);alert(month);alert(year);此例中,如果你忽略了基數(shù)參數(shù),如parseInt(year),返回的值將是0,因?yàn)椤?9”被當(dāng)做8進(jìn)制(好比執(zhí)行 parseInt( year, 8 )),而09在8進(jìn)制中不是個(gè)有效數(shù)字。替換方法是將字符串轉(zhuǎn)換成數(shù)字,包括:+"08" / 結(jié)果是 8Number("08") / 8這些通常快于parseInt(),因?yàn)閜arseInt()方法,顧名思意,不是簡(jiǎn)單地解析與轉(zhuǎn)換。但是,如果你想輸入例如“08 hello”,parse
49、Int()將返回?cái)?shù)字,而其它以NaN告終。基本編碼規(guī)范建立和遵循編碼規(guī)范是很重要的,這讓你的代碼保持一致性,可預(yù)測(cè),更易于閱讀和理解。一個(gè)新的開發(fā)者加入這個(gè)團(tuán)隊(duì)可以通讀規(guī)范,理解其它團(tuán)隊(duì)成員書寫的代碼,更快上手干活。許多激烈的爭(zhēng)論發(fā)生在會(huì)議上或是郵件列表上,問題往往針對(duì)某些代碼規(guī)范的特定方面(例如代碼縮進(jìn),是Tab制表符鍵還是space空格鍵)。如果你是組織中建議采用規(guī)范的,準(zhǔn)備好面對(duì)各種反對(duì)的或是聽起來不同但很強(qiáng)烈的觀點(diǎn)。要記住,建立和堅(jiān)定不移地遵循規(guī)范要比糾結(jié)于規(guī)范的細(xì)節(jié)重要的多。縮進(jìn)(Indentation)代碼沒有縮進(jìn)基本上就不能讀了。唯一糟糕的事情就是不一致的縮進(jìn),因?yàn)樗瓷先ハ袷亲?/p>
50、循了規(guī)范,但是可能一路上伴隨著混亂和驚奇。重要的是規(guī)范地使用縮進(jìn)。一些開發(fā)人員更喜歡用tab制表符縮進(jìn),因?yàn)槿魏稳硕伎梢哉{(diào)整他們的編輯器以自己喜歡的空格數(shù)來顯示Tab。有些人喜歡空格通常四個(gè),這都無所謂,只要團(tuán)隊(duì)每個(gè)人都遵循同一個(gè)規(guī)范就好了。這本書,例如,使用四個(gè)空格縮進(jìn),這也是JSLint中默認(rèn)的縮進(jìn)。什么應(yīng)該縮進(jìn)呢?規(guī)則很簡(jiǎn)單花括號(hào)里面的東西。這就意味著函數(shù)體,循環(huán) (do, while, for, for-in),if,switch,以及對(duì)象字面量中的對(duì)象屬性。下面的代碼就是使用縮進(jìn)的示例:function outer(a, b) var c = 1, d = 2, inner; if
51、(a > b) inner = function () return r: c - d ; ; else inner = function () return r: c + d ; ; return inner;花括號(hào)(Curly Braces)花括號(hào)(亦稱大括號(hào),下同)應(yīng)總被使用,即使在它們?yōu)榭蛇x的時(shí)候。技術(shù)上將,在in或是for中如果語句僅一條,花括號(hào)是不需要的,但是你還是應(yīng)該總是使用它們,這會(huì)讓代碼更有持續(xù)性和易于更新。想象下你有一個(gè)只有一條語句的for循環(huán),你可以忽略花括號(hào),而沒有解析的錯(cuò)誤。/ 糟糕的實(shí)例for (var i = 0; i < 10; i += 1) al
52、ert(i);但是,如果,后來,主體循環(huán)部分又增加了行代碼:/ 糟糕的實(shí)例for (var i = 0; i < 10; i += 1) alert(i); alert(i + " is " + (i % ? "odd" : "even");第二個(gè)alert已經(jīng)在循環(huán)之外,縮進(jìn)可能欺騙了你。為了長(zhǎng)遠(yuǎn)打算,最好總是使用花括號(hào),即時(shí)值一行代碼:/ 好的實(shí)例for (var i = 0; i < 10; i += 1) alert(i);if條件類似:/ 壞if (true) alert(1);else alert(2); /
53、好if (true) alert(1); else alert(2);左花括號(hào)的位置(Opening Brace Location)開發(fā)人員對(duì)于左大括號(hào)的位置有著不同的偏好在同一行或是下一行。if (true) alert("It's TRUE!"); /或 if (true) alert("It's TRUE!");這個(gè)實(shí)例中,仁者見仁智者見智,但也有個(gè)案,括號(hào)位置不同會(huì)有不同的行為表現(xiàn)。這是因?yàn)榉痔?hào)插入機(jī)制(semicolon insertion mechanism)JavaScript是不挑剔的,當(dāng)你選擇不使用分號(hào)結(jié)束一行代碼時(shí)Ja
54、vaScript會(huì)自己幫你補(bǔ)上。這種行為可能會(huì)導(dǎo)致麻煩,如當(dāng)你返回對(duì)象字面量,而左括號(hào)卻在下一行的時(shí)候:/ 警告: 意外的返回值function func() return / 下面代碼不執(zhí)行 name : "Batman" 如果你希望函數(shù)返回一個(gè)含有name屬性的對(duì)象,你會(huì)驚訝。由于隱含分號(hào),函數(shù)返回undefined。前面的代碼等價(jià)于:/ 警告: 意外的返回值function func() return undefined; / 下面代碼不執(zhí)行 name : "Batman" 總之,總是使用花括號(hào),并始終把在與之前的語句放在同一行:function
55、func() return name : "Batman" ;關(guān)于分號(hào)注:就像使用花括號(hào),你應(yīng)該總是使用分號(hào),即使他們可由JavaScript解析器隱式創(chuàng)建。這不僅促進(jìn)更科學(xué)和更嚴(yán)格的代碼,而且有助于解決存有疑惑的地方,就如前面的例子顯示。空格(White Space)空格的使用同樣有助于改善代碼的可讀性和一致性。在寫英文句子的時(shí)候,在逗號(hào)和句號(hào)后面會(huì)使用間隔。在JavaScript中,你可以按照同樣的邏輯在列表表達(dá)式(相當(dāng)于逗號(hào))和結(jié)束語句(相對(duì)于完成了“想法”)后面添加間隔。適合使用空格的地方包括:· for循環(huán)分號(hào)分開后的的部分:如for (var i =
56、0; i < 10; i += 1) .· for循環(huán)中初始化的多變量(i和max):for (var i = 0, max = 10; i < max; i += 1) .· 分隔數(shù)組項(xiàng)的逗號(hào)的后面:var a = 1, 2, 3;· 對(duì)象屬性逗號(hào)的后面以及分隔屬性名和屬性值的冒號(hào)的后面:var o = a: 1, b: 2;· 限定函數(shù)參數(shù):myFunc(a, b, c)· 函數(shù)聲明的花括號(hào)的前面:function myFunc() · 匿名函數(shù)表達(dá)式function的后面:var myFunc = function
57、() ;使用空格分開所有的操作符和操作對(duì)象是另一個(gè)不錯(cuò)的使用,這意味著在+, -, *, =, <, >, <=, >=, =, !=, &&, |, +=等前后都需要空格。/ 寬松一致的間距/ 使代碼更易讀/ 使得更加“透氣”var d = 0, a = b + 1;if (a && b && c) d = a % c; a += d; / 反面例子/ 缺失或間距不一/ 使代碼變得疑惑var d = 0, a = b + 1;if (a&&b&&c) d=a % c; a+= d;最后需要注意的一個(gè)空格花括號(hào)間距。最好使用空格:· 函數(shù)、if-else語句、循環(huán)、對(duì)象字面量的左花括號(hào)的前面()· else或while之間的右花括號(hào)()空格使用的一點(diǎn)不足就是增加了文件的大小,但是壓縮無此問題。有一個(gè)經(jīng)常被忽略的代碼可讀性方面是垂直空格的使用。你可以使用空行來分隔代碼單元,就像是文學(xué)作品中使用段落分隔一樣。命名規(guī)范(Naming Conventions)另一種方法讓你的代碼更具可預(yù)測(cè)性和可維護(hù)性是采用命名規(guī)范。這就意味著你需要用同一種形式
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 南京醫(yī)科大學(xué)《物聯(lián)網(wǎng)自動(dòng)識(shí)別技術(shù)》2023-2024學(xué)年第二學(xué)期期末試卷
- 醫(yī)療用品購銷合同
- 哪位手上有裝修包清工合同
- 合同之伸縮縫施工合同
- 住宅套內(nèi)公共區(qū)域裝修工程合同
- 茶藝師(高級(jí))復(fù)習(xí)測(cè)試題
- 統(tǒng)編版語文三年級(jí)下冊(cè)第六單元習(xí)作身邊那些有特點(diǎn)的人 公開課一等獎(jiǎng)創(chuàng)新教學(xué)設(shè)計(jì)
- 糧食玉米購銷合同范本
- 樹木供銷合同范本
- 主播月合同范本
- 2025年?duì)I口職業(yè)技術(shù)學(xué)院高職單招(數(shù)學(xué))歷年真題考點(diǎn)含答案解析
- 江西省八所重點(diǎn)2025屆高三下學(xué)期4月聯(lián)考(二模)政治試卷(含答案)
- 2025年03月福建省氣象局事業(yè)單位筆試歷年典型考題(歷年真題考點(diǎn))解題思路附帶答案詳解
- 2025年青藏鐵路集團(tuán)有限公司招聘(184人)筆試參考題庫附帶答案詳解
- 中國釣魚行業(yè)發(fā)展監(jiān)測(cè)及投資戰(zhàn)略規(guī)劃研究報(bào)告
- 2025資陽輔警考試題庫
- 2025年上海嘉定區(qū)江橋鎮(zhèn)企業(yè)服務(wù)有限公司招聘筆試參考題庫附帶答案詳解
- 第一篇 專題三 計(jì)算題培優(yōu)3 帶電粒子在交變場(chǎng)和立體空間中的運(yùn)動(dòng)-2025高考物理二輪復(fù)習(xí)
- 學(xué)校保潔方案
- 2025年高考語文二輪復(fù)習(xí)策略講座
- T-SCSF 0004-2020 海洋牧場(chǎng)海藻場(chǎng)建設(shè)技術(shù)規(guī)范
評(píng)論
0/150
提交評(píng)論