欧美一级特黄大片做受成人-亚洲成人一区二区电影-激情熟女一区二区三区-日韩专区欧美专区国产专区

JavaScript中BUG和錯(cuò)誤怎么辦

小編給大家分享一下JavaScript中BUG和錯(cuò)誤怎么辦,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

在根河等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場(chǎng)前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供網(wǎng)站設(shè)計(jì)、成都網(wǎng)站建設(shè) 網(wǎng)站設(shè)計(jì)制作定制網(wǎng)站設(shè)計(jì),公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),成都品牌網(wǎng)站建設(shè),營(yíng)銷型網(wǎng)站,外貿(mào)網(wǎng)站制作,根河網(wǎng)站建設(shè)費(fèi)用合理。

計(jì)算機(jī)程序中的缺陷通常稱為 bug。 它讓程序員覺(jué)得很好,將它們想象成小事,只是碰巧進(jìn)入我們的作品。 實(shí)際上,當(dāng)然,我們自己把它們放在了那里。

如果一個(gè)程序是思想的結(jié)晶,你可以粗略地將錯(cuò)誤分為因?yàn)樗枷牖靵y引起的錯(cuò)誤,以及思想轉(zhuǎn)換為代碼時(shí)引入的錯(cuò)誤。 前者通常比后者更難診斷和修復(fù)。

語(yǔ)言

計(jì)算機(jī)能夠自動(dòng)地向我們指出許多錯(cuò)誤,如果它足夠了解我們正在嘗試做什么。 但是這里 JavaScript 的寬松是一個(gè)障礙。 它的綁定和屬性概念很模糊,在實(shí)際運(yùn)行程序之前很少會(huì)發(fā)現(xiàn)拼寫(xiě)錯(cuò)誤。 即使這樣,它也允許你做一些不會(huì)報(bào)錯(cuò)的無(wú)意義的事情,比如計(jì)算true *'monkey'。

JavaScript 有一些報(bào)錯(cuò)的事情。 編寫(xiě)不符合語(yǔ)言語(yǔ)法的程序會(huì)立即使計(jì)算機(jī)報(bào)錯(cuò)。 其他的東西,比如調(diào)用不是函數(shù)的東西,或者在未定義的值上查找屬性,會(huì)導(dǎo)致在程序嘗試執(zhí)行操作時(shí)報(bào)告錯(cuò)誤。

不過(guò),JavaScript 在處理無(wú)意義的計(jì)算時(shí),會(huì)僅僅返回NaN(表示不是數(shù)字)或undefined這樣的結(jié)果。程序會(huì)認(rèn)為其執(zhí)行的代碼毫無(wú)問(wèn)題并順利運(yùn)行下去,要等到隨后的運(yùn)行過(guò)程中才會(huì)出現(xiàn)問(wèn)題,而此時(shí)已經(jīng)有許多函數(shù)使用了這個(gè)無(wú)意義的值。程序執(zhí)行中也可能不會(huì)遇到任何錯(cuò)誤,只會(huì)產(chǎn)生錯(cuò)誤的程序輸出。找出這類錯(cuò)誤的源頭是非常困難的。

我們將查找程序中的錯(cuò)誤或者 bug 的過(guò)程稱為調(diào)試(debug)。

嚴(yán)格模式

當(dāng)啟用了嚴(yán)格模式(strict mode)后,JavaScript 就會(huì)在執(zhí)行代碼時(shí)變得更為嚴(yán)格。我們只需在文件或函數(shù)體頂部放置字符串"use strict"就可以啟用嚴(yán)格模式了。下面是示例代碼:

function canYouSpotTheProblem() {
 "use strict";
 for (counter = 0; counter < 10; counter++) {
 console.log("Happy happy");
 }
}

canYouSpotTheProblem();
// → ReferenceError: counter is not defined

通常,當(dāng)你忘記在綁定前面放置let時(shí),就像在示例中的counter一樣,JavaScript 靜靜地創(chuàng)建一個(gè)全局綁定并使用它。 在嚴(yán)格模式下,它會(huì)報(bào)告錯(cuò)誤。 這非常有幫助。 但是,應(yīng)該指出的是,當(dāng)綁定已經(jīng)作為全局綁定存在時(shí),這是行不通的。 在這種情況下,循環(huán)仍然會(huì)悄悄地覆蓋綁定的值。

嚴(yán)格模式中的另一個(gè)變化是,在未被作為方法而調(diào)用的函數(shù)中,this綁定持有值undefined。 當(dāng)在嚴(yán)格模式之外進(jìn)行這樣的調(diào)用時(shí),this引用全局作用域?qū)ο螅搶?duì)象的屬性是全局綁定。 因此,如果你在嚴(yán)格模式下不小心錯(cuò)誤地調(diào)用方法或構(gòu)造器,JavaScript 會(huì)在嘗試從this讀取某些內(nèi)容時(shí)產(chǎn)生錯(cuò)誤,而不是愉快地寫(xiě)入全局作用域。

