今天在檢視頁面控制檯的時候,無意中看到了一個暴露到全域性的變數 i,全域性變數是不會被壓縮工具壓縮成簡寫的字母,這個被頻繁使用的變數名暴露到全域性也是個相當大的隱患,可能一個不小心就覆蓋了第二次暴露到全域性的同名變數。
剛開始我就懷疑是自己出現了這樣愚蠢的錯誤:
1 2 3 4 5 6 7 8 9 10 11 12 |
function A() { // 在一個函式中多次用到了 for 迴圈,為了節省變數,都是用了變數 i for(var i = 0; ...) { //... } for(i = 0; ...) { //... } for(i = 0; ...) { //... } } |
結果在某次拆分函式的時候,忘記定義:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function A(){ for(var i = 0; ...) { //... } for(i = 0; ...) { //... } } function B(){ for(i = 0; ...) { //... } } |
這個時候,變數 i 在 B 函式執行的時候就暴露到了全域性。抱著這樣的懷疑,我搜尋了 50 多個模組的程式碼,一無所獲…此時,我依然十分懷疑是自己的程式哪裡疏忽了,全域性搜尋 i = 和 i++ ,五分鐘過去了,未果…
找到這個變數
如果這個變數名叫做 fuckIE ,分分鐘全域性搜尋就出來了,類似這種簡短的常用的變數,著實讓人頭疼了好一會兒。後來想到了這個方案:
1 2 3 4 5 6 |
Object.defineProperty(window, "i", { get : function(){ return window.i; }, set : function(newValue){ debugger;window.i = newValue; }, enumerable : true, configurable : true }); |
在全域性定義變數 i 的時刻,打一個斷點,然後 F10 往前走一步,果然,在控制檯右側的 Call Stack 中找到了端倪!
這個變數是從第三方元件中(offline元件,使用相當頻繁的一個元件)暴露出來的,估計出錯的方式同我上面的描述差不多,拆分函式的時候忘記重新定義變數 i。
當然還有更快的方式:
1 |
window.__defineSetter__('i', function(){ debugger }) |
不挖坑才是最好的解決方案
1. 使用 use strict;
在嚴格模式下,這種問題暴露無遺,每個函式內都加上 use strict; ,雖然在語言上有所限制,但是低階錯誤一定不會出現,因為嚴格模式會給你報錯!
2. 使用 jslint/jshint 等 js 分析工具
這些東西除了配置上較為繁瑣,用起來還是很順手的,做過配置的錯誤都會直接在 IDE 上標紅顯示出來,很容易發現問題,但是不建議一個專案中途使用,因為程式碼習慣的問題,很多地方被 js 分析工具作為錯誤丟擲來,改動量是相當大的。
我有次也犯了個比較隱晦的錯誤:
1 2 3 4 5 6 |
$(window).on('click', function(evt){ var target = event.target.nodeName.toLowerCase(); if(target !== 'ul'){ //... } }); |
在 IE 和 Chrome 下,程式碼跑得好好的,但是到了測試較少的 FF 下,問題出來了, event is not defined. ,IE 和 Chrome 是支援 window.event 抓取當前事件物件的,而 FF 不支援,所以每次點選頁面上都會報錯。。。
諸如此類的問題,在我們的平時編碼之中不勝列舉,所以有一個編碼規範作為強約束是十分有必要的!