??????如果JavaScript程序僅僅是在引擎中運行的話被啼,它會嚴格遵循規(guī)范并且是可以預(yù)測的郑叠。但是JavaScript程序幾乎總是在宿主環(huán)境中運行秋泳,這使得它在一定程度上變得不可預(yù)測。例如倡缠,當你的代碼和其他第三方代碼一起運行哨免,或者當你的代碼在不同的JavaScript引擎(并非僅僅是瀏覽器)上運行時,有些地方就會出現(xiàn)差異昙沦。
1. Annex B(ECMAScript)
??????文檔“Annex B”和“WebECMAScript”舰始,記錄了一些官方ECMAScript規(guī)范和目前基于瀏覽器的JavaScript實現(xiàn)之間的差異炕吸。
??????比如:window.escape(..)和window.unescape(..)就不是ECMAScript官方標準里有的。瀏覽器JavaScript里有,但其它非瀏覽器的JavaScript引擎里很可能沒有哆档。
2. 宿主對象
??????JavaScript中有關(guān)變量的規(guī)則定義得十分清楚,但也不乏一些例外情況彭羹,比如自動定義的變量奕筐,以及由宿主環(huán)境(瀏覽器等)創(chuàng)建并提供給JavaScript引擎的變量——所謂的“宿主對象”。
??????宿主對象可能的行為差異:
??????①無法訪問正常的object內(nèi)建方法徘钥,如toString()
??????②無法寫覆蓋
??????③包含一些預(yù)定義的只讀屬性
??????④包含無法將this重載為其他對象的方法
??????我們經(jīng)常接觸的console就是一個宿主對象衔蹲,console在瀏覽器中是輸出到開發(fā)工具控制臺。而在Node.js和其他服務(wù)器端JavaScript環(huán)境中呈础,則是指向JavaScript環(huán)境系統(tǒng)進程的標準輸出(stdout)和標準錯誤輸出(stderr)舆驶。
3. 全局DOM對象
??????由于瀏覽器演進的歷史遺留問題,在創(chuàng)建帶有id屬性的DOM元素時也會創(chuàng)建同名的全局變量而钞。
??????這也是盡量不要使用全局變量的一個原因沙廉。如果確實要用,也要確保變量名的唯一性臼节,從而避免與其他地方的變量產(chǎn)生沖突蓝仲,包括HTML和其他第三方代碼。
4. 原生原型
??????不要擴展原生原型官疲。
??????因為你給原生對象添加的方法和屬性袱结,可能會在若干年后,剛巧跟JavaScript自己的變動發(fā)生沖突途凫,JavaScript可能也擴展了那個對象垢夹,添加了新方法和屬性,起的名字還跟你當年起的一樣维费。
??????也可能還有第三方代碼也干跟你一樣的傻事果元,然后你們沖突了促王。
??????所以大家都不應(yīng)該擴展原生原型,這不好而晒。
??????但polyfill是可以的蝇狼。
??????polyfill就是,已知新規(guī)范中有某方法倡怎,而一些不支持新規(guī)范的運行環(huán)境中沒有迅耘,給這些老運行環(huán)境加上這些方法。
??????polyfill能有效地為不符合最新規(guī)范的老版本瀏覽器填補缺失的功能监署。
??????ES5-Shim(https://github.com/es-shims/es5-shim)是一個完整的shim/polyfill集合颤专,能夠為你的項目提供ES5基本規(guī)范支持。同樣钠乏,ES6-Shim(https://github.com/es-shims/es6-shim)提供了對ES6基本規(guī)范的支持栖秕。
??????對于語法上的差別,可以用Traceur這樣的工具來實現(xiàn)新舊語法之間的轉(zhuǎn)換晓避。
5. <script>
??????在網(wǎng)頁中使用<script src=..></script>加載的js文件簇捍,以及在頁面內(nèi)用<script> .. </script>來包含的內(nèi)聯(lián)代碼,它們之間有些什么規(guī)則俏拱?
??????①它們共享global對象(在瀏覽器中則是window)垦写,也就是說這些文件中的代碼在共享的命名空間中運行,并相互交互彰触。
??????②如果某個script中定義了函數(shù)foo(),后面的script代碼就可以訪問并調(diào)用foo()命辖,就像foo()在其內(nèi)部被聲明過一樣况毅。
??????③但是全局變量作用域的提升機制在這些邊界中不適用。后面的script中聲明的變量尔艇,不會被提升到前面的script中尔许,在前面的script中訪問變量會拋出錯誤。
??????④如果script中的代碼發(fā)生錯誤终娃,它會像獨立的JavaScript程序那樣停止味廊,但是后續(xù)的script中的代碼(仍然共享global)依然會接著運行,不會受影響棠耕。
??????⑤在內(nèi)聯(lián)代碼中不可以出現(xiàn)</script>字符串余佛,一旦出現(xiàn)即被視為代碼塊結(jié)束。外部文件代碼則沒關(guān)系窍荧。
??????⑥我們是根據(jù)代碼文件的字符集屬性(UTF-8辉巡、ISO-8859-8等)來解析外部文件中的代碼(或者默認字符集),而內(nèi)聯(lián)代碼則使用其所在頁面文件的字符集(或者默認字符集)蕊退。內(nèi)聯(lián)代碼的script標簽沒有charset屬性郊楣。
6. 保留字
??????ES5規(guī)范在7.6.1節(jié)中定義了一些“保留字”憔恳,我們不能將它們用作變量名。這些保留字有四類:“關(guān)鍵字”净蚤、“預(yù)留關(guān)鍵字”钥组、null常量和true/false布爾常量。
??????在ES5之前今瀑,保留字也不能用來作為對象常量中的屬性名稱或者鍵值程梦,但ES5出來后就沒有這個限制了。
??????具體要看瀏覽器環(huán)境放椰,有些老版本瀏覽器還是不允許用保留字作為對象屬性作烟。所以注意測試各環(huán)境運行情況。
7. 實現(xiàn)中的限制
??????JavaScript規(guī)范對于函數(shù)中參數(shù)的個數(shù)砾医,以及字符串常量的長度等并沒有限制拿撩;但是由于JavaScript引擎實現(xiàn)各異,規(guī)范在某些地方有一些限制如蚜。
??????如:
??????①字符串常量中允許的最大字符數(shù)(并非只是針對字符串值)
??????②可以作為參數(shù)傳遞到函數(shù)中的數(shù)據(jù)大醒购恪(也稱為棧大小,以字節(jié)為單位)
??????③函數(shù)聲明中的參數(shù)個數(shù)
??????④未經(jīng)優(yōu)化的調(diào)用棧(例如遞歸)的最大層數(shù)错邦,即函數(shù)調(diào)用鏈的最大長度
??????⑤JavaScript程序以阻塞方式在瀏覽器中運行的最長時間(秒)
??????⑥變量名的最大長度
??????我們不會經(jīng)常碰到這些限制探赫,但應(yīng)該對它們有所了解,特別是不同的JavaScript引擎的限制各異撬呢。