例如,考慮下面的代碼,該代碼不帶new關(guān)鍵字調(diào)用構(gòu)造器,以便其this不會(huì)引用新構(gòu)造的對(duì)象:

function Person(name) { this.name = name; }
let ferdinand = Person("Ferdinand"); // oops
console.log(name);
// → Ferdinand

雖然我們錯(cuò)誤調(diào)用了Person,代碼也可以執(zhí)行成功,但會(huì)返回一個(gè)未定義值,并創(chuàng)建名為name的全局綁定。而在嚴(yán)格模式中,結(jié)果就不同了。

"use strict";
function Person(name) { this.name = name; }
let ferdinand = Person("Ferdinand");
// → TypeError: Cannot set property 'name' of undefined

JavaScript 會(huì)立即告知我們代碼中包含錯(cuò)誤。這種特性十分有用。

幸運(yùn)的是,使用class符號(hào)創(chuàng)建的構(gòu)造器,如果在不使用new來(lái)調(diào)用,則始終會(huì)報(bào)錯(cuò),即使在非嚴(yán)格模式下也不會(huì)產(chǎn)生問(wèn)題。

嚴(yán)格模式做了更多的事情。 它不允許使用同一名稱給函數(shù)賦多個(gè)參數(shù),并且完全刪除某些有問(wèn)題的語(yǔ)言特性(例如with語(yǔ)句,這是錯(cuò)誤的,本書(shū)不會(huì)進(jìn)一步討論)。

簡(jiǎn)而言之,在程序頂部放置"use strict"很少會(huì)有問(wèn)題,并且可能會(huì)幫助你發(fā)現(xiàn)問(wèn)題。

類型

有些語(yǔ)言甚至在運(yùn)行程序之前想要知道,所有綁定和表達(dá)式的類型。 當(dāng)類型以不一致的方式使用時(shí),他們會(huì)馬上告訴你。 JavaScript 只在實(shí)際運(yùn)行程序時(shí)考慮類型,即使經(jīng)常嘗試將值隱式轉(zhuǎn)換為它預(yù)期的類型,所以它沒(méi)有多大幫助。

盡管如此,類型為討論程序提供了一個(gè)有用的框架。 許多錯(cuò)誤來(lái)自于值的類型的困惑,它們進(jìn)入或來(lái)自一個(gè)函數(shù)。 如果你把這些信息寫(xiě)下來(lái),你不太可能會(huì)感到困惑。

你可以在上一章的goalOrientedRobot函數(shù)上面,添加一個(gè)像這樣的注釋來(lái)描述它的類型。

// (WorldState, Array) → {direction: string, memory: Array}
function goalOrientedRobot(state, memory) {
 // ...
}

有許多不同的約定,用于標(biāo)注 JavaScript 程序的類型。

關(guān)于類型的一點(diǎn)是,他們需要引入自己的復(fù)雜性,以便能夠描述足夠有用的代碼。 你認(rèn)為從數(shù)組中返回一個(gè)隨機(jī)元素的randomPick函數(shù)的類型是什么? 你需要引入一個(gè)綁定類型T,它可以代表任何類型,這樣你就可以給予randomPick一個(gè)像([T])->T的類型(從T到T的數(shù)組的函數(shù))。

當(dāng)程序的類型已知時(shí),計(jì)算機(jī)可以為你檢查它們,在程序運(yùn)行之前指出錯(cuò)誤。 有幾種 JavaScript 語(yǔ)言為語(yǔ)言添加類型并檢查它們。 最流行的稱為 TypeScript。 如果你有興趣為你的程序添加更多的嚴(yán)謹(jǐn)性,我建議你嘗試一下。

在本書(shū)中,我們將繼續(xù)使用原始的,危險(xiǎn)的,非類型化的 JavaScript 代碼。

測(cè)試

如果語(yǔ)言不會(huì)幫助我們發(fā)現(xiàn)錯(cuò)誤,我們將不得不努力找到它們:通過(guò)運(yùn)行程序并查看它是否正確執(zhí)行。

一次又一次地手動(dòng)操作,是一個(gè)非常糟糕的主意。 這不僅令人討厭,而且也往往是無(wú)效的,因?yàn)槊看胃淖儠r(shí)都需要花費(fèi)太多時(shí)間來(lái)詳盡地測(cè)試所有內(nèi)容。

計(jì)算機(jī)擅長(zhǎng)重復(fù)性任務(wù),測(cè)試是理想的重復(fù)性任務(wù)。 自動(dòng)化測(cè)試是編寫(xiě)測(cè)試另一個(gè)程序的程序的過(guò)程。 編寫(xiě)測(cè)試比手工測(cè)試有更多的工作,但是一旦你完成了它,你就會(huì)獲得一種超能力:它只需要幾秒鐘就可以驗(yàn)證,你的程序在你編寫(xiě)為其測(cè)試的所有情況下都能正常運(yùn)行。 當(dāng)你破壞某些東西時(shí),你會(huì)立即注意到,而不是在稍后的時(shí)間里隨機(jī)地碰到它。

