如何避免 JavaScript 開發者常犯的 9 個錯誤?

2020-10-08 程式設計師秦心

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// ORif (name === "nodejs") { console.log(name);}// no output

這兩個比較操作符的區別是:兩個等號執行寬鬆的比較,三個等號執行嚴格的比較。

大致比較時,只比較值。但嚴格地說,值和數據類型都是要比較的。

下面的代碼更好地解釋了這一點:

const number = "1";console.log(number == 1);// trueconsole.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();// JavaScriptobj.printNameIn2Secs();// undefined

第一個結果是 JavaScript ,因為 this.name 正確地指向對象的 name 屬性。第二個結果是 undefined ,因為 this 未指代對象的屬性(包括 name)。

原因在於 this 依賴於正在調用該函數的對象。每個函數都有一個 this 變量,但是它的指向由調用 this 的對象決定。

bj.printName()this 直接指向 objobj.printNameIn2Secsthis 直接指向 obj 。然而,但是 this 在回調函數 setTimeout 中沒有指向任何對象,因為沒有任何對象調用它。

如果一個對象調用 setTimeout ,則執行 obj.setTimeout... 。因為沒有對象調用這個函數,所以使用默認對象(即 window )。

window 上沒有 name ,故返回 undefined

setTimeout 中保留 this 指代的最好方法是使用 bindcallapply 或箭頭功能(在 ES6 中引入)。不同於常規函數,箭頭函數不創建自己的 this

所以,下面的代碼會保留 this 指代:

const obj = { name: "JavaScript", printName: function () { console.log(this.name); }, printNameIn2Secs: function () { setTimeout(() => { console.log(this.name); }, 2000); },};obj.printName();// JavaScriptobj.printNameIn2Secs();// JavaScript

忽視對象的可變性

JavaScript 對象中的引用數據類型不像字符串、數字等原始數據類型。比如,在鍵值對對象中:

const obj1 = { name: "JavaScript",};const obj2 = obj1;obj2.name = "programming";console.log(obj1.name);// programming

obj1obj2 在內存中指向相同的地址。

在數組中:

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

由於 aundefinedb 也為 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 = falseif(isRaining) { console.log('It is raining')} else { console.log('It is not raining')}// It is not raining

上面的示例中是一種常見的檢查 Boolean 值的方法,但是在測試某些值時還是出現了錯誤。

在 JavaScript 中,比較 0false 會返回 true ,比較 1true 會返回 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);// 3020console.log(num1 + num3);// 60console.log(word1 + word2);// JavaScript

將字符串和數字相加時,JavaScript 會把數字轉換成字符串。而數字相加時,則進行數學運算。

總結

除了上面羅列出的,肯定還有更多錯誤(小錯誤或大錯誤)。所以,你需要知道最新的語言發展動態。

學習和避免這些錯誤將有助於你構建更好、更可靠的 Web 應用程式和工具。

原文連結:https://www.freecodecamp.org/news/nine-most-common-mistakes-developers-make-in-javascript/

作者: Dipto Karmakar

