JavaScript 是一種給網頁添加功能和交互的腳本語言,對於使用不同程式語言的初學者來說很容易理解。有了一些教程,你就可以馬上開始使用它了。
但很多初學者都會犯一些常見的錯誤。在這篇文章中,我們將介紹 9 個常見的錯誤(或者說不好的實踐)以及它們的解決方案,幫助你成為更好的 JavaScript 開發者。
將賦值操作符(=)和相等操作符(==,===)混為一談正如名稱所示,賦值操作符是用來給變量賦值的。開發者常常把它與相等操作符混淆。
舉個例子:
const name = "javascript";
if ((name = "nodejs")) {
console.log(name);
}
// output - nodejs本例中,不是比較 name 變量和 nodejs 字符串,而是為 name 賦值 nodejs,並將 nodejs 輸出到控制臺。
在 JavaScript 中,兩個等號(==)和三個等號(===)是比較操作符。
對於上述代碼,可以使用以下方法比較值:
const name = "javascript";
if (name == "nodejs") {
console.log(name);
}
// no output
// OR
if (name === "nodejs") {
console.log(name);
}
// no output這兩個比較操作符的區別是:兩個等號執行寬鬆的比較,三個等號執行嚴格的比較。
大致比較時,只比較值。但嚴格地說,值和數據類型都是要比較的。
下面的代碼更好地解釋了這一點:
const number = "1";
console.log(number == 1);
// true
console.log(number === 1);
// false給變量 number 賦值 1。如果將 number 用雙等號與 1 進行比較,會返回 true,因為兩個值都是 1。
然而,在用三個等號的情況下,因為每個值的數據類型不同,所以返回 false。
預期的回調是同步的在 JavaScript 裡,用回調方法處理異步操作。然而,Promises 和 async/await 是處理異步操作的首選方法,因為多次回調會導致回調地獄。
回調是不同步的。在延遲執行完成操作之後,它們作為一個函數被調用。
例如,全局 setTimeout 接收回調函數作為第一個參數,接收持續時間(毫秒)作為第二個參數:
function callback() {
console.log("I am the first");
}
setTimeout(callback, 300);
console.log("I am the last");
// output
// I am the last
// I am the first在 300ms 之後,調用回調函數。但是代碼的其餘部分在完成前運行,因此,最後一個 console.log 將首先運行。
開發者經常犯的一個錯誤就是誤解了回調是同步的,比如,認為回調函數一個值用於其他操作。
錯誤在於:
function addTwoNumbers() {
let firstNumber = 5;
let secondNumber;
setTimeout(function () {
secondNumber = 10;
}, 200);
console.log(firstNumber + secondNumber);
}
addTwoNumbers();
// NaN由於 secondNumber 不確定,所以輸出 NaN。運行 firstNumber+secondNumber 的時候,仍然沒有定義 secondNumber,因為 setTimeout 函數會在 200ms 之後執行回調。
最好的方法是在回調函數中執行剩餘的代碼:
function addTwoNumbers() {
let firstNumber = 5;
let secondNumber;
setTimeout(function () {
secondNumber = 10;
console.log(firstNumber + secondNumber);
}, 200);
}
addTwoNumbers();
// 15
this 指代錯誤在 JavaScript 中,this 是一個常被誤解的概念。在 JavaScript 使用 this,你需要理解它的作用是什麼,這裡的 this 跟其他語言中的 this 用法不同。
以下是關於 this 的常見錯誤的示例:
const obj = {
name: "JavaScript",
printName: function () {
console.log(this.name);
},
printNameIn2Secs: function () {
setTimeout(function () {
console.log(this.name);
}, 2000);
},
};
obj.printName();
// JavaScript
obj.printNameIn2Secs();
// undefined第一個結果是 JavaScript,因為 this.name 正確地指向對象的 name 屬性。第二個結果是 undefined,因為 this 未指代對象的屬性(包括 name)。
原因在於 this 依賴於正在調用該函數的對象。每個函數都有一個 this 變量,但是它的指向由調用 this 的對象決定。
bj.printName() 的 this直接指向 obj。obj.printNameIn2Secs 的 this 直接指向 obj。然而,但是 this 在回調函數 setTimeout 中沒有指向任何對象,因為沒有任何對象調用它。
如果一個對象調用 setTimeout,則執行obj.setTimeout...。因為沒有對象調用這個函數,所以使用默認對象(即 window)。
window 上沒有 name,故返回 undefined。
在 setTimeout 中保留 this 指代的最好方法是使用 bind、call、apply 或箭頭功能(在 ES6 中引入)。不同於常規函數,箭頭函數不創建自己的 this。
所以,下面的代碼會保留 this 指代:
const obj = {
name: "JavaScript",
printName: function () {
console.log(this.name);
},
printNameIn2Secs: function () {
setTimeout(() => {
console.log(this.name);
}, 2000);
},
};
obj.printName();
// JavaScript
obj.printNameIn2Secs();
// JavaScript
忽視對象的可變性JavaScript 對象中的引用數據類型不像字符串、數字等原始數據類型。比如,在鍵值對對象中:
const obj1 = {
name: "JavaScript",
};
const obj2 = obj1;
obj2.name = "programming";
console.log(obj1.name);
// programmingobj1 和 obj2 在內存中指向相同的地址。
在數組中:
const arr1 = [2, 3, 4];
const arr2 = arr1;
arr2[0] = "javascript";
console.log(arr1);
// ['javascript', 3, 4]開發者經常犯的一個錯誤是忽略了 JavaScript 的這個特性,而這將導致意外的錯誤。
如果出現這種情況,訪問原始屬性的任何嘗試都會返回 undefined 或者引發錯誤。
最好的方法是,當你想複製一個對象的時候,總是創建一個新的引用。為了達到這個目的,擴展運算符(在 ES6 中引入的...)就是一個完美的解決方案。
比如,在鍵值對對象中:
const obj1 = {
name: "JavaScript",
};
const obj2 = { ...obj1 };
console.log(obj2);
// {name: 'JavaScript' }
obj2.name = "programming";
console.log(obj.name);
// 'JavaScript'在數組中:
const arr1 = [2, 3, 4];
const arr2 = [...arr1];
console.log(arr2);
// [2,3,4]
arr2[0] = "javascript";
console.log(arr1);
// [2, 3, 4]
保存數組和對象至瀏覽器儲存使用 JavaScript 的時候,開發者可能希望利用 localStorage 來保存值。然而,一個常見的錯誤是直接將數組和對象保存在 localStorage 中。localStorage 只接收字符串。
JavaScript 將對象轉換成字符串以保來保存,其結果是對象保存為 [Object Object],數組保存為逗號分隔開的字符串。
比如:
const obj = { name: "JavaScript" };
window.localStorage.setItem("test-object", obj);
console.log(window.localStorage.getItem("test-object"));
// [Object Object]
const arr = ["JavaScript", "programming", 45];
window.localStorage.setItem("test-array", arr);
console.log(window.localStorage.getItem("test-array"));
// JavaScript, programming, 45在保存這些對象時,很難訪問它們。例如,對於一個對象,通過 .name 訪問它會導致錯誤。因為 [Object Object] 現在是一個字符串,而不包含 name 屬性。
通過使用 JSON.stringify(將對象轉換為字符串)和 JSON.parse(將字符串轉換為對象),可以更好地保存本地存儲對象和數組。通過這種方式可以輕鬆訪問對象。
上述代碼的正確版本為:
const obj = { name: "JavaScript" };
window.localStorage.setItem("test-object", JSON.stringify(obj));
const objInStorage = window.localStorage.getItem("test-object");
console.log(JSON.parse(objInStorage));
// {name: 'JavaScript'}
const arr = ["JavaScript", "programming", 45];
window.localStorage.setItem("test-array", JSON.stringify(arr));
const arrInStorage = window.localStorage.getItem("test-array");
console.log(JSON.parse(arrInStorage));
// JavaScript, programming, 45
不使用默認值為動態變量設置默認值是一個很好的預防意外錯誤的方法。這裡有一個常見錯誤的例子:
function addTwoNumbers(a, b) {
console.log(a + b);
}
addTwoNumbers();
// NaN由於 a 為 undefined,b 也為 undefined,因此結果為 NaN。你可以使用默認值防止類似錯誤,比如:
function addTwoNumbers(a, b) {
if (!a) a = 0;
if (!b) b = 0;
console.log(a + b);
}
addTwoNumbers();
// 0此外,可以在 ES6 中這樣使用默認值:
function addTwoNumbers(a = 0, b = 0) {
console.log(a + b);
}
addTwoNumbers();
// 0此示例雖小,但強調了默認值的重要性。
另外,如果沒有提供期望,開發者可以提供一個錯誤或者警告信息。
變量命名錯誤是的,開發者還是會犯這個錯誤。命名是困難的,但開發人員沒有其他選擇。註解和命名變量一樣,都是編程的好習慣。
比如:
function total(discount, p) {
return p * discount
}變量 discount 沒問題,但是 p 或者 total 呢?是什麼的 total?最好是:
function totalPrice(discount, price) {
return discount * price
}對變量進行適當的命名非常重要,因為在特定的時間和將來,可能有別的開發者使用這個代碼庫。
適當地命名變量會讓貢獻者很容易理解項目是如何運行的。
檢查布爾值const isRaining = false
if(isRaining) {
console.log('It is raining')
} else {
console.log('It is not raining')
}
// It is not raining上面的示例中是一種常見的檢查 Boolean 值的方法,但是在測試某些值時還是出現了錯誤。
在 JavaScript 中,比較 0 和 false 會返回 true,比較 1 和 true 會返回 true。這就是說,如果 isRaining 是 1,那麼它就是 true。
這常在對象中出現錯誤,比如:
const obj = {
name: 'JavaScript',
number: 0
}
if(obj.number) {
console.log('number property exists')
} else {
console.log('number property does not exist')
}
// number property does not exist儘管存在 number 屬性,但 obj.number 返回 0,這是一個假值,因此執行了 else 代碼。
所以,除非你確定了要使用的值的範圍,否則你應該測試布爾值和對象中的屬性:
if(a === false)...
if(object.hasOwnProperty(property))...
使人迷惑的添加和連接在 JavaScript 中,加號(+)有兩種功能:相加和連接。相加是針對數字,而連接是針對字符串。有些開發者經常誤用這個操作符。
比如:
const num1 = 30;
const num2 = "20";
const num3 = 30;
const word1 = "Java"
const word2 = "Script"
console.log(num1 + num2);
// 3020
console.log(num1 + num3);
// 60
console.log(word1 + word2);
// JavaScript將字符串和數字相加時,JavaScript 會把數字轉換成字符串。而數字相加時,則進行數學運算。
總結除了上面羅列出的,肯定還有更多錯誤(小錯誤或大錯誤)。所以,你需要知道最新的語言發展動態。
學習和避免這些錯誤將有助於你構建更好、更可靠的 Web 應用程式和工具。