測(cè)試通常采用小標(biāo)簽程序的形式來(lái)驗(yàn)證代碼的某些方面。 例如,一組(標(biāo)準(zhǔn)的,可能已經(jīng)由其他人測(cè)試過(guò))toUpperCase方法的測(cè)試可能如下:

function test(label, body) {
 if (!body()) console.log(`Failed: ${label}`);
}

test("convert Latin text to uppercase", () => {
 return "hello".toUpperCase() == "HELLO";
});
test("convert Greek text to uppercase", () => {
 return "Χα?ρετε".toUpperCase() == "ΧΑ?ΡΕΤΕ";
});
test("don't convert case-less characters", () => {
 return "?????".toUpperCase() == "?????";
});

像這樣寫(xiě)測(cè)試往往會(huì)產(chǎn)生很多重復(fù),笨拙的代碼。 幸運(yùn)的是,有些軟件通過(guò)提供適合于表達(dá)測(cè)試的語(yǔ)言(以函數(shù)和方法的形式),并在測(cè)試失敗時(shí)輸出豐富的信息來(lái)幫助你構(gòu)建和運(yùn)行測(cè)試集合(測(cè)試套件,test suite)。 這些通常被稱為測(cè)試運(yùn)行器(test runner)。

一些代碼比其他代碼更容易測(cè)試。 通常,代碼與外部交互的對(duì)象越多,建立用于測(cè)試它的上下文就越困難。 上一章中顯示的編程風(fēng)格,使用自包含的持久值而不是更改對(duì)象,通常很容易測(cè)試。

調(diào)試

當(dāng)程序的運(yùn)行結(jié)果不符合預(yù)期或在運(yùn)行過(guò)程中產(chǎn)生錯(cuò)誤時(shí),你就會(huì)注意到程序出現(xiàn)問(wèn)題了,下一步就是要推斷問(wèn)題出在什么地方。

有時(shí)錯(cuò)誤很明顯。錯(cuò)誤消息會(huì)指出錯(cuò)誤出現(xiàn)在程序的哪一行,只要稍加閱讀錯(cuò)誤描述及出錯(cuò)的那行代碼,你一般就知道如何修正錯(cuò)誤了。

但不總是這樣。 有時(shí)觸發(fā)問(wèn)題的行,只是第一個(gè)地方,它以無(wú)效方式使用其他地方產(chǎn)生的奇怪的值。 如果你在前幾章中已經(jīng)解決了練習(xí),你可能已經(jīng)遇到過(guò)這種情況。

下面的示例代碼嘗試將一個(gè)整數(shù)轉(zhuǎn)換成給定進(jìn)制表示的字符串(十進(jìn)制、二進(jìn)制等),其原理是:不斷循環(huán)取出最后一位數(shù)字,并將其除以基數(shù)(將最后一位數(shù)從數(shù)字中除去)。但該程序目前的輸出表明程序中是存在bug的。

function numberToString(n, base = 10) {
 let result = "", sign = "";
 if (n < 0) {
 sign = "-";
 n = -n;
 }
 do {
 result = String(n % base) + result;
 n /= base;
 } while (n > 0);
 return sign + result;
}
console.log(numberToString(13, 10));
// → 1.5e-3231.3e-3221.3e-3211.3e-3201.3e-3191.3e-3181.3…

你可能已經(jīng)發(fā)現(xiàn)程序運(yùn)行結(jié)果不對(duì)了,不過(guò)先暫時(shí)裝作不知道。我們知道程序運(yùn)行出了問(wèn)題,試圖找出其原因。

這是一個(gè)地方,你必須抵制隨機(jī)更改代碼來(lái)查看它是否變得更好的沖動(dòng)。 相反,要思考。 分析正在發(fā)生的事情,并提出為什么可能發(fā)生的理論。 然后,再做一些觀察來(lái)檢驗(yàn)這個(gè)理論 - 或者,如果你還沒(méi)有理論,可以進(jìn)一步觀察來(lái)幫助你想出一個(gè)理論。

有目的地在程序中使用console.log來(lái)查看程序當(dāng)前的運(yùn)行狀態(tài),是一種不錯(cuò)的獲取額外信息的方法。在本例中,我們希望n的值依次變?yōu)?13,1,然后是 0。讓我們先在循環(huán)起始處輸出n的值。

13
1.3
0.13
0.013
…
1.5e-323

沒(méi)錯(cuò)。13 除以 10 并不會(huì)產(chǎn)生整數(shù)。我們不應(yīng)該使用n/=base,而應(yīng)該使用n=Math.floor(n/base),使數(shù)字“右移”,這才是我們實(shí)際想要的結(jié)果。

