11 種糟糕的 JavaScript 編寫方式
JavaScript 是現(xiàn)代 Web 開發(fā)不可或缺的一部分,但它有一些需要注意的陷阱。在本文中,我們將介紹10種常見的 JavaScript 編寫方式,以幫助您避免一些常見的錯誤和陷阱。
1.隱式類型轉(zhuǎn)換
隱式類型轉(zhuǎn)換是指 JavaScript 在運行時自動將一種數(shù)據(jù)類型轉(zhuǎn)換為另一種數(shù)據(jù)類型。
例如,當您使用加號連接兩個字符串時,JavaScript 會自動將它們轉(zhuǎn)換為字符串并連接它們。雖然這看起來很方便,但它經(jīng)常會導(dǎo)致問題,尤其是對于沒有經(jīng)驗的開發(fā)人員而言。
let result = "3" + 4 + 5 ; // '345'
let result2 = 3 + 4 + "5" ; // '75'
上面代碼中,第一行的輸出結(jié)果是字符串‘345’,第二行的輸出結(jié)果是字符串‘75’。這是因為 JavaScript 在處理加號時會將數(shù)字轉(zhuǎn)換為字符串,然后進行拼接操作。因此,當您使用加號連接數(shù)字和字符串時,必須小心避免意外行為。
2.使用var聲明變量
在 ES6 之前,在 JavaScript 中聲明變量的唯一方法是使用 var 關(guān)鍵字。但是var存在一些作用域問題,容易造成變量污染等問題。因此,最好使用 let 或 const 來聲明變量,它們是塊級作用域。
function example () {
var foo = "bar" ;
if ( true ) {
var foo = "baz" ;
console . log (foo); // 'baz'
}
console . log (foo); // 'baz'
}
function example2 () {
let foo = "bar" ;
if ( true ) {
let foo = "baz" ;
console . log (foo);// 'baz'
}
console . log (foo); // 'bar'
}
example ();
example2 ();
在上面的代碼中,當?shù)谝粋€示例 var 聲明變量時,內(nèi)部的 foo 覆蓋了外部的 foo,導(dǎo)致兩者的輸出都是字符串 'baz'。
在第二個例子 let 中,聲明變量后,內(nèi)部的 foo 只存在于內(nèi)部作用域中,不會影響外部的 foo。
再比如下面這個面試中經(jīng)常遇到的問題:
for ( var i = 0 ; i < 5 ; i ++ ) {
setTimeout ( function () {
console . log (i);
}, 1000 );
}
// output 5, 5, 5, 5, 5
這是因為setTimeout的回調(diào)函數(shù)是異步執(zhí)行的,執(zhí)行的時候i的值已經(jīng)變成5了,可以用let或者closure來解決這個問題。
3、DOM操作頻繁
直接操作 DOM 是非常低效的,因為這樣做會導(dǎo)致頁面重復(fù)重新渲染,從而降低性能和用戶體驗。
相反,應(yīng)該使用更高效的方式來操作 DOM,比如減少 DOM 查詢次數(shù),使用虛擬 DOM 技術(shù)等。
// not recommended
document.getElementById('my-button').addEventListener('click', function() {
document.getElementById('my-text').innerText = 'Hello, world!';
});
// recommended
const myText = document.getElementById('my-text');
document.getElementById('my-button').addEventListener('click', function() {
myText.innerText = 'Hello, world!';
});
在上面的代碼中,第一個示例在每次單擊按鈕時重新查詢 DOM 并更新它,而第二個示例僅查詢一次 DOM,然后更新元素的文本內(nèi)容。
4.修改原型對象
修改原型對象可能會導(dǎo)致一些意想不到的問題。例如:
Array.prototype.sum = function() {
return this.reduce(function(a, b) {
return a + b;
}, 0);
};
var arr = [1, 2, 3];
console.log(arr.sum()); // 6
雖然上面的代碼工作正常,但它可能會對其他部分產(chǎn)生意想不到的影響。為了避免這個問題,我們應(yīng)該盡量避免修改原型對象。
5.使用全局變量
全局變量是 JavaScript 中常見的一種變量類型,但是它們會引起命名沖突等問題。因此,最好避免使用全局變量并以模塊化方式組織代碼。
// not recommended
function foo() {
globalVar = "bar";
}
// recommended
const myModule = (function() {
const localVar = "baz";
function foo() {
// do something with localVar
}
return {
foo: foo
};
})();
在上面的代碼中,第一個例子使用了一個全局變量globalVar,它可能會被其他代碼覆蓋。
第二個例子使用IIFE(Immediately Invoked Function Expressions)創(chuàng)建一個模塊,其中變量只在模塊內(nèi)部可見,不會影響其他代碼。
6.忽略分號
JavaScript 允許在語句末尾省略分號,但這往往會導(dǎo)致一些錯誤,尤其是在代碼被縮小或合并時。因此,建議始終以分號結(jié)束語句以避免潛在問題。
// not recommended
let x = 1
let y = 2
// recommended
let x = 1 ;
let y = 2 ;
在上面的代碼中,第一個示例省略了分號,這可能會導(dǎo)致一些意外行為。第二個例子顯式添加了分號,更加規(guī)范。
7. 使用 for-in 循環(huán)
for-in 循環(huán)是一種遍歷對象屬性的方法,但它有一些缺點。因為它不僅會遍歷對象本身的屬性,還會遍歷從原型鏈繼承的屬性,這可能會導(dǎo)致一些意想不到的結(jié)果。
const person = {
name : "Alice" ,
age : 30 ,
gender : "female"
};
// not recommended
for ( const key in person) {
console . log (person[key]);
}
// recommended
Object . keys (person). forEach (key => console . log (person[key]));
上面代碼中,第一個例子使用for-in循環(huán)遍歷對象屬性,而第二個例子使用Object.keys()方法獲取對象自身的屬性名,并配合forEach()方法來獲取 遍歷。
8.比較NaN
NaN 是一種特殊類型的數(shù)值,表示Not-a-Number(不是數(shù)字)。但是,由于 NaN 不等于任何值,所以在比較 isNaN() 時需要用一個函數(shù)來判斷。
// not recommended
if (x == NaN) {
// do something
}
// recommended
if (isNaN(x)) {
// do something
}
上面代碼中,第一個例子錯誤地使用了相等運算符來比較NaN,而第二個例子使用了isNaN()函數(shù)來進行判斷。
9.使用eval()
eval() 是一種執(zhí)行字符串代碼的方法,但它容易引起安全問題和性能問題。因此,最好避免在生產(chǎn)環(huán)境中使用 eval()。
// not recommended
eval("console.log('Hello, world!')");
// recommended
const code = "console.log('Hello, world!')";
Function(code)();
在上面的代碼中,第一個示例使用 eval() 函數(shù)來執(zhí)行字符串代碼,而第二個示例使用 Function() 函數(shù)來創(chuàng)建一個新函數(shù)并執(zhí)行它。
10.忽略錯誤處理
忽略錯誤處理是一個常見的錯誤,因為它會導(dǎo)致代碼崩潰。例如:
try {
// some code
} catch (e) {}
雖然拋出異常的這段代碼可能無關(guān)緊要,但如果異常處理不當,它可能會在開發(fā)或運行時導(dǎo)致問題。
為了避免這個問題,我們應(yīng)該始終正確處理異常,例如通過記錄或顯示用戶友好的錯誤消息。
11. 傳遞函數(shù)參數(shù)時忘記綁定this
將函數(shù)作為參數(shù)傳遞時,需要注意綁定this的值。例如:
const obj = {
name : 'Alice' ,
greet : function () {
console . log ( 'Hello, ' + this .name);
}
};
setTimeout (obj. greet, 1000 ); // Hello, undefined
正確的寫法應(yīng)該是使用bind方法:
const obj = {
name : 'Alice' ,
greet : function () {
console . log ( 'Hello, ' + this .name);
}
};
setTimeout (obj. greet . bind (obj), 1000 ); // Hello , Alice
結(jié)語
JavaScript 是現(xiàn)代 Web 開發(fā)不可或缺的一部分,它為我們提供了豐富的功能和靈活性,在編寫 JavaScript 代碼時,我們應(yīng)該盡量保持代碼的可讀性、簡潔性和可維護性,以便構(gòu)建出色的 Web 應(yīng)用程序。