相關焦點

  • 開發者最容易犯的13個JavaScript錯誤
    開發者最容易犯的JavaScript錯誤,總結出13個。這些當中可能少不了你犯的錯誤^_^。我們描述了這些陋習,並列出來解決辦法,希望對開發者有幫助。 1.for..首先,開發者嘗試創建一個包含10項的數組,這將創建10個空槽的陣列。然而,如果你試圖得到一數組項,你將得到」未定義「的結果。換句話說,效果就像你沒有保存內存空間。沒有真正的好原因來預定義數組長度。
  • Oracle認證:PHP開發者常犯的MySQL錯誤
    【IT168技術】為了方便廣大考生更好的複習,綜合整理提供了Oracle認證:PHP開發者常犯的MySQL錯誤,以供各位考生考試複習參考,希望對考生複習有所幫助。  PHP開發者常犯的10個MySQL錯誤  資料庫是WEB大多數應用開發的基礎。
  • 10個JavaScript最常出現的錯誤
    為了便於閱讀,每個錯誤都被縮短了,讓我們更深入地研究每一個問題,以確定是什麼導致了這些問題,以及如何避免產生這些問題讓我們來看一個在現實應用中如何發生這種情況的示例。我們將選擇React,但是不正確初始化的相同原理也適用於Angular,Vue或任何其他框架。
  • 企業在IT風險評估中五個常犯錯誤匯總
    本文我們將討論企業在風險評估過程中最常犯的五個錯誤。  Protiviti公司董事總經理Scott Laliberte表示,不幸的是,現在很多企業根本沒有進行風險評估或者他們錯將漏洞評估或滲透測試當做是風險評估。並且,在那些執行風險評估的企業中,很多企業沒有根據威脅環境或業務模式的改變而改進風險評估。他表示,在這種情況下風險評估將會變得過時,而錯過關鍵問題。
  • 開源軟體公司易犯的 5 大錯誤,又該如何避免?
    來自Thevarguy的 Christopher Tozzi撰文總結了開源軟體公司常犯的5個錯誤,並給出了要避免這樣的錯誤的建議。但是,今天開源已經是一種常態,有太多的開源公司在這15年此消彼長的發展著,我們回顧過去,是什麼讓開源軟體公司在健康成長,那些常犯的錯誤是否能夠讓後來者吸取教訓,避免重蹈覆轍。
  • 來自1000多個項目的十大JavaScript錯誤(以及如何避免)
    為了回饋我們的開發人員社區,我們查看了包含數千個項目的資料庫,並發現了JavaScript中的前10大錯誤。我將向你展示導致它們的原因以及如何防止它們發生。如果你避免這些「陷阱」,它將使你成為更好的開發人員。
  • 新手學攝影常犯的5個錯誤,你犯過嗎?這樣避免幫你拍照更美
    其實當中有一些技巧和經驗是非常重要的,本文詳細介紹 5 個旅行攝影常犯的錯誤,助你拍出好看的照片。1、尚未準備好研究好一個景點主要原因是能夠準確地決定你能拍什麼好看的主題。有很多攝影愛好者出現在一個場景,就花了幾分鐘拍照,然後就轉移到下一個。
  • 基金投資者常犯的十個投資錯誤
    其中,盈利客戶雖然佔比近9成,但半數左右客戶僅小賺,收益不足10%。 導致基金賺錢,基民不賺錢的因素有很多,其中更多的因素在於基民身上,很多基金投資者在基金投資過程中犯了很多常識性的錯誤。
  • 新手學攝影常犯的6點錯誤,你有犯過嗎?這樣避免助你拍照更美
    剛剛接觸攝影時,大多數攝影新手都會犯一些同樣的錯誤。在他們對攝影技術的理解和運用達到爐火純青的地步之前,總會有一些照片因下面這些錯誤而破壞了照片的美感。通常情況下,在進行創新之前,我們應該熟練掌握一定的攝影技術。本文詳細介紹6種常犯錯的攝影錯誤,助你拍出更加美麗的照片。
  • 第一次裝修常犯的8個錯誤,又醜又浪費錢,別糟蹋了新房
    對於初次裝修的業主來說,房子裝修是個大工程,需要做的功課也是非常多的,並不是隨隨便便裝修一下就行,等入住後就知道有多後悔了,今天就給大家分享一套花了20萬裝修的新房,這8個細節錯誤,第一次裝修的朋友經常犯,真是又醜又浪費,不信?
  • 買油時,多數人常犯3個錯誤!滿足4個特徵,就是優質好油,放心買
    買油時,多數人常犯3個錯誤!滿足4個特徵,就是優質好油,放心買大家好,感謝閱讀我分享的文章,我要和大家說的是:『買油時,多數人常犯3個錯誤!滿足4個特徵,就是優質好油,放心買!』有人買便宜的,有人買貴的,下面我們就來聊聊如何購買食用油。首先,買油時很多人經常會犯3個錯誤,導致了吃的油並不好,快和我一起看看吧。①一直吃同一種油很多人認為花生油味道香,營養高,就一直買花生油;有人覺得大豆油既營養又便宜,就每次都買大豆油,這是錯誤的。不同的油,營養價值也不同,換著吃才營養均衡,更健康。
  • 不犯就出好片 數旅遊攝影常犯的十個錯誤
    這裡寫出十點最容易在旅遊攝影中犯的錯誤,給大家參考一下。不犯就出好片 數旅遊攝影常犯的十個錯誤出發前不做功課  旅遊總是要有計劃的,如果說是跟團旅遊的話,那麼可以少操點心,自由行的話,不做功課的話,會在旅遊過程中犯困,輕則浪費時間
  • 瑜伽前屈摺疊常犯的4個錯誤!
    接下來我們來看看前屈摺疊常犯的4個錯誤,以及我們可以怎麼做去避免受傷,感覺到放鬆沒有疼痛,在練習中以及練習後。 比如站立前屈就是個很好的例子,以為在做這個體式時如果沒有伸直腿就做錯了。然而,那些髂腰肌或大腿後側緊的,稍微彎曲膝蓋,就可以延長脊柱。在下犬式中也是一樣。所以需要時彎曲膝蓋沒有什麼錯。
  • 這5張插畫告訴人們,使用智慧型手機常犯的錯誤,大家儘量避免
    這5張插畫告訴人們,使用智慧型手機常犯的錯誤,大家儘量避免 文/文涓隨著時代的發展,大家使用智慧型手機的頻率越來越高,一方面是因為智慧型手機的功能非常強大,另一方面是因為人們的交往越來越頻繁。不過在使用手機的過程當中,大家也會有意無意的犯下很多錯誤,嚴重影響了個人的健康,也是一件非常遺憾的事情。
  • 如何找YouTuber?Influencer Marketing最常犯的六個錯誤
    以下為大家介紹Influencer Marketing最常犯的六個錯誤。 錯誤一:沒有制定策略的營銷活動 最常見的錯誤就是執行了一個YouTube的Influencer Marketing營銷計劃,重金禮聘邀請具有影響力的YouTuber,但想要Youtuber帶來的實際影響力卻非常模糊,例如希望通過這個影片可以觸及到10萬的用戶、3萬個贊數
  • Python程式設計師最常犯的10個錯誤,你中招了嗎?
    鑑於此,本文列出了Python開發人員常犯的10個小錯誤,資深程序猿也難免會中招哦。,這也是Python的一大特色,但當默認值可變時,可能會給開發者帶來一些困擾。雖然上面代碼的錯誤很明顯,但是在編寫複雜代碼時,資深程式設計師也難免會犯此類錯誤。幸好Python集成了大量經典的編程範式,如果運用得當,可以大大簡化代碼並提高編程效率。簡單的代碼會降低出現上述bug的機率。
  • 新手最常犯的30個裝修錯誤給你拎出來,繞道走!
    新手最常犯的30個裝修錯誤給你拎出來,記得繞道走!新房裝修,是一件勞心勞神、費錢費時的差事。裝修中的細枝末節實在是太多,各個細節都需要我們做決定。但很多業主都是裝修新手,一不小心就拿錯主意,犯下了不少裝修錯誤。今天,小編就給大家理理裝修新手常犯的30個錯誤,希望準備裝修的小夥伴們能多看看,牢記於心,繞開這些大坑。
  • ESSAY寫作中常犯詞彙錯誤的示範
    1.用 「SO」 來替換 「VERY」由於我們在口語中很常可以用 「so」 來替換 「very」,這是一個在英語論文寫作中很容易犯的錯誤。例如:I was so tired與 I was very tired在口語表達有相同的意思。但在英語論文寫作中,「so」和 「very」 並不是互換的。
  • Java開發最常犯的10個錯誤,打死都不要犯!
    開發人員最常犯的錯誤。推薦:HashMap 和 Hashtable 的 6 個區別!5、使用集合原始類型(raw type)在Java中,原始類型(raw type)和無界通配符類型很容易讓人混淆。舉個Set的例子,Set是原始類型,而Set是無界通配符類型。
  • 7個讓很多人在生活中都會常犯的錯誤的衛生習慣
    身體健康是每個人都希望擁有的,但是你可能並不知道,往往影響身體健康的因素有很多,有一些是和我們生活中常見的衛生習慣有關,這些習慣常常看似微不足道,很多人並沒有重視過,可是它卻是影響健康的「隱形殺手」,例如,沒有定期更換牙刷,或者喜歡開著馬桶蓋衝馬桶,這些習慣相信許多人都會經常做的,那麼今天小編來聊聊,7個讓很多人在生活中會常犯的錯誤的衛生習慣