使用console.log來(lái)查看程序行為的替代方法,是使用瀏覽器的調(diào)試器(debugger)功能。 瀏覽器可以在代碼的特定行上設(shè)置斷點(diǎn)(breakpoint)。 當(dāng)程序執(zhí)行到帶有斷點(diǎn)的行時(shí),它會(huì)暫停,并且你可以檢查該點(diǎn)的綁定值。 我不會(huì)詳細(xì)討論,因?yàn)檎{(diào)試器在不同瀏覽器上有所不同,但請(qǐng)查看瀏覽器的開(kāi)發(fā)人員工具或在 Web 上搜索來(lái)獲取更多信息。

設(shè)置斷點(diǎn)的另一種方法,是在程序中包含一個(gè)debugger語(yǔ)句(僅由該關(guān)鍵字組成)。 如果你的瀏覽器的開(kāi)發(fā)人員工具是激活的,則只要程序達(dá)到這個(gè)語(yǔ)句,程序就會(huì)暫停。

錯(cuò)誤傳播

不幸的是,程序員不可能避免所有問(wèn)題。 如果你的程序以任何方式與外部世界進(jìn)行通信,則可能會(huì)導(dǎo)致輸入格式錯(cuò)誤,工作負(fù)荷過(guò)重或網(wǎng)絡(luò)故障。

如果你只為自己編程,那么你就可以忽略這些問(wèn)題直到它們發(fā)生。 但是如果你創(chuàng)建了一些將被其他人使用的東西,你通常希望程序比只是崩潰做得更好。 有時(shí)候,正確的做法是不擇手段地繼續(xù)運(yùn)行。 在其他情況下,最好向用戶報(bào)告出了什么問(wèn)題然后放棄。 但無(wú)論在哪種情況下,該程序都必須積極采取措施來(lái)回應(yīng)問(wèn)題。

假設(shè)你有一個(gè)函數(shù)promptInteger,要求用戶輸入一個(gè)整數(shù)并返回它。 如果用戶輸入"orange",它應(yīng)該返回什么?

一種辦法是返回一個(gè)特殊值,通常會(huì)使用null,undefined或 -1。

function promptNumber(question) {
 let result = Number(prompt(question, ""));
 if (Number.isNaN(result)) return null;
 else return result;
}

console.log(promptNumber("How many trees do you see?"));

現(xiàn)在,調(diào)用promptNumber的任何代碼都必須檢查是否實(shí)際讀取了數(shù)字,否則必須以某種方式恢復(fù) - 也許再次詢問(wèn)或填充默認(rèn)值。 或者它可能會(huì)再次向它的調(diào)用者返回一個(gè)特殊值,表示它未能完成所要求的操作。

在很多情況下,當(dāng)錯(cuò)誤很常見(jiàn)并且調(diào)用者應(yīng)該明確地考慮它們時(shí),返回特殊值是表示錯(cuò)誤的好方法。 但它確實(shí)有其不利之處。 首先,如果函數(shù)已經(jīng)可能返回每一種可能的值呢? 在這樣的函數(shù)中,你必須做一些事情,比如將結(jié)果包裝在一個(gè)對(duì)象中,以便能夠區(qū)分成功與失敗。

function lastElement(array) {
 if (array.length == 0) {
 return {failed: true};
 } else {
 return {element: array[array.length - 1]};
 }
}

返回特殊值的第二個(gè)問(wèn)題是它可能產(chǎn)生非常笨拙的代碼。 如果一段代碼調(diào)用promptNumber 10 次,則必須檢查是否返回null 10 次。 如果它對(duì)null的回應(yīng)是簡(jiǎn)單地返回null本身,函數(shù)的調(diào)用者將不得不去檢查它,以此類推。

異常

當(dāng)函數(shù)無(wú)法正常工作時(shí),我們只希望停止當(dāng)前任務(wù),并立即跳轉(zhuǎn)到負(fù)責(zé)處理問(wèn)題的位置。這就是異常處理的功能。

異常是一種當(dāng)代碼執(zhí)行中遇到問(wèn)題時(shí),可以觸發(fā)(或拋出)異常的機(jī)制,異常只是一個(gè)普通的值。觸發(fā)異常類似于從函數(shù)中強(qiáng)制返回:異常不只跳出到當(dāng)前函數(shù)中,還會(huì)跳出函數(shù)調(diào)用方,直到當(dāng)前執(zhí)行流初次調(diào)用函數(shù)的位置。這種方式被稱為“堆棧展開(kāi)(Unwinding the Stack)”。你可能還記得我們?cè)诘?章中介紹的函數(shù)調(diào)用棧,異常會(huì)減小堆棧的尺寸,并丟棄所有在縮減程序棧尺寸過(guò)程中遇到的函數(shù)調(diào)用上下文。

