感謝社區(qū)中各位的大力支持,譯者再次奉上一點點福利:阿里云產(chǎn)品券焙压,享受所有官網(wǎng)優(yōu)惠谆趾,并抽取幸運大獎:點擊這里領(lǐng)取
當(dāng)你的JS代碼在真實世界中運行時于购,除了我們在本書中完整探索過的核心語言機(jī)制以外巩梢,它還有好幾種不同的行為方式创泄。如果JS純粹地運行在一個引擎中,那么它就會按照語言規(guī)范非黑即白地動作且改,是完全可以預(yù)測的验烧。但是JS很可能總是運行在一個宿主環(huán)境的上下文中板驳,這將會給你的代碼帶來某種程度的不可預(yù)測性又跛。
例如,當(dāng)你的代碼與源自于其他地方的代碼并肩運行時若治,或者當(dāng)你的代碼在不同種類的JS引擎(不只是瀏覽器)中運行時慨蓝,有些事情的行為就可能不同。
我們將簡要地探索這些問題中的一些端幼。
Annex B (ECMAScript)
一個鮮為人知的事實是礼烈,這門語言的官方名稱是ECMAScript(意指管理它的ECMA標(biāo)準(zhǔn)本體)。那么“JavaScript”是什么婆跑?JavaScript是這門語言常見的商業(yè)名稱此熬,當(dāng)然,更恰當(dāng)?shù)卣f,JavaScript基本上是語言規(guī)范的瀏覽器實現(xiàn)犀忱。
官方的ECMAScript語言規(guī)范包含“Annex B”募谎,它是為了瀏覽器中JS的兼容性,討論那些與官方語言規(guī)范有偏差的特別部分阴汇。
考慮這些偏差部分的恰當(dāng)方法是数冬,它們僅在你的代碼運行在瀏覽器中時才是確實會出現(xiàn)/合法的。如果你的代碼總是運行在瀏覽器中搀庶,那你就不會看到明顯的不同拐纱。如果不是(比如它可以運行在node.js、Rhino中哥倔,等等)秸架,或者你不確定,那么就要小心對待未斑。
兼容性上的主要不同是:
- 八進(jìn)制數(shù)字字面量是允許的咕宿,比如在非
strict mode
下的0123
(小數(shù)83
)。 -
window.escape(..)
和window.unescape(..)
允許你使用%
分割的十六進(jìn)制轉(zhuǎn)義序列來轉(zhuǎn)義或非轉(zhuǎn)義字符串蜡秽。例如:window.escape( "?foo=97%&bar=3%" )
產(chǎn)生"%3Ffoo%3D97%25%26bar%3D3%25"
-
String.prototype.substr
與String.prototype.substring
十分相似府阀,除了第二個參數(shù)是length
(要包含的字符數(shù)),而非結(jié)束(不含)的索引芽突。
Web ECMAScript
Web ECMAScript語言規(guī)范(http://javascript.spec.whatwg.org/)涵蓋了官方ECMAScript語言規(guī)范與當(dāng)前瀏覽器中JavaScript實現(xiàn)之間的不同试浙。
換言之,這些項目是瀏覽器的“必須品”(為了相互兼容)寞蚌,但是(在本書編寫時)沒有列在官方語言規(guī)范的“Annex B”部分是:
- ``是合法的單行注釋分割符田巴。
-
String.prototype
擁有返回HTML格式化字符串的附加方法:anchor(..)
、big(..)
挟秤、blink(..)
壹哺、bold(..)
、fixed(..)
艘刚、fontcolor(..)
管宵、fontsize(..)
、italics(..)
攀甚、link(..)
箩朴、small(..)
、strike(..)
秋度、和sub(..)
炸庞。注意: 它們在實際應(yīng)用中非常罕見,而且一般來說不鼓勵使用荚斯,而是用其他內(nèi)建DOM API或用戶定義的工具取代埠居。 -
RegExp
擴(kuò)展:RegExp.$1
..RegExp.$9
(匹配組)和RegExp.lastMatch
/RegExp["$&"]
(最近的匹配)查牌。 -
Function.prototype
附加功能:Function.prototype.arguments
(內(nèi)部arguments
對象的別名)和Function.caller
(內(nèi)部arguments.caller
的別名)。注意:arguments
和arguments.caller
都被廢棄了滥壕,所以你應(yīng)當(dāng)盡可能避免使用它們僧免。這些別名更是這樣 —— 不要使用它們!
注意: 其他的一些微小和罕見的偏差點沒有包含在我們這里的列表中捏浊。有必要的話懂衩,更多詳細(xì)信息可以參見外部的“Annex B”和“Web ECMAScript”文檔。
一般來說金踪,所有這些不同點都很少被使用浊洞,所以這些與語言規(guī)范有出入的地方不是什么重大問題。只是如果你依賴于其中任何一個的話胡岔,要小心法希。
宿主對象
JS中變量的行為有一些廣為人知的例外 —— 當(dāng)它們是被自動定義,或由持有你代碼的環(huán)境(瀏覽器等)創(chuàng)建并提供給JS時 —— 也就是所謂的“宿主對象”(包括object
和function
兩者)靶瘸。
例如:
var a = document.createElement( "div" );
typeof a; // "object" -- 意料之中的
Object.prototype.toString.call( a ); // "[object HTMLDivElement]"
a.tagName; // "DIV"
a
不僅是一個object
苫亦,而且是一個特殊的宿主對象,因為它是一個DOM元素怨咪。它擁有一個不同的內(nèi)部[[Class]]
值("HTMLDivElement"
)屋剑,而且?guī)в蓄A(yù)定義的(而且通常是不可更改的)屬性。
另一個已經(jīng)在第四章的“Falsy對象”一節(jié)中探討過的同樣的怪異之處是:存在這樣一些對象诗眨,當(dāng)被強(qiáng)制轉(zhuǎn)換為boolean
時唉匾,它們將(令人糊涂地)被轉(zhuǎn)換為false
而不是預(yù)期的true
。
另一些需要小心的宿主對象行為包括:
- 不能訪問像
toString()
這樣的object
內(nèi)建方法 - 不可覆蓋
- 擁有特定的預(yù)定義只讀屬性
- 擁有一些
this
不可被重載為其他對象的方法 - 其他……
為了使我們的JS代碼與它外圍的環(huán)境一起工作匠楚,宿主對象至關(guān)重要巍膘。但在你與宿主對象交互時是要特別注意,并且在推測它的行為時要小心芋簿,因為它們經(jīng)常與普通的JSobject
不符峡懈。
一個盡人皆知的你可能經(jīng)常與之交互的宿主對象的例子,就是console
對象和他的各種函數(shù)(log(..)
与斤、error(..)
等等)肪康。console
對象是由 宿主環(huán)境 特別提供的,所以你的代碼可以與之互動來進(jìn)行各種開發(fā)相關(guān)的輸出任務(wù)幽告。
在瀏覽器中梅鹦,console
與開發(fā)者工具控制臺的顯示相勾連裆甩,因此在node.js和其他服務(wù)器端JS環(huán)境中冗锁,console
一般連接著JavaScript環(huán)境系統(tǒng)進(jìn)程的標(biāo)準(zhǔn)輸出流(stdout
)和標(biāo)準(zhǔn)錯誤流(stderr
)。
全局DOM變量
你可能知道嗤栓,在全局作用域中聲明變量(用或者不用var
)不僅會創(chuàng)建一個全局變量冻河,還會創(chuàng)建它的鏡像:在global
對象(瀏覽器中的window
)上的同名屬性箍邮。
但少為人知的是,(由于瀏覽器的遺留行為)使用id
屬性創(chuàng)建DOM元素會創(chuàng)建同名的全局變量叨叙。例如:
<div id="foo"></div>
和:
if (typeof foo == "undefined") {
foo = 42; // 永遠(yuǎn)不會運行
}
console.log( foo ); // HTML元素
你可能臆測只有JS代碼會創(chuàng)建這樣的變量溉委,并習(xí)慣于在這樣假定的前提下進(jìn)行全局變量檢測(使用typeof
或者.. in window
檢查)手负,但是如你所見,你的宿主HTML頁面的內(nèi)容也會創(chuàng)建它們,如果你不小心它們就可以輕而易舉地擺脫你的存在性檢查熄求。
這就是另一個你為什么應(yīng)該盡全力避免使用全局變量的原因,如果你不得不這樣做翅帜,那就使用不太可能沖突的變量名怜浅。但是你還是需要確認(rèn)它不會與HTML的內(nèi)容以及其他的代碼相沖突。
原生原型
最廣為人知的爽醋,經(jīng)典的JavaScript 最佳實踐 智慧之一是:永遠(yuǎn)不要擴(kuò)展原生原型蚁署。
當(dāng)你將方法或?qū)傩蕴砑拥?code>Array.prototype時,無論你想出什么樣的(還)不存在于Array.prototype
上名稱蚂四,如果它是有用的光戈、設(shè)計良好的、并且被恰當(dāng)命名的新增功能遂赠,那么它就有很大的可能性被最終加入語言規(guī)范 —— 這種情況下你的擴(kuò)展就處于沖突之中久妆。
這里有一個真實地發(fā)生在我身上的例子,很好地展示了這一點跷睦。
那時我正在為其他網(wǎng)站建造一個可嵌入的控件镇饺,而且我的控件依賴于JQuery(雖然任何框架都很可能遭受這樣的坑)。它幾乎可以在每一個網(wǎng)站上工作送讲,但是我們碰到了一個它會完全崩潰的網(wǎng)站奸笤。
經(jīng)過差不多一周的分析/調(diào)試之后,我發(fā)現(xiàn)這個出問題的網(wǎng)站有這樣一段代碼哼鬓,埋藏在它的一個遺留文件的深處:
// Netscape 4 沒有 Array.push
Array.prototype.push = function(item) {
this[this.length] = item;
};
除了那瘋狂的注釋(誰還會關(guān)心Netscape 4<嘤摇?)异希,它看起來很合理健盒,對吧?
問題是称簿,在這段 Netscape 4 時代的代碼被編寫之后的某個時點扣癣,Array.prototype.push
被加入了語言規(guī)范,但是被加入的東西與這段代碼是不兼容的憨降。標(biāo)準(zhǔn)的push(..)
允許一次加入多個項目父虑,而這個黑進(jìn)來的東西會忽略后續(xù)項目。
基本上所有的JS框架都有這樣的代碼 —— 依賴于帶有多個元素的push(..)
授药。在我的例子中士嚎,我在圍繞著一個完全被毀壞的CSS選擇器引擎進(jìn)行編碼呜魄。但是可以料想到還有其他十幾處可疑的地方。
一開始編寫這個push(..)
黑科技的開發(fā)者稱它為push
莱衩,這種直覺很正確爵嗅,但是沒有預(yù)見到添加多個元素。當(dāng)然他們的初衷是好的笨蚁,但是也埋下了一個地雷睹晒,當(dāng)我差不多在10年之后路過時才不知不覺地踩上。
這里要吸取幾個教訓(xùn)括细。
第一册招,不要擴(kuò)展原生類型,除非你絕對確信你的代碼將是運行在那個環(huán)境中的唯一代碼勒极。如果你不能100%確信是掰,那么擴(kuò)展原生類型就是危險的。你必須掂量掂量風(fēng)險辱匿。
其次键痛,不要無條件地定義擴(kuò)展(因為你可能意外地覆蓋原生類型)。就這個特定的例子匾七,用代碼說話就是:
if (!Array.prototype.push) {
// Netscape 4 沒有 Array.push
Array.prototype.push = function(item) {
this[this.length] = item;
};
}
if
守護(hù)語句將會僅在JS環(huán)境中不存在push()
時才定義那個push()
黑科技絮短。在我的情況中,這可能就夠了昨忆。但即便是這種方式也不是沒有風(fēng)險:
- 如果網(wǎng)站的代碼(為了某些瘋狂的理由6∑怠)有賴于忽略多個項目的
push(..)
,那么幾年以后當(dāng)標(biāo)準(zhǔn)的push(..)
推出時邑贴,那些代碼將會壞掉席里。 - 如果有其他庫被引入,并在這個
if
守護(hù)之前就黑進(jìn)了push(..)
拢驾,而且還是以一種不兼容的方式奖磁,那么它就在那一刻毀壞了這個網(wǎng)站。
這里的重點繁疤,坦白地講咖为,是一個沒有得到JS開發(fā)者們足夠重視的有趣問題:如果在你代碼運行的環(huán)境中,你的代碼不是唯一的存在稠腊,那么 你應(yīng)該依賴于任何原生的內(nèi)建行為嗎?
嚴(yán)格的答案是 不躁染,但這非常不切實際。你的代碼通常不會為所有它依賴的內(nèi)建行為重新定義它自己的架忌、不可接觸的私有版本吞彤。即便你 能,那也是相當(dāng)?shù)睦速M鳖昌。
那么备畦,你應(yīng)當(dāng)為內(nèi)建行為進(jìn)行特性測試,以及為了驗證它能如你預(yù)期的那樣工作而進(jìn)行兼容性測試嗎许昨?但如果測試失敗了 —— 你的代碼應(yīng)當(dāng)拒絕運行嗎懂盐?
// 不信任 Array.prototype.push
(function(){
if (Array.prototype.push) {
var a = [];
a.push(1,2);
if (a[0] === 1 && a[1] === 2) {
// 測試通過,可以安全使用糕档!
return;
}
}
throw Error(
"Array#push() is missing/broken!"
);
})();
理論上莉恼,這貌似有些道理,但是為每一個內(nèi)建方法設(shè)計測試還是非常不切實際速那。
那么俐银,我們應(yīng)當(dāng)怎么做?我們應(yīng)當(dāng) 信賴但驗證(特性測試和兼容性測試)每一件事嗎端仰?我們應(yīng)當(dāng)假設(shè)既存的東西是符合規(guī)范的并讓(由他人)造成的破壞任意傳播嗎捶惜?
沒有太好的答案±笊眨可以觀察到的唯一事實是吱七,擴(kuò)展原生原型是這些東西咬到你的唯一方式。
如果你不這么做鹤竭,而且在你的應(yīng)用程序中也沒有其他人這么做踊餐,那么你就是安全的。否則臀稚,你就應(yīng)當(dāng)多多少少建立一些懷疑的吝岭、悲觀的機(jī)制、并對可能的破壞做好準(zhǔn)備吧寺。
在所有已知環(huán)境中窜管,為你的代碼準(zhǔn)備一整套單元/回歸測試是發(fā)現(xiàn)一些前述問題的方法,但是它不會對這些沖突為你做出任何實際的保護(hù)稚机。
Shims/Polyfills
人們常說微峰,擴(kuò)展一個原生類型唯一安全的地方是在一個(不兼容語言規(guī)范的)老版本環(huán)境中,因為它不太可能再改變了 —— 帶有新語言規(guī)范特性的新瀏覽器會取代老版本瀏覽器抒钱,而非改良它們蜓肆。
如果你能預(yù)見未來,而且確信未來的標(biāo)準(zhǔn)將是怎樣谋币,比如Array.prototype.foobar
仗扬,那么現(xiàn)在就制造你自己的兼容版本來使用就是完全安全的,對吧蕾额?
if (!Array.prototype.foobar) {
// 愚蠢早芭,愚蠢
Array.prototype.foobar = function() {
this.push( "foo", "bar" );
};
}
如果已經(jīng)有了Array.prototype.foobar
的規(guī)范,而且規(guī)定的行為與這個邏輯等價诅蝶,那么你定義這樣的代碼段就十分安全退个,在這種情況下它通常稱為一個“polyfill(填補)”(或者“shim(墊片)”)募壕。
在你的代碼庫中引入這樣的代碼,對給那些沒有更新到最新規(guī)范的老版本瀏覽器環(huán)境打“補丁”非常 有用语盈。為所有你支持的環(huán)境創(chuàng)建可預(yù)見的代碼舱馅,使用填補是非常好的方法。
提示: ES5-Shim (https://github.com/es-shims/es5-shim) 是一個將項目代碼橋接至ES5基準(zhǔn)線的完整的shims/polyfills集合刀荒,相似地代嗤,ES6-Shim (https://github.com/es-shims/es6-shim) 提供了ES6新增的新API的shim。雖然API可以被填補缠借,但新的語法通常是不能的干毅。要橋接語法的部分,你將還需要使用一個ES6到ES5的轉(zhuǎn)譯器泼返,比如Traceur (https://github.com/google/traceur-compiler/wiki/GettingStarted)硝逢。
如果有一個即將到來的標(biāo)準(zhǔn),而且關(guān)于它叫什么名字和它將如何工作的討論達(dá)成了一致绅喉,那么為了兼容面向未來的標(biāo)準(zhǔn)提前創(chuàng)建填補趴捅,被稱為“prollyfill(probably-fill —— 預(yù)填補)”。
真正的坑是某些標(biāo)準(zhǔn)行為不能被(完全)填補/預(yù)填補霹疫。
在開發(fā)者社區(qū)中有這樣一種爭論:對于常見的情況一個部分地填補是否是可接受的拱绑,或者如果一個填補不能100%地與語言規(guī)范兼容是否應(yīng)當(dāng)避免它。
許多開發(fā)者至少會接受一些常見的部分填補(例如Object.create(..)
)丽蝎,因為沒有被填補的部分是他們不管怎樣都不會用到的猎拨。
一些開發(fā)者相信,包圍著 polyfill/shim 的if
守護(hù)語句應(yīng)當(dāng)引入某種形式的一致性測試屠阻,在既存的方法缺失或者測試失敗時取代它红省。這額外的一層兼容性測試有時被用于將“shim”(兼容性測試)與“polyfill”(存在性測試)區(qū)別開。
這里的要點是国觉,沒有絕對 正確 的答案吧恃。即使是在老版本環(huán)境中“安全地”擴(kuò)展原生類型,也不是100%安全的麻诀。在其他人代碼存在的情況下依賴于(可能被擴(kuò)展過的)原生類型也是一樣痕寓。
在這兩種情況下都應(yīng)當(dāng)小心地使用防御性的代碼,并在文檔中大量記錄它的風(fēng)險蝇闭。
<script>
大多數(shù)通過瀏覽器使用的網(wǎng)站/應(yīng)用程序都將它們的代碼包含在一個以上的文件中呻率,在一個頁面中含有幾個或好幾個分別加載這些文件的<script src=..></script>
元素,甚至幾個內(nèi)聯(lián)的<script> .. </script>
元素也很常見呻引。
但這些分離的文件/代碼段是組成分離的程序礼仗,還是綜合為一個JS程序?
(也許令人吃驚)現(xiàn)實是它們在極大程度上,但不是全部元践,像獨立的JS程序那樣動作韭脊。
它們所 共享 的一個東西是一個單獨的global
對象(在瀏覽器中是window
),這意味著多個文件可以將它們的代碼追加到這個共享的名稱空間中单旁,而且它們都是可以交互的沪羔。
所以,如果一個script
元素定義了一個全局函數(shù)foo()
慎恒,當(dāng)?shù)诙€script
運行時任内,它就可以訪問并調(diào)用foo()
撵渡,就好像它自己已經(jīng)定義過了這個函數(shù)一樣融柬。
但是全局變量作用域 提升(參見本系列的 作用域與閉包)不會跨越這些界線發(fā)生,所以下面的代碼將不能工作(因為foo()
的聲明還沒有被聲明過)趋距,無論它們是否是內(nèi)聯(lián)的<script> .. </script>
元素還是外部加載的<script src=..></script>
文件:
<script>foo();</script>
<script>
function foo() { .. }
</script>
但是這兩個都將 可以 工作:
<script>
foo();
function foo() { .. }
</script>
或者:
<script>
function foo() { .. }
</script>
<script>foo();</script>
另外粒氧,如果在一個script
元素(內(nèi)聯(lián)或者外部的)中發(fā)生了一個錯誤,一個分離的獨立的JS程序?qū)〔⑼V菇诟侨魏魏罄m(xù)的script
都將會(依然在共享的global
中)暢通無阻地運行外盯。
你可以在你的代碼中動態(tài)地創(chuàng)建script
元素,并將它們插入到頁面的DOM中翼雀,它們之中的代碼基本上將會像從一個分離的文件中普通地加載那樣運行:
var greeting = "Hello World";
var el = document.createElement( "script" );
el.text = "function foo(){ alert( greeting );\
} setTimeout( foo, 1000 );";
document.body.appendChild( el );
注意: 當(dāng)然饱苟,如果你試一下上面的代碼段并將el.src
設(shè)置為某些文件的URL,而非將el.text
設(shè)置為代碼內(nèi)容狼渊,你就會動態(tài)地創(chuàng)建一個外部加載的<script src=..></script>
元素箱熬。
內(nèi)聯(lián)代碼塊中的代碼,與在外部文件中的相同的代碼之間的一個不同之處是狈邑,在內(nèi)聯(lián)的代碼塊中城须,字符</script>
的序列不能一起出現(xiàn),因為(無論它在哪里出現(xiàn))它將會被翻譯為代碼塊的末尾米苹。所以糕伐,小心這樣的代碼:
<script>
var code = "<script>alert( 'Hello World' )</script>";
</script>
它看起來無害,但是在string
字面量中出現(xiàn)的</script>
將會不正常地終結(jié)script塊蘸嘶,造成一個錯誤良瞧。繞過它最常見的一個方法是:
"</sc" + "ript>";
另外要小心的是,一個外部文件中的代碼將會根據(jù)和文件一起被提供(或默認(rèn)的)的字符集編碼(UTF-8训唱、ISO-8859-8等等)來翻譯莺褒,但在內(nèi)聯(lián)在你HTML頁面中的一個script
元素中的相同代碼將會根據(jù)這個頁面的(或它默認(rèn)的)字符集編碼來翻譯。
警告: charset
屬性在內(nèi)聯(lián)script元素中不能工作雪情。
關(guān)于內(nèi)聯(lián)script
元素遵岩,另一個被廢棄的做法是在內(nèi)聯(lián)代碼的周圍引入HTML風(fēng)格或X(HT)ML風(fēng)格的注釋,就像:
<script>
<!--
alert( "Hello" );
//-->
</script>
<script>
<!--//--><![CDATA[//><!--
alert( "World" );
//--><!]]>
</script>
這兩種東西現(xiàn)在完全是不必要的了,所以如果你還在這么做尘执,停下舍哄!
注意: 實際上純粹是因為這種老技術(shù),JavaScript才把``(HTML風(fēng)格的注釋)兩者都被規(guī)定為合法的單行注釋分隔符(var x = 2; another valid line comment
)誊锭。永遠(yuǎn)不要使用它們表悬。
保留字
ES5語言規(guī)范在第7.6.1部分中定義了一套“保留字”,它們不能被用作獨立的變量名丧靡。技術(shù)上講蟆沫,有四個類別:“關(guān)鍵字”,“未來保留字”温治,null
字面量饭庞,以及true
/false
布爾字面量。
像function
和switch
這樣的關(guān)鍵字是顯而易見的熬荆。像enum
之類的未來保留字舟山,雖然它們中的許多(class
、extends
等等)現(xiàn)在都已經(jīng)實際被ES6使用了卤恳;但還有另外一些像interface
之類的僅在strict模式下的保留字累盗。
StackOverflow用戶“art4theSould”創(chuàng)造性地將這些保留字編成了一首有趣的小詩(http://stackoverflow.com/questions/26255/reserved-keywords-in-javascript/12114140#12114140):
Let this long package float,
Goto private class if short.
While protected with debugger case,
Continue volatile interface.
Instanceof super synchronized throw,
Extends final export throws.Try import double enum?
- False, boolean, abstract function,
Implements typeof transient break!
Void static, default do,
Switch int native new.
Else, delete null public var
In return for const, true, char
…Finally catch byte.
注意: 這首詩包含ES3中的保留字(byte
、long
等等)突琳,它們在ES5中不再被保留了若债。
在ES5以前,這些保留字也不能被用于對象字面量中的屬性名或鍵拆融,但這種限制已經(jīng)不復(fù)存在了蠢琳。
所以,這是不允許的:
var import = "42";
但這是允許的:
var obj = { import: "42" };
console.log( obj.import );
你應(yīng)當(dāng)小心冠息,有些老版本的瀏覽器(主要是老IE)沒有完全地遵循這些規(guī)則挪凑,所以有些將保留字用作對象屬性名的地方任然會造成問題。小心地測試所有你支持的瀏覽器環(huán)境逛艰。
實現(xiàn)的限制
JavaScript語言規(guī)范沒有在諸如函數(shù)參數(shù)值的個數(shù)躏碳,或者字符串字面量的長度上做出隨意的限制,但是由于不同引擎的實現(xiàn)細(xì)節(jié)散怖,無論如何這些限制是存在的菇绵。
例如:
function addAll() {
var sum = 0;
for (var i=0; i < arguments.length; i++) {
sum += arguments[i];
}
return sum;
}
var nums = [];
for (var i=1; i < 100000; i++) {
nums.push(i);
}
addAll( 2, 4, 6 ); // 12
addAll.apply( null, nums ); // 應(yīng)該是:499950000
在某些JS引擎中,你將會得到正確答案499950000
镇眷,但在另一些引擎中(比如Safari 6.x)咬最,你會得到一個錯誤:“RangeError: Maximum call stack size exceeded.”
已知存在的其他限制的例子:
- 在字符串字面量(不是一個字符串變量)中允許出現(xiàn)的最大字符個數(shù)
- 在一個函數(shù)調(diào)用的參數(shù)值中可以發(fā)送的數(shù)據(jù)的大小(字節(jié)數(shù)欠动,也稱為棧的大杏牢凇)
- 在一個函數(shù)聲明中的參數(shù)數(shù)量
- 沒有經(jīng)過優(yōu)化的調(diào)用棧最大深度(比如惑申,使用遞歸時):從一個函數(shù)到另一個函數(shù)的調(diào)用鏈能有多長
- JS程序可以持續(xù)運行并阻塞瀏覽器的秒數(shù)
- 變量名的最大長度
- ...
遭遇這些限制不是非常常見,但你應(yīng)當(dāng)知道這些限制存在并確實會發(fā)生翅雏,而且重要的是它們因引擎不同而不同圈驼。
復(fù)習(xí)
我們知道并且可以依賴于這樣的事實:JS語言本身擁有一個標(biāo)準(zhǔn),而且這個標(biāo)準(zhǔn)可預(yù)見地被所有現(xiàn)代瀏覽器/引擎實現(xiàn)了望几。這是非常好的一件事绩脆!
但是JavaScript幾乎不會與世隔絕地運行。它會運行在混合了第三方庫的環(huán)境中運行橄抹,而且有時甚至?xí)诓煌瑸g覽器中不同的引擎/環(huán)境中運行靴迫。
對這些問題多加注意,會改進(jìn)你代碼的可靠性和健壯性楼誓。