JavaScript 作為一門靈活的編程語言,有著許多令人困惑的特性和行為。即使是經驗豐富的開發者,有時也會掉入這些"陷阱"中,分享一些我遇到的也踩過的坑。
1. 類型轉換的迷惑
JavaScript 的類型轉換規則可能會讓人摸不著頭腦:
console.log([] + []); // 輸出:""
console.log([] + {}); // 輸出:"[object Object]"
console.log({} + []); // 輸出:0(在某些瀏覽器中)
console.log([] == ![]); // 輸出:true
這些看似不合理的結果,其實都遵循著 JavaScript 的類型轉換規則。當進行加法運算時,JavaScript 會優先將操作數轉換為原始類型,然后進行運算。
2. 變量提升的陷阱
console.log(a); // 輸出:undefined
var a = 1;
console.log(b); // 報錯:ReferenceError
let b = 2;
變量提升是 JavaScript 中一個經典的概念。使用 var 聲明的變量會被提升到作用域頂部,但初始化不會提升。而 let 和 const 聲明的變量存在暫時性死區(TDZ),在聲明前訪問會拋出錯誤。
3. this 指向問題
const obj = {
name: '小明',
sayHi() {
setTimeout(function() {
console.log('你好,' + this.name);
}, 100);
}
};
obj.sayHi(); // 輸出:你好,undefined
在這個例子中,setTimeout 中的回調函數里的 this 指向全局對象(非嚴格模式下)或 undefined(嚴格模式下),而不是 obj。解決方案包括:
// 方案1:使用箭頭函數
setTimeout(() => {
console.log('你好,' + this.name);
}, 100);
// 方案2:使用 bind
setTimeout(function() {
console.log('你好,' + this.name);
}.bind(this), 100);
4. 閉包陷阱
for (var i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i);
}, 100);
}
// 輸出:3, 3, 3
這是一個經典的閉包問題。使用 var 聲明的變量 i 是函數作用域的,所有的 setTimeout 回調都共享同一個 i。解決方案:
5. 數值計算精度問題
console.log(0.1 + 0.2); // 輸出:0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // 輸出:false
這是因為 JavaScript 使用 IEEE 754 雙精度浮點數來表示數字,某些小數無法被精確表示。解決方案:
6. 數組方法的陷阱
解決方案:
7. Promise 的常見陷阱
正確的做法:
8. 事件監聽器的內存泄漏
// 錯誤示例:可能造成內存泄漏
function addHandler() {
const element = document.getElementById('button');
element.addEventListener('click', () => {
console.log('Clicked');
});
}
// 正確示例:
function addHandler() {
const element = document.getElementById('button');
const handler = () => {
console.log('Clicked');
};
element.addEventListener('click', handler);
// 清理函數
return () => {
element.removeEventListener('click', handler);
};
}
該文章在 2025/1/16 12:22:18 編輯過