如果異常總是會(huì)將堆棧尺寸縮減到棧底,那么異常也就毫無(wú)用處了。它只不過(guò)是換了一種方式來(lái)徹底破壞你的程序罷了。異常真正強(qiáng)大的地方在于你可以在堆棧上設(shè)置一個(gè)“障礙物”,當(dāng)異??s減堆棧到達(dá)這個(gè)位置時(shí)會(huì)被捕獲。一旦發(fā)現(xiàn)異常,你可以使用它來(lái)解決問(wèn)題,然后繼續(xù)運(yùn)行該程序。

function promptDirection(question) {
 let result = prompt(question, "");
 if (result.toLowerCase() == "left") return "L";
 if (result.toLowerCase() == "right") return "R";
 throw new Error("Invalid direction: " + result);
}

function look() {
 if (promptDirection("Which way?") == "L") {
 return "a house";
 } else {
 return "two angry bears";
 }
}

try {
 console.log("You see", look());
} catch (error) {
 console.log("Something went wrong: " + error);
}

throw關(guān)鍵字用于引發(fā)異常。 異常的捕獲通過(guò)將一段代碼包裝在一個(gè)try塊中,后跟關(guān)鍵字catch來(lái)完成。 當(dāng)try塊中的代碼引發(fā)異常時(shí),將求值catch塊,并將括號(hào)中的名稱綁定到異常值。 在catch塊結(jié)束之后,或者try塊結(jié)束并且沒(méi)有問(wèn)題時(shí),程序在整個(gè)try / catch語(yǔ)句的下面繼續(xù)執(zhí)行。

在本例中,我們使用Error構(gòu)造器來(lái)創(chuàng)建異常值。這是一個(gè)標(biāo)準(zhǔn)的 JavaScript 構(gòu)造器,用于創(chuàng)建一個(gè)對(duì)象,包含message屬性。在多數(shù) JavaScript 環(huán)境中,構(gòu)造器實(shí)例也會(huì)收集異常創(chuàng)建時(shí)的調(diào)用棧信息,即堆棧跟蹤信息(Stack Trace)。該信息存儲(chǔ)在stack屬性中,對(duì)于調(diào)用問(wèn)題有很大的幫助,我們可以從堆棧跟蹤信息中得知問(wèn)題發(fā)生的精確位置,即問(wèn)題具體出現(xiàn)在哪個(gè)函數(shù)中,以及執(zhí)行失敗為止調(diào)用的其他函數(shù)鏈。

需要注意的是現(xiàn)在look函數(shù)可以完全忽略promptDirection出錯(cuò)的可能性。這就是使用異常的優(yōu)勢(shì):只有在錯(cuò)誤觸發(fā)且必須處理的位置才需要錯(cuò)誤處理代碼。其間的函數(shù)可以忽略異常處理。

嗯,我們要講解的理論知識(shí)差不多就這些了。

異常后清理

異常的效果是另一種控制流。 每個(gè)可能導(dǎo)致異常的操作(幾乎每個(gè)函數(shù)調(diào)用和屬性訪問(wèn))都可能導(dǎo)致控制流突然離開(kāi)你的代碼。

這意味著當(dāng)代碼有幾個(gè)副作用時(shí),即使它的“常規(guī)”控制流看起來(lái)像它們總是會(huì)發(fā)生,但異??赡軙?huì)阻止其中一些發(fā)生。

這是一些非常糟糕的銀行代碼。

const accounts = {
 a: 100,
 b: 0,
 c: 20
};

function getAccount() {
 let accountName = prompt("Enter an account name");
 if (!accounts.hasOwnProperty(accountName)) {
 throw new Error(`No such account: ${accountName}`);
 }
 return accountName;
}

function transfer(from, amount) {
 if (accounts[from] < amount) return;
 accounts[from] -= amount;
 accounts[getAccount()] += amount;
}

transfer函數(shù)將一筆錢(qián)從一個(gè)給定的賬戶轉(zhuǎn)移到另一個(gè)賬戶,在此過(guò)程中詢問(wèn)另一個(gè)賬戶的名稱。 如果給定一個(gè)無(wú)效的帳戶名稱,getAccount將引發(fā)異常。

但是transfer首先從帳戶中刪除資金,之后調(diào)用getAccount,之后將其添加到另一個(gè)帳戶。 如果它在那個(gè)時(shí)候由異常中斷,它就會(huì)讓錢(qián)消失。

這段代碼本來(lái)可以更智能一些,例如在開(kāi)始轉(zhuǎn)移資金之前調(diào)用getAccount。 但這樣的問(wèn)題往往以更微妙的方式出現(xiàn)。 即使是那些看起來(lái)不像是會(huì)拋出異常的函數(shù),在特殊情況下,或者當(dāng)他們包含程序員的錯(cuò)誤時(shí),也可能會(huì)這樣。

解決這個(gè)問(wèn)題的一個(gè)方法是使用更少的副作用。 同樣,計(jì)算新值而不是改變現(xiàn)有數(shù)據(jù)的編程風(fēng)格有所幫助。 如果一段代碼在創(chuàng)建新值時(shí)停止運(yùn)行,沒(méi)有人會(huì)看到這個(gè)完成一半的值,并且沒(méi)有問(wèn)題。

但這并不總是實(shí)際的。 所以try語(yǔ)句具有另一個(gè)特性。 他們可能會(huì)跟著一個(gè)finally塊,而不是catch塊,也不是在它后面。 finally塊會(huì)說(shuō)“不管發(fā)生什么事,在嘗試運(yùn)行try塊中的代碼后,一定會(huì)運(yùn)行這個(gè)代碼?!?/p>

function transfer(from, amount) {
 if (accounts[from] < amount) return;
 let progress = 0;
 try {
 accounts[from] -= amount;
 progress = 1;
 accounts[getAccount()] += amount;
 progress = 2;
 } finally {
 if (progress == 1) {
  accounts[from] += amount;
 }
 }
}

這個(gè)版本的函數(shù)跟蹤其進(jìn)度,如果它在離開(kāi)時(shí)注意到,它中止在創(chuàng)建不一致的程序狀態(tài)的位置,則修復(fù)它造成的損害。

請(qǐng)注意,即使finally代碼在異常退出try塊時(shí)運(yùn)行,它也不會(huì)影響異常。finally塊運(yùn)行后,堆棧繼續(xù)展開(kāi)。

即使異常出現(xiàn)在意外的地方,編寫(xiě)可靠運(yùn)行的程序也非常困難。 很多人根本就不關(guān)心,而且由于異常通常針對(duì)異常情況而保留,因此問(wèn)題可能很少發(fā)生,甚至從未被發(fā)現(xiàn)。 這是一件好事還是一件糟糕的事情,取決于軟件執(zhí)行失敗時(shí)會(huì)造成多大的損害。

選擇性捕獲

當(dāng)程序出現(xiàn)異常且異常未被捕獲時(shí),異常就會(huì)直接回退到棧頂,并由 JavaScript 環(huán)境來(lái)處理。其處理方式會(huì)根據(jù)環(huán)境的不同而不同。在瀏覽器中,錯(cuò)誤描述通常會(huì)寫(xiě)入 JavaScript 控制臺(tái)中(可以使用瀏覽器工具或開(kāi)發(fā)者菜單來(lái)訪問(wèn)控制臺(tái))。我們將在第 20 章中討論的,無(wú)瀏覽器的 JavaScript 環(huán)境 Node.js 對(duì)數(shù)據(jù)損壞更加謹(jǐn)慎。 當(dāng)發(fā)生未處理的異常時(shí),它會(huì)中止整個(gè)過(guò)程。

對(duì)于程序員的錯(cuò)誤,讓錯(cuò)誤通行通常是最好的。 未處理的異常是表示糟糕的程序的合理方式,而在現(xiàn)代瀏覽器上,JavaScript 控制臺(tái)為你提供了一些信息,有關(guān)在發(fā)生問(wèn)題時(shí)堆棧上調(diào)用了哪些函數(shù)的。

對(duì)于在日常使用中發(fā)生的預(yù)期問(wèn)題,因未處理的異常而崩潰是一種糟糕的策略。

語(yǔ)言的非法使用方式,比如引用一個(gè)不存在的綁定,在null中查詢屬性,或調(diào)用的對(duì)象不是函數(shù)最終都會(huì)引發(fā)異常。你可以像自己的異常一樣捕獲這些異常。

進(jìn)入catch語(yǔ)句塊時(shí),我們只知道try體中引發(fā)了異常,但不知道引發(fā)了哪一類或哪一個(gè)異常。

JavaScript(很明顯的疏漏)并未對(duì)選擇性捕獲異常提供良好的支持,要不捕獲所有異常,要不什么都不捕獲。這讓你很容易假設(shè),你得到的異常就是你在寫(xiě)catch時(shí)所考慮的異常。

但它也可能不是。 可能會(huì)違反其他假設(shè),或者你可能引入了導(dǎo)致異常的 bug。 這是一個(gè)例子,它嘗試持續(xù)調(diào)用promptDirection,直到它得到一個(gè)有效的答案:

for (;;) {
 try {
 let dir = promtDirection("Where?"); // ← typo!
 console.log("You chose ", dir);
 break;
 } catch (e) {
 console.log("Not a valid direction. Try again.");
 }
}

我們可以使用for (;;)循環(huán)體來(lái)創(chuàng)建一個(gè)無(wú)限循環(huán),其自身永遠(yuǎn)不會(huì)停止運(yùn)行。我們?cè)谟脩艚o出有效的方向之后會(huì)跳出循環(huán)。但我們拼寫(xiě)錯(cuò)了promptDirection,因此會(huì)引發(fā)一個(gè)“未定義值”錯(cuò)誤。由于catch塊完全忽略了異常值,假定其知道問(wèn)題所在,錯(cuò)將綁定錯(cuò)誤信息當(dāng)成錯(cuò)誤輸入。這樣不僅會(huì)引發(fā)無(wú)限循環(huán),而且會(huì)掩蓋掉真正的錯(cuò)誤消息——綁定名拼寫(xiě)錯(cuò)誤。

一般而言,只有將拋出的異常重定位到其他地方進(jìn)行處理時(shí),我們才會(huì)捕獲所有異常。比如說(shuō)通過(guò)網(wǎng)絡(luò)傳輸通知其他系統(tǒng)當(dāng)前應(yīng)用程序的崩潰信息。即便如此,我們也要注意編寫(xiě)的代碼是否會(huì)將錯(cuò)誤信息掩蓋起來(lái)。

因此,我們轉(zhuǎn)而會(huì)去捕獲那些特殊類型的異常。我們可以在catch代碼塊中判斷捕獲到的異常是否就是我們期望處理的異常,如果不是則將其重新拋出。那么我們?cè)撊绾伪鎰e拋出異常的類型呢?

我們可以將它的message屬性與我們所期望的錯(cuò)誤信息進(jìn)行比較。 但是,這是一種不穩(wěn)定的編寫(xiě)代碼的方式 - 我們將使用供人類使用的信息來(lái)做出程序化決策。 只要有人更改(或翻譯)該消息,代碼就會(huì)停止工作。

我們不如定義一個(gè)新的錯(cuò)誤類型,并使用instanceof來(lái)識(shí)別異常。

class InputError extends Error {}

function promptDirection(question) {
 let result = prompt(question);
 if (result.toLowerCase() == "left") return "L";
 if (result.toLowerCase() == "right") return "R";
 throw new InputError("Invalid direction: " + result);
}

新的錯(cuò)誤類擴(kuò)展了Error。 它沒(méi)有定義它自己的構(gòu)造器,這意味著它繼承了Error構(gòu)造器,它需要一個(gè)字符串消息作為參數(shù)。 事實(shí)上,它根本沒(méi)有定義任何東西 - 這個(gè)類是空的。 InputError對(duì)象的行為與Error對(duì)象相似,只是它們的類不同,我們可以通過(guò)類來(lái)識(shí)別它們。

現(xiàn)在循環(huán)可以更仔細(xì)地捕捉它們。

for (;;) {
 try {
 let dir = promptDirection("Where?");
 console.log("You chose ", dir);
 break;
 } catch (e) {
 if (e instanceof InputError) {
  console.log("Not a valid direction. Try again.");
 } else {
  throw e;
 }
 }
}

這里的catch代碼只會(huì)捕獲InputError類型的異常,而其他類型的異常則不會(huì)在這里進(jìn)行處理。如果又輸入了不正確的值,那么系統(tǒng)會(huì)向用戶準(zhǔn)確報(bào)告錯(cuò)誤——“綁定未定義”。

斷言

斷言(assertions)是程序內(nèi)部的檢查,用于驗(yàn)證某個(gè)東西是它應(yīng)該是的方式。 它們并不是用于處理正常操作中可能出現(xiàn)的情況,而是發(fā)現(xiàn)程序員的錯(cuò)誤。

例如,如果firstElement被描述為一個(gè)函數(shù),永遠(yuǎn)不會(huì)在空數(shù)組上調(diào)用,我們可以這樣寫(xiě):

function firstElement(array) {
 if (array.length == 0) {
 throw new Error("firstElement called with []");
 }
 return array[0];
}

現(xiàn)在,它不會(huì)默默地返回未定義值(當(dāng)你讀取一個(gè)不存在的數(shù)組屬性的時(shí)候),而是在你濫用它時(shí)立即干掉你的程序。 這使得這種錯(cuò)誤不太可能被忽視,并且當(dāng)它們發(fā)生時(shí)更容易找到它們的原因。

我不建議嘗試為每種可能的不良輸入編寫(xiě)斷言。 這將是很多工作,并會(huì)產(chǎn)生非常雜亂的代碼。 你會(huì)希望為很容易犯(或者你發(fā)現(xiàn)自己做過(guò))的錯(cuò)誤保留他們。

本章小結(jié)

錯(cuò)誤和無(wú)效的輸入十分常見(jiàn)。編程的一個(gè)重要部分是發(fā)現(xiàn),診斷和修復(fù)錯(cuò)誤。 如果你擁有自動(dòng)化測(cè)試套件或向程序添加斷言,則問(wèn)題會(huì)變得更容易被注意。

我們常常需要使用優(yōu)雅的方式來(lái)處理程序可控范圍外的問(wèn)題。如果問(wèn)題可以就地解決,那么返回一個(gè)特殊的值來(lái)跟蹤錯(cuò)誤就是一個(gè)不錯(cuò)的解決方案?;蛘?,異常也可能是可行的。

拋出異常會(huì)引發(fā)堆棧展開(kāi),直到遇到下一個(gè)封閉的try/catch塊,或堆棧底部為止。catch塊捕獲異常后,會(huì)將異常值賦予catch塊,catch塊中應(yīng)該驗(yàn)證異常是否是實(shí)際希望處理的異常,然后進(jìn)行處理。為了有助于解決由于異常引起的不可預(yù)測(cè)的執(zhí)行流,可以使用finally塊來(lái)確保執(zhí)行try塊之后的代碼。

習(xí)題

重試

假設(shè)有一個(gè)函數(shù)primitiveMultiply,在 20% 的情況下將兩個(gè)數(shù)相乘,在另外 80% 的情況下會(huì)觸發(fā)MultiplicatorUnitFailure類型的異常。編寫(xiě)一個(gè)函數(shù),調(diào)用這個(gè)容易出錯(cuò)的函數(shù),不斷嘗試直到調(diào)用成功并返回結(jié)果為止。

確保只處理你期望的異常。

class MultiplicatorUnitFailure extends Error {}

function primitiveMultiply(a, b) {
 if (Math.random() < 0.2) {
 return a * b;
 } else {
 throw new MultiplicatorUnitFailure();
 }
}

function reliableMultiply(a, b) {
 // Your code here.
}

console.log(reliableMultiply(8, 8));
// → 64

上鎖的箱子

考慮以下這個(gè)編寫(xiě)好的對(duì)象:

const box = {
 locked: true,
 unlock() { this.locked = false; },
 lock() { this.locked = true; },
 _content: [],
 get content() {
 if (this.locked) throw new Error("Locked!");
 return this._content;
 }
};

這是一個(gè)帶鎖的箱子。其中有一個(gè)數(shù)組,但只有在箱子被解鎖時(shí),才可以訪問(wèn)數(shù)組。不允許直接訪問(wèn)_content屬性。

編寫(xiě)一個(gè)名為withBoxUnlocked的函數(shù),接受一個(gè)函數(shù)類型的參數(shù),其作用是解鎖箱子,執(zhí)行該函數(shù),無(wú)論是正常返回還是拋出異常,在withBoxUnlocked函數(shù)返回前都必須鎖上箱子。

const box = {
 locked: true,
 unlock() { this.locked = false; },
 lock() { this.locked = true; },
 _content: [],
 get content() {
 if (this.locked) throw new Error("Locked!");
 return this._content;
 }
};

function withBoxUnlocked(body) {
 // Your code here.
}

withBoxUnlocked(function() {
 box.content.push("gold piece");
});

try {
 withBoxUnlocked(function() {
 throw new Error("Pirates on the horizon! Abort!");
 });
} catch (e) {
 console.log("Error raised:", e);
}
console.log(box.locked);
// → true

以上是“JavaScript中BUG和錯(cuò)誤怎么辦”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!

網(wǎng)站標(biāo)題:JavaScript中BUG和錯(cuò)誤怎么辦
本文網(wǎng)址:http://aaarwkj.com/article26/gooecg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供微信小程序域名注冊(cè)、響應(yīng)式網(wǎng)站、Google、品牌網(wǎng)站制作、營(yíng)銷型網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

h5響應(yīng)式網(wǎng)站建設(shè)
男人天堂一区二区av| 午夜射精视频在线观看| 饥渴少妇高潮特殊按摩| 午夜性色福利在线播放 | 日本一区中文字幕欧美| 成年人正常性生活频率| 大香蕉欧美视频伊在线| 成年免费视频一区二区三区| 国产精品成人av在线网站| 先锋影音女同中文字幕| 国产精品亚洲综合制服日韩| 欧美一日韩一级片免费看| 日韩av专区在线免费观看| 国产三级精品三级精品在一区| 青青草视频免费公开播放| 婷婷人妻中文字幕在线| 欧美制服丝袜亚洲自拍偷拍| 一区二区三区毛片观看| 国产精品欧美日韩中文| 国产激情久久久久久影院| 中文字幕精品久久久人妻| 日韩在线视频精品一区| 日本午夜专区一区二区| 久久99久久久国产精品| 视频一区二区日韩不卡| 国产激情盗摄一区二区三区| 18禁污污网站国产| 七十二式性日韩视频| 伦理中文字幕一区二区| 搡老熟女老女人一区二区| 91九色国产在线播放| 黄色一级日本黄色一级| 国产一区二区在线粉嫩| 亚洲av成人一区二区三区| 日本中文字幕一区二区视频| 91好色视频在线观看| 亚洲国产精品久久久精品| 日本人妻系列中文字幕| 国产经典三级在线观看| 国产精品粗又长一区| 亚洲一区二区三区av电影|