整合營(yíng)銷(xiāo)服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢(xún)熱線:

          JavaScript異常錯(cuò)誤處理指南

          JavaScript異常錯(cuò)誤處理指南



          前端的 JavaScript 開(kāi)發(fā)中,發(fā)現(xiàn)開(kāi)發(fā)者對(duì)于錯(cuò)誤異常的處理普遍都比較簡(jiǎn)單粗暴,如果應(yīng)用程序中缺少有效的錯(cuò)誤處理和容錯(cuò)機(jī)制,代碼的健壯性就無(wú)從談起。本文整理出了一些常見(jiàn)的錯(cuò)誤異常處理的場(chǎng)景,旨在為前端的 JavaScript 錯(cuò)誤異常處理提供一些基礎(chǔ)的指導(dǎo)。


          Error 對(duì)象

          先來(lái)簡(jiǎn)單介紹一下 JavaScript 中的 Error 對(duì)象,通常 Error 對(duì)象由重要的兩部分組成,包含了 error.message 錯(cuò)誤信息和 error.stack 錯(cuò)誤追溯棧。

          產(chǎn)生一個(gè)錯(cuò)誤很簡(jiǎn)單,比如在 foo.js 中直接調(diào)用一個(gè)不存在的 callback 函數(shù)。

          // foo.js
          function foo () {
              callback();
          }
          
          foo();
          

          此時(shí)通過(guò) Chrome 瀏覽器的控制臺(tái)會(huì)展示如下的信息。

          Uncaught ReferenceError: callback is not defined
              at foo (foo.js:2)
              at foo.js:5
          

          其中 Uncaught ReferenceError: callback is not defined 就是 error.message 錯(cuò)誤信息,而剩下的 at xxx 就是具體的錯(cuò)誤追溯棧,在 Chrome 的控制臺(tái)中,對(duì)錯(cuò)誤的展示進(jìn)行了優(yōu)化。

          如果我們通過(guò) window.onerror 來(lái)捕獲到該錯(cuò)誤后將 Error 對(duì)象直接輸出到頁(yè)面中會(huì)展示出更原始的數(shù)據(jù)。

          <!-- 展示錯(cuò)誤的容器 -->
          <textarea id="error"></textarea>
          
          // 輸出錯(cuò)誤
          window.onerror=function (msg, url, line, col, err) {
              document.getElementById('error').textContent=err.message + '\n\n' + err.stack;
          };
          

          原始的錯(cuò)誤數(shù)據(jù)中會(huì)展示出錯(cuò)誤追溯棧中的 Source URL。

          callback is not defined
          
          ReferenceError: callback is not defined
              at foo (http://example.com/js-error/foo.js:2:5)
              at http://example.com/js-error/foo.js:5:1
          

          有了錯(cuò)誤追溯棧,就能通過(guò)發(fā)生錯(cuò)誤的文件 Source URL 和錯(cuò)誤在代碼中的具體位置來(lái)快速定位到錯(cuò)誤。

          看起來(lái)好像很簡(jiǎn)單,但實(shí)際的開(kāi)發(fā)中如何有效的捕獲錯(cuò)誤,如何有效的拋出錯(cuò)誤都有一些需要注意的點(diǎn),下面逐個(gè)的來(lái)講解。


          window.onerror

          前端在捕獲錯(cuò)誤時(shí)都會(huì)通過(guò)綁定 window.onerror 事件來(lái)捕獲全局的 JavaScript 執(zhí)行錯(cuò)誤,標(biāo)準(zhǔn)的瀏覽器在響應(yīng)該事件時(shí)會(huì)依次提供 5 個(gè)參數(shù)。

          window.onerror=function(message, source, lineno, colno, error) { ... }
          
          1. message 錯(cuò)誤信息
          2. source 錯(cuò)誤發(fā)生時(shí)的頁(yè)面 URL
          3. lineno 錯(cuò)誤發(fā)生時(shí)的 JS 文件行數(shù)
          4. colno 錯(cuò)誤發(fā)生時(shí)的 JS 文件列數(shù)
          5. error 錯(cuò)誤發(fā)生時(shí)拋出的標(biāo)準(zhǔn) Error 對(duì)象

          使用 window.addEventListener 也能綁定 error 事件,但是該事件函數(shù)的參數(shù)是一個(gè) ErrorEvent 對(duì)象。

          綁定 window.onerror 事件時(shí),事件處理函數(shù)的第 5 個(gè)參數(shù)在低版本瀏覽中或 JS 資源跨域場(chǎng)景下可能不是 Error 對(duì)象。


          在 Chrome 瀏覽器中如果頁(yè)面加載的 JS 資源文件中存在跨域的 script 標(biāo)簽,在發(fā)生錯(cuò)誤時(shí)會(huì)提示 Script error 而缺乏錯(cuò)誤追溯棧。

          window.onerror 在響應(yīng)跨域 JavaScript 錯(cuò)誤時(shí)缺乏錯(cuò)誤追溯棧時(shí)的 arguments 對(duì)象如下:

          [
              'Script error.',
              '',
              0,
              0,
              null
          ]
          

          為了正常的捕獲到跨域 JS 資源文件的錯(cuò)誤,需要具備兩個(gè)條件: 1. 為 JS 資源文件增加 CORS 響應(yīng)頭。 2. 通過(guò) script 引用該 JS 文件時(shí)增加 crossorigin="anonymous" 的屬性,如果是動(dòng)態(tài)加載的 JS,可以寫(xiě)作 script.crossOrigin=true 。

          window.onerror 能捕獲一些全局的 JavaScript 錯(cuò)誤,但還有不少場(chǎng)景在全局是捕獲不到的。


          try/catch

          window.onerror 能捕獲全局場(chǎng)景下的錯(cuò)誤,如果已知一些程序的場(chǎng)景中可能會(huì)出現(xiàn)錯(cuò)誤,這個(gè)時(shí)候一般會(huì)使用 try/catch 來(lái)進(jìn)行捕獲。

          但是在使用 try/catch 塊時(shí)無(wú)法捕獲異步錯(cuò)誤,例如塊中使用了 setTimeout 。

          try {
              setTimeout(function () {
                  callTimeout();  // callTimeout 未定義,會(huì)拋錯(cuò)
              }, 1000);
          }
          catch (err) {
              console.log('catch the error', err); // 不會(huì)被執(zhí)行
          }
          

          try/catch 在處理 setTimeout 這類(lèi)異步場(chǎng)景時(shí)是無(wú)效的,執(zhí)行時(shí)仍會(huì)拋錯(cuò),catch 中的代碼不會(huì)被執(zhí)行。

          雖然在 try/catch 中沒(méi)有捕獲到,此時(shí)如果有綁定 window.onerror 則會(huì)被全局捕獲。

          由此可見(jiàn), try/catch 應(yīng)該是只能捕獲 JS Event Loop 中同步的任務(wù)。

          如果想正確的捕獲 setTimeout 中的錯(cuò)誤,需要將 try/catch 塊寫(xiě)到 setTimeout 的函數(shù)中。

          setTimeout(function () {
              try {
                  callTimeout(); // callTimeout 未定義,不會(huì)拋錯(cuò)
              }
              catch (err) {
                  console.log('catch the error', err); // 將會(huì)被執(zhí)行
              }
          }, 1000);
          

          Promise

          Promise 有自己的錯(cuò)誤處理機(jī)制,通常 Promise 函數(shù)中的錯(cuò)誤無(wú)法被全局捕獲。

          var promise=new Promise(executor);
          promise.then(onFulfilled, onRejected);
          

          比較容易遺漏錯(cuò)誤處理的地方有 executor 和 onFulfilled ,在這些函數(shù)中如果發(fā)生錯(cuò)誤都不能被全局捕獲。

          正確的捕獲 Promise 的錯(cuò)誤,應(yīng)該使用 Promise.prototype.catch 方法,意外的錯(cuò)誤和使用 reject 主動(dòng)捕獲的錯(cuò)誤都會(huì)觸發(fā) catch 方法。


          catch 方法中通常會(huì)接收到一個(gè) Error 對(duì)象,但是當(dāng)調(diào)用 reject 函數(shù)時(shí)傳入的是一個(gè)非 Error 對(duì)象時(shí),catch 方法也會(huì)接收到一個(gè)非 Error 對(duì)象,這里的 reject 和 throw 的表現(xiàn)是一樣的,所以在使用 reject 時(shí),最好是傳入一個(gè) Error 對(duì)象。

          reject(
              new Error('this is reject message')
          );
          

          值得注意的是,如果 Promise 的 executor 中存在 setTimeout 語(yǔ)句時(shí), setTimeout 的報(bào)錯(cuò)會(huì)被全局捕獲。


          Async Function

          Async Function 和 Promise 一樣,發(fā)生錯(cuò)誤不會(huì)被全局的 window.onerror 捕獲,所以在使用時(shí)如果有報(bào)錯(cuò),需要手動(dòng)增加 try/catch 語(yǔ)句。

          匿名函數(shù)

          匿名函數(shù)的使用在 JavaScript 中很常見(jiàn),但是當(dāng)出現(xiàn)匿名函數(shù)的報(bào)錯(cuò)時(shí),在錯(cuò)誤追溯棧中會(huì)以 anonymous 來(lái)標(biāo)識(shí)錯(cuò)誤,為了排查錯(cuò)誤方便,可以將函數(shù)進(jìn)行命名,或者使用函數(shù)的 displayName 屬性。

          函數(shù)如果有 displayName 屬性,在錯(cuò)誤棧中會(huì)展示該屬性值,如果用于命名重要的業(yè)務(wù)邏輯屬性,將有效幫助排查錯(cuò)誤。


          throw error

          上面說(shuō)了很多錯(cuò)誤捕獲的注意點(diǎn),如果要主動(dòng)的拋錯(cuò),都會(huì)使用 throw 來(lái)拋錯(cuò),常見(jiàn)的幾種拋錯(cuò)方法如下:

          throw new Error('Problem description.')  // 方法 1
          throw Error('Problem description.')      // 方法 2
          throw 'Problem description.'             // 方法 3
          throw null                               // 方法 4
          

          其中方法 1 和方法 2 的效果一樣,瀏覽器都能正確的展示錯(cuò)誤追溯棧。方法 3 和方法 4 不推薦,雖然能拋錯(cuò),但是在拋錯(cuò)的時(shí)候不能展示錯(cuò)誤追溯棧。

          try/catch 和 throw ,一個(gè)用來(lái)捕獲錯(cuò)誤,一個(gè)用來(lái)拋出錯(cuò)誤,如果兩個(gè)結(jié)合起來(lái)用通常等于脫了褲子放屁多此一舉,唯一有點(diǎn)用的是可以對(duì)錯(cuò)誤信息進(jìn)行再加工。

          可以在 Chrome 控制臺(tái)中模擬出一個(gè)結(jié)合使用的實(shí)際場(chǎng)景。

          try {
              foo();
          }
          catch (err) {
              err.message='Catch the error: ' + err.message;
              throw Error(err);
          }
          

          由于在 catch 塊中又拋出了錯(cuò)誤,所以該錯(cuò)誤沒(méi)有被捕獲到,但此時(shí)錯(cuò)誤信息經(jīng)過(guò)了二次封裝。

          Uncaught Error: ReferenceError: Catch the error: foo is not defined
          

          通過(guò)對(duì)錯(cuò)誤信息的二次封裝,可以增加一些有利于快速定位錯(cuò)誤的額外信息。


          原作者:雨夜帶刀's Blog

          、Javascript的異常處理機(jī)制

          當(dāng)javascript代碼中出現(xiàn)錯(cuò)誤的時(shí)候,js引擎就會(huì)根據(jù)js的調(diào)用棧逐級(jí)尋找對(duì)應(yīng)的catch,如果沒(méi)有找到相應(yīng)的catch handler或catch handler本身又有error或者又拋出新的error,最后就會(huì)把這個(gè)error的處理交給瀏覽器,并顯示在錯(cuò)誤控制臺(tái)中)顯示錯(cuò)誤信息給訪問(wèn)者。

          二、try/catch/finally

          是js提供的異常處理機(jī)制,用法如下:

          try {
          // 這段代碼從上往下運(yùn)行,其中任何一個(gè)語(yǔ)句拋出異常該代碼塊就結(jié)束運(yùn)行}
          catch (e) {
          // 如果try代碼塊中拋出了異常,catch代碼塊中的代碼就會(huì)被執(zhí)行。
          // e是一個(gè)局部變量,用來(lái)指向Error對(duì)象或者其他拋出的對(duì)象
          }
          finally {
            //無(wú)論try中代碼是否有異常拋出(甚至是try代碼塊中有return語(yǔ)句),
                 //finally代碼塊中始終會(huì)被執(zhí)行
          }

          三、常見(jiàn)錯(cuò)誤

          3.1 syntaxError

          顧名思義,典型的語(yǔ)法錯(cuò)誤。

          function foo{
          }
          if{}

          Js代碼是從上往下依次執(zhí)行,但是js引擎先解析代碼是否語(yǔ)法出錯(cuò),如果語(yǔ)法都解析不通過(guò),那么顯而易見(jiàn):一行代碼也不會(huì)執(zhí)行,從而會(huì)在控制臺(tái)中輸出語(yǔ)法報(bào)錯(cuò):


          syntaxError錯(cuò)誤

          3.2 變量未定義

          變量未定義(也叫未聲明)指的是,當(dāng)程序中使用了一個(gè)未定義的變量則會(huì)報(bào)錯(cuò)。

          如下代碼:

          var foo=1
          var bar=foo + n1

          顯而易見(jiàn),n1變量是未定義的就直接使用,從而會(huì)在控制臺(tái)中輸出如下錯(cuò)誤:


          XXX is not defined 變量未定義

          3.3 TypeError錯(cuò)誤

          TypeError錯(cuò)誤指的是數(shù)據(jù)類(lèi)型未正確使用。

          例如一:

          var foo=function(){
          	console.log('foo')
          }
          foo='hello world'
          foo()

          在某些邏輯下,foo本身存儲(chǔ)的是函數(shù),但是誤把foo賦值了一個(gè)字符串或其它不是函數(shù)的數(shù)據(jù)類(lèi)型,但是foo當(dāng)作函數(shù)來(lái)調(diào)用了,則會(huì)報(bào)TypeError錯(cuò)誤在控制臺(tái)中輸出:


          TypeError,xxx is not a function

          例如二:

          未正確獲取元素,導(dǎo)致得到一個(gè)null而不是DOM節(jié)點(diǎn)對(duì)象后,綁定事件而引發(fā)的TypeError錯(cuò)誤。

          <script>
          var oBtn=document.getElementById('btn') 
          //因?yàn)榇a從上往下解析的原因,還未解析到button標(biāo)簽,返回為null。
          //null是空對(duì)象,不能綁定任何屬性,onclick雖然是事件,
          //但也是對(duì)象中屬性的一部分,所以報(bào)TypeError錯(cuò)誤。
          oBtn.onclick=function(){
          	console.log('bar')
          }
          </script>
          <button id="btn">foo</button>


          cannot set property 'onclick' of null

          正確錯(cuò)誤是把選擇元素的js代碼放置html標(biāo)簽之后,也就是緊鄰 </body>標(biāo)簽,或放在windo.onload事件中。

          <script>
          	window.onload=function(){
          		var oBtn=document.getElementById('btn') 
              //因?yàn)榇a從上往下解析的原因,還未解析到button標(biāo)簽,返回為null。
              //null是空對(duì)象,不能綁定任何屬性,onclick雖然是事件,
              //但也是對(duì)象中屬性的一部分,所以報(bào)TypeError錯(cuò)誤。
              oBtn.onclick=function(){
                console.log('bar')
              }
          	}
          </script>
          <button id="btn">foo</button>

          3.4 JSON解析錯(cuò)誤

          首先,我們需要了解JSON是什么 ?

          JSON(JavaScript Object Notation, JS 對(duì)象簡(jiǎn)譜) 是一種輕量級(jí)的數(shù)據(jù)交換格式。它基于 ECMAScript (歐洲計(jì)算機(jī)協(xié)會(huì)制定的js規(guī)范)的一個(gè)子集,采用完全獨(dú)立于編程語(yǔ)言的文本格式來(lái)存儲(chǔ)和表示數(shù)據(jù)。簡(jiǎn)潔和清晰的層次結(jié)構(gòu)使得 JSON 成為理想的數(shù)據(jù)交換語(yǔ)言。

          而它的定義規(guī)則和js中字面量聲明對(duì)象很像,所以很多初學(xué)者以為json就是js對(duì)象,其實(shí)這是錯(cuò)誤的。

          3.4.1 JSON 是 JS對(duì)象表示語(yǔ)法的子集。

          • 數(shù)據(jù)在名稱(chēng)/值對(duì)中
          • 數(shù)據(jù)由逗號(hào)分隔
          • 大括號(hào) {} 保存對(duì)象
          • 中括號(hào) [] 保存數(shù)組,數(shù)組可以包含多個(gè)對(duì)象

          3.4.2 JSON 數(shù)據(jù)的書(shū)寫(xiě)格式是

          key : value

          名稱(chēng)/值包括字段名稱(chēng)(在雙引號(hào)中),后面寫(xiě)一個(gè)冒號(hào),然后是值:

          "name" : "foo"

          3.4.3 JSON 值可以是:

          • 數(shù)字(整數(shù)或浮點(diǎn)數(shù))
          • 字符串(在雙引號(hào)中)
          • 邏輯值(true 或 false)
          • 數(shù)組(在中括號(hào)中)
          • 對(duì)象(在大括號(hào)中)
          • null

          前方高能~~~

          談到這里,json數(shù)據(jù)從哪來(lái)呢?

          在請(qǐng)求Ajax的時(shí)候,會(huì)從后臺(tái)服務(wù)器中拿到j(luò)son數(shù)據(jù),往往會(huì)把json解析成js對(duì)象。則前端工程師會(huì)用到JSON.parse方法。有時(shí)候前端也會(huì)定義JSON數(shù)據(jù),如果語(yǔ)法不正確當(dāng)轉(zhuǎn)成js對(duì)象時(shí),則會(huì)報(bào)錯(cuò)。如下代碼:

          //var foo='{ name:"bar" }'//name未帶雙引號(hào)
          var foo='{ "name":bar }'//bar未帶雙引號(hào)
          
          var f=JSON.parse( foo )


          token n in JSON at position

          正確的JSON轉(zhuǎn)換js對(duì)象的方式如下:

          var foo='{ "name":"bar","age":20 }'//20無(wú)需帶,則理解為數(shù)值類(lèi)型
          
          var f=JSON.parse( foo )
          console.log( f ) //{name: "bar", age: 20} ,此時(shí)可以正確的把json轉(zhuǎn)換成js對(duì)象,
          												//通過(guò) 點(diǎn) 語(yǔ)法,也就是f.name和f.age訪問(wèn)到具體的數(shù)據(jù)

          以上是JavaScript中常見(jiàn)的錯(cuò)誤,后期遇到會(huì)不斷更新,感謝小伙伴們的踴躍投稿和留言。

          內(nèi)容是《Web前端開(kāi)發(fā)之Javascript視頻》的課件,請(qǐng)配合大師哥《Javascript》視頻課程學(xué)習(xí)。

          錯(cuò)誤處理對(duì)于web應(yīng)用開(kāi)發(fā)至關(guān)重要,任何javascript錯(cuò)誤都有可能會(huì)導(dǎo)致網(wǎng)頁(yè)無(wú)法使用,因此作為開(kāi)發(fā)人員,必須要及時(shí)處理有可能出現(xiàn)的錯(cuò)誤;

          從IE4.0之后,幾乎所有的瀏覽器都包含了一些基本的錯(cuò)誤處理功能,但并沒(méi)有統(tǒng)一,后來(lái),由ECMAscript添加了異常處理機(jī)制,也就是try…catch…finally結(jié)構(gòu)以及throw操作;

          錯(cuò)誤處理的重要性: 好的錯(cuò)誤處理技術(shù)可以讓腳本的開(kāi)發(fā)、調(diào)試和部署更加流暢,能對(duì)代碼進(jìn)行更好的控制;另外,JS缺乏標(biāo)準(zhǔn)的開(kāi)發(fā)環(huán)境;

          錯(cuò)誤類(lèi)型:

          語(yǔ)法錯(cuò)誤(syntax error):也稱(chēng)為解析錯(cuò)誤,發(fā)生在傳統(tǒng)編程語(yǔ)言的編譯解釋時(shí);發(fā)生語(yǔ)法錯(cuò)誤時(shí),就會(huì)發(fā)生阻塞,也就是不能繼續(xù)執(zhí)行代碼,但只是同一個(gè)線程中的代碼會(huì)受影響,其他線程中的代碼不受影響;

          // Uncaught SyntaxError: Invalid or unexpected token
          // 未捕獲的語(yǔ)法錯(cuò)誤:無(wú)效或意外的標(biāo)記
          document.write("zeronetwork;

          運(yùn)行時(shí)錯(cuò)誤(Runtime error):也稱(chēng)為exception異常, 其發(fā)生在編譯期/解釋期后,此時(shí),問(wèn)題并不出現(xiàn)在代碼的語(yǔ)法上,而是在嘗試完成一個(gè)非法的操作;

          <input type="button" value="單擊" onclick="handleClick()" />
          <script>
          // Uncaught ReferenceError: openMy is not defined
          // 未捕獲的引用錯(cuò)誤:未定義openMy
          function handleClick(){
              openMy();
          }
          </script>

          錯(cuò)誤報(bào)告:

          因?yàn)槊總€(gè)瀏覽器都有自己的內(nèi)置Javascript解釋程序,所以每種瀏覽器報(bào)告錯(cuò)誤的方式都不同;有些是彈出錯(cuò)誤信息,有些是把信息打印在控制臺(tái)中;

          IE(windows): 默認(rèn)情況下,會(huì)彈出包含錯(cuò)誤細(xì)節(jié)的對(duì)話框,并詢(xún)問(wèn)是否繼續(xù)執(zhí)行頁(yè)面上的腳本;如果瀏覽器有調(diào)試器(如:Microsoft Script Debugger) ,此對(duì)話框會(huì)提供一個(gè)是調(diào)試還是忽略的選項(xiàng);如果在IE設(shè)置中取消了”顯示錯(cuò)誤”,那么會(huì)在頁(yè)面左下角顯示一個(gè)黃色的圖標(biāo);

          注:如果JS代碼就在HTML里,顯示錯(cuò)誤行是正確的,如果是外部的JS文件,則行號(hào)往往差一行,如第5行則為第4行;

          Mozilla(所有平臺(tái)): 在控制臺(tái)中打印錯(cuò)誤信息,并發(fā)出警告;其會(huì)報(bào)告三種類(lèi)型的消息:錯(cuò)誤、嚴(yán)格警告和消息等的;

          Safari (MacOS):是對(duì)JavaScript錯(cuò)誤和調(diào)試的支持最差,默認(rèn)情況下,它對(duì)終端用戶(hù)不提供任何javascript錯(cuò)誤報(bào)告;

          錯(cuò)誤處理:

          Javascript提供了兩種處理錯(cuò)誤的方式:

          • BOM包含一個(gè)onerror事件處理函數(shù),該函數(shù)通常被綁定在window對(duì)象或image對(duì)象上;
          • ECMAscript定義了try…catch結(jié)構(gòu)來(lái)處理異常;

          onerror事件處理函數(shù):

          window對(duì)象的onerror屬性是一個(gè)事件處理程序,頁(yè)面上出現(xiàn)異常時(shí),error事件便在window對(duì)象上觸發(fā),并把錯(cuò)誤消息輸出到Javascript控制臺(tái)上,這種方式也稱(chēng)為全局錯(cuò)誤捕獲;如:

          window.onload=function(){
              show(); // 在onload事件中調(diào)用了一個(gè)不存在的函數(shù)
          }
          window.onerror=function(){
              alert("出現(xiàn)錯(cuò)誤");
              return true;
          }

          獲取錯(cuò)誤信息:

          window.onerror事件處理程序在調(diào)用時(shí)可以傳5個(gè)參數(shù),由這5個(gè)參數(shù)可以獲取詳細(xì)的錯(cuò)誤信息;

          • message:錯(cuò)誤信息,描述錯(cuò)誤的一條消息;
          • URL:引發(fā)錯(cuò)誤的Javascript所在的文檔的URL;
          • line:文檔中發(fā)生錯(cuò)誤的行數(shù);
          • column:發(fā)生錯(cuò)誤的列數(shù);
          • error:錯(cuò)誤對(duì)象,這個(gè)error也稱(chēng)為全局錯(cuò)誤對(duì)象;
          window.onerror=function(sMessage, sUrl, sLine, sColumn, error){
          console.log("Error:" + sMessage + " URL:" + sUrl + " Line:" + sLine + " Column:" + sColumn);
          console.log(error);
              return true;
          }

          onerror處理程序的返回值:

          如果返回true,則阻止執(zhí)行默認(rèn)的事件處理程序,也就是將通知瀏覽器,事件處理程序已經(jīng)處理了錯(cuò)誤,不需要其他操作,反之會(huì)顯示錯(cuò)誤消息;

          某些元素也支持onerror; 但其處理函數(shù)沒(méi)有任何關(guān)于error信息的參數(shù),如:

          document.images[0].onerror=function(event){
          console.log(event);  // Event
          console.log(event.type);  // error
          }

          這里的event參數(shù)是一個(gè)類(lèi)型為Event事件對(duì)象,其存儲(chǔ)的信息除了type返回了error,并沒(méi)有其他和錯(cuò)誤相關(guān)的信息;

          全局錯(cuò)誤處理window.onerror通常不能恢復(fù)腳本繼續(xù)執(zhí)行,但會(huì)給開(kāi)發(fā)者發(fā)送錯(cuò)誤信息;

          window.onerror=function(error){
            console.log(error);
          }
          show();
          console.log("中止,不會(huì)被執(zhí)行");
          window.onload=function(){
            console.log("也不會(huì)被執(zhí)行");
          }

          可以是簡(jiǎn)單的打印,也可以把錯(cuò)誤保存到日志記錄里;

          window.onerror就是綁定在window對(duì)象的error事件,也可以使用標(biāo)準(zhǔn)的添加事件偵聽(tīng)的方式window.addEventListener(eventtype, handler),其需要兩個(gè)參數(shù),eventtype為事件類(lèi)型,在此為error,handler是事件處理函數(shù),其需要一個(gè)參數(shù)event,一個(gè)ErrorEvent類(lèi)型的對(duì)象,其保存著有關(guān)事件和錯(cuò)誤的所有信息,如:

          window.addEventListener("error", function(event){
            console.log(event);  // ErrorEvent
            console.log(event.error);  // Error對(duì)象
            console.log(event.error.name);
            console.log(event.error.message);
            console.log(event.error.stack);
            console.log(event.lineno);  // 行
            console.log(event.colno);  // 列
            console.log(event.filename);
          });

          在實(shí)際的開(kāi)發(fā)中,這兩種方式都會(huì)被使用,只不過(guò)addEventListener有定的兼容必問(wèn)題,所以要兼顧所有的瀏覽器且不太關(guān)注事件對(duì)象本身的話,就使用window.onerror;

          當(dāng)加載自不同域的腳本中發(fā)生語(yǔ)法錯(cuò)誤時(shí),為避免信息泄露,語(yǔ)法錯(cuò)誤的細(xì)節(jié)將不會(huì)報(bào)告,只會(huì)返回簡(jiǎn)單的"Script error.";

          <script>
          window.onerror=function(msg, url, lineNo, columnNo, error){
            console.log(msg);  // Script error
            console.log(url);  // ""
            console.log(lineNo);  // 0
            console.log(columnNo);  // 0
            console.log(error);  // null
          }
          </script>
          <script src="https://www.zeronetwork.cn/demo/demo.js"></script>

          可以針對(duì)同域和不同域的錯(cuò)誤分開(kāi)處理,如:

          <script>
          window.onerror=function(msg, url, lineNo, columnNo, error){
            var str_error=msg.toLowerCase();
            var sub_string="script error";
            if(str_error.indexOf(sub_string) > -1)
              alert("腳本發(fā)生錯(cuò)誤,詳情請(qǐng)?jiān)诳刂婆_(tái)查看");
            else{
              var message=[
                '消息:' + msg,
                'URL:' + url,
                '行:' + lineNo,
                '列:' + columnNo,
                '錯(cuò)誤對(duì)象:' + error
              ].join(" - ");
              alert(message);
            }
          }
          show();
          </script>
          <script src="https://www.zeronetwork.cn/demo/demo.js"></script>

          從上在的執(zhí)行結(jié)果來(lái)看,error事件執(zhí)行了兩次,原因是使用了兩個(gè)script,也就是當(dāng)一個(gè)script有錯(cuò)誤發(fā)生時(shí),它只會(huì)阻止當(dāng)前的script塊,而不會(huì)阻止其他的script塊;如:

          <script>
          show();  // 會(huì)捕獲
          console.log("不會(huì)被執(zhí)行");
          myshow();  // 不會(huì)捕獲
          </script>
          <script src="https://www.zeronetwork.cn/demo/demo.js"></script>
          <script>
          console.log("執(zhí)行了");
          demo();  // 會(huì)捕獲
          console.log("不會(huì)被執(zhí)行");
          </script>

          body元素的onerror特性,也可以充當(dāng)事件處理函數(shù),如:

          <body onerror="alert('出現(xiàn)了錯(cuò)誤');return true;">

          注意,先注釋掉window.onerror等代碼;

          此時(shí),可以直接使用event、source、lineno、colno、error等屬性;

          <body onerror="alert(event + '\n' + source + '\n' + lineno + '\n' + colno + '\n' + error);return true;">

          當(dāng)然了,也可以為body綁定error事件,此時(shí)各屬性,必須明確指定,如:

          document.body.onerror=function(msg, url,lineno,colno,error){
            alert(msg + '\n' + url + '\n' + lineno + '\n' + colno + '\n' + error);
            return true;
          }

          try-catch語(yǔ)句:

          try語(yǔ)句中為期待正常執(zhí)行的代碼塊,當(dāng)在try語(yǔ)句中發(fā)生錯(cuò)誤,其余代碼會(huì)中止執(zhí)行,catch語(yǔ)句就處理該錯(cuò)誤,如果沒(méi)有錯(cuò)誤,就跳過(guò)catch語(yǔ)句;try和catch必須成對(duì)出現(xiàn);

          try{
              //code
              [break]
          }catch([exception]){
              //code
          }[finally]{
              //code
          }
          // 如
          try {
              show();
              alert("不能執(zhí)行");
          } catch (error) {
              alert("出現(xiàn)一個(gè)錯(cuò)誤:" + error);
          } finally{
              alert("管你呢");
          }

          try語(yǔ)句塊內(nèi)的錯(cuò)誤只會(huì)中止try語(yǔ)句塊中發(fā)生錯(cuò)誤之后的邏輯代碼,并不會(huì)中止整個(gè)腳本的運(yùn)行;執(zhí)行try-catch語(yǔ)句,必須是運(yùn)行時(shí),運(yùn)行時(shí)錯(cuò)誤,也被稱(chēng)為異常;try-catch語(yǔ)句中指定只能有一個(gè)catch子句;try-catch語(yǔ)句適合處理無(wú)法預(yù)知、無(wú)法控制的錯(cuò)誤;finally常被用于無(wú)論結(jié)果是否有異常,都要執(zhí)行的代碼,如:

          try{
            alert("try");
            show();
            alert("no exec");
          }catch(error){
            alert("catch");
          }finally{
            alert("finally");
          }
          alert("continute");

          代碼執(zhí)行的兩條路徑:如果沒(méi)有異常,執(zhí)行路徑為:try->finally,反之為:try的部分->catch->finally;

          一般用于關(guān)閉打開(kāi)的鏈接和釋放資源;

          var connection={open: function(){},close: function(){},send: function(data){}}
          // var data="大師哥王唯"; // 注釋這一行,讓它產(chǎn)生異常
          connection.open();
          try{
              connection.send(data);
          }catch(exception){
              console.log("出現(xiàn)一個(gè)錯(cuò)誤");
          }finally{
              connection.close();
              console.log("關(guān)閉了");
          }

          還有一個(gè)典型的應(yīng)用,讀寫(xiě)文件,如:

          function openFile(){};
          function writeFile(data){};
          function closeFile(){};
          openFile();
          try{
            writeFile();
          }catch(error){
            console.log(error);
          }finally{
            closeFile();
          }

          在try-catch-finally語(yǔ)句塊中的變量是全局變量:

          try{
            var name="王唯";
            show();
            var city="蚌埠";
          }catch(error){
            var age=18;
            console.log(name);
          }finally{
            var sex="男";
            console.log(name);
            console.log(age);
          }
          console.log(name);
          console.log(city);
          console.log(age);
          console.log(sex);

          try-catch-finally與return:

          如果直接在try-catch-finally語(yǔ)句塊中執(zhí)行return,會(huì)拋出異常,如:

          try {
            console.log("try");
            // return;  // Illegal return statement 非法返回語(yǔ)句
            console.log("try agin");
          } catch (error) {
            console.log(error);
            // return;  // Illegal return statement
          }finally{
            console.log("finally");
            // return;  // Illegal return statement
          }

          如:

          function foo(){
            try {
              console.log("try");
              return 1;
              show();
              console.log("try agin");
            } catch (error) {
              console.log(error);
              return 2;
            }finally{
              console.log("finally");
              return 3;
            }
          }
          console.log(foo());  // 3

          try-finally:

          沒(méi)有catch從句,只有try-finally也可以,目的是,只確保執(zhí)行開(kāi)始和最終的過(guò)程,而不處理錯(cuò)誤,如:

          try{
            console.log("try");
            show();
          }finally{
            console.log("finally"); // 會(huì)執(zhí)行
          }
          console.log("over"); // 不會(huì)執(zhí)行,已中止

          但此時(shí),還是會(huì)拋出異常的,但此時(shí),會(huì)在執(zhí)行完finally后中止執(zhí)行,并會(huì)查找外部的catch語(yǔ)句;

          嵌套try-catch語(yǔ)句:

          在catch子句中,也有可能會(huì)發(fā)生錯(cuò)誤,所以就可以使用嵌套的try-catch語(yǔ)句,如:

          try {
              show();
              console.log("不能執(zhí)行");
          } catch (error) {
              console.log("出現(xiàn)一個(gè)錯(cuò)誤:" + error);
              try {
                  var arr=new Array(10000000000000000);
                  arr.push(error);
              } catch (error) {
                  console.log("又出現(xiàn)了一個(gè)錯(cuò)誤:" + error);
              }
          } finally{
              console.log("管你呢");
          }

          也可以在try中嵌套try-catch-finally語(yǔ)句,如:

          try{
            try{
              console.log("try");
              show();
            }catch(error){
              console.log("error");
            }finally{
              console.log("finally");
            }
          }catch(error){
            console.log(error);
          }

          一個(gè)比較典型的應(yīng)用,就是處理json數(shù)據(jù),如:

          // var json='{"name":"wangwei", "age": 18, "sex": "男"}';
          var json='{bad json}';  // Uncaught SyntaxError
          var data=JSON.parse(json);
          console.log(data.name);
          console.log(data.age);
          console.log(data.sex);

          一量json數(shù)據(jù)發(fā)生錯(cuò)誤,整個(gè)應(yīng)用都會(huì)崩潰,所以應(yīng)該使用try-catch,如:

          <div id="msg">您的信息:</div>
          <script>
          window.onload=function(){
            var msg=document.getElementById("msg");
            try{
              // var json='{"name":"王唯", "age": 18, "sex": "男"}';
              var json='{bad json}';  // Uncaught SyntaxError
              var data=JSON.parse(json);
              msg.innerHTML +="姓名:" + data.name + ",年齡:" + data.age + ",性別:" + data.sex;
            }catch(error){
              msg.innerHTML="開(kāi)小差了,找不到你的信息";
            }
          }
          </script>

          當(dāng)使用了try-catch語(yǔ)句,就不會(huì)將錯(cuò)誤提交給瀏覽器,也就不會(huì)觸發(fā)error事件,如:

          window.onerror=function(error){
            console.log(error);  // 不會(huì)觸發(fā)
          }
          try{
            show();
          }catch(error){
            console.log(error);
          }

          Error錯(cuò)誤對(duì)象:

          在catch中會(huì)捕獲一個(gè)Error錯(cuò)誤對(duì)象;該對(duì)象在Javascript解析或運(yùn)行時(shí),一旦發(fā)生錯(cuò)誤,引擎就會(huì)拋出這個(gè)對(duì)象;如果沒(méi)有相關(guān)聯(lián)的try-catch捕獲該對(duì)象,就由瀏覽器輸出這個(gè)對(duì)象;

          // ...
          console.log("錯(cuò)誤:" + error + " name:" + error.name + " message:" + error.message);

          也可以通過(guò)Error的構(gòu)造器創(chuàng)建一個(gè)錯(cuò)誤對(duì)象,這個(gè)Error對(duì)象也可用于用戶(hù)自定義的異常;語(yǔ)法:new Error([message[, filename[, lineNumber]]]);

          • message:可選,錯(cuò)誤描述信息;
          • fileName:可選,非標(biāo)準(zhǔn),被創(chuàng)建的Error對(duì)象的fileName屬性值,默認(rèn)是調(diào)用Error構(gòu)造器代碼所在的文件的名字; 但大部分瀏覽器沒(méi)有實(shí)現(xiàn);
          • lineNumber:可選,非標(biāo)準(zhǔn),被創(chuàng)建的Error對(duì)象的lineNumber屬性值,默認(rèn)是調(diào)用Error構(gòu)造器代碼所在的文件的行號(hào);但大部分瀏覽器沒(méi)有實(shí)現(xiàn);

          實(shí)例化Error對(duì)象,也可以不使用new關(guān)鍵字,如:

          var error=new Error("自定義錯(cuò)誤對(duì)象");
          var error=Error("不使用new");
          console.log(error);
          console.log(error.name);  // Error
          console.log(error.message);  // 自定義錯(cuò)誤對(duì)象

          Error錯(cuò)誤對(duì)象屬性:

          • name:表示錯(cuò)誤類(lèi)型的字符串;
          • message:實(shí)際的錯(cuò)誤信息;

          Error類(lèi)還有6個(gè)子類(lèi),其可以通過(guò)錯(cuò)誤對(duì)象的name屬性返回具體異常類(lèi)的名稱(chēng):

          • EvalError:錯(cuò)誤發(fā)生在eval()函數(shù)中;
          • RangeError:數(shù)值超出javascript可表示的范圍;;
          • ReferenceError:使用了非法或不能識(shí)別的引用;
          • SyntaxError:發(fā)生了語(yǔ)法錯(cuò)誤;
          • TypeError:操作數(shù)的類(lèi)型不是預(yù)期所需的;
          • URIError:在encodeURI()或decodeURI()函數(shù)中發(fā)生了錯(cuò)誤;
          // EvalError
          try{
            throw new EvalError("Eval異常");
          }catch(error){
            console.log(error);
            console.log(error instanceof EvalError);  // true
            console.log(error.name);  // EvalError
            console.log(error.message);  // Eval異常
          }
          // RangeError
          var num=1;
          try{
            num.toPrecision(500);  //  [pr??s??n] 精度
          }catch(error){
            console.log(error); 
          }
          // ReferenceError
          var x;
          try {
            x=y + 1;
          } catch (error) {
            console.log(error);
          }
          // SyntaxError
          try{
            eval("alert('wangwei)");
          }catch(error){
            console.log(error);
          }
          // TypeError
          var num=1;
          try{
            num.toUpperCase(); // 無(wú)法將數(shù)字轉(zhuǎn)為大寫(xiě)
          }catch(error){
            console.log(error);
          }
          // URIError (malformed [?m?l?f??md]格式不正確,畸形的)
          try{
            decodeURI("%%%"); // 使用了非法字符
          }catch(error){
            console.log(error);
          }

          Error對(duì)象的message屬性是瀏覽器生成的用于表示錯(cuò)誤描述的信息,因?yàn)檫@個(gè)屬性是特定于瀏覽器的,所以不同的瀏覽器上可能產(chǎn)生不同的錯(cuò)誤信息,如:

          try {
              eval("a ++ b");
              show();
              console.log("執(zhí)行了嗎?");
          } catch (error) {
              // SyntaxError:Unexpected identifier或
              // SyntaxError:unexpected token: identifier
              console.log(error.name + ":" + error.message);
          }

          使用name屬性判斷錯(cuò)誤類(lèi)型:

          try {
              eval("a++b");
          } catch (error) {
              console.log(error instanceof SyntaxError); // true
              if(error.name=="SyntaxError")
                  console.log(error.name + ":" + error.message);
              else
                  console.log("未知錯(cuò)誤:" + error.message);
          }

          拋出異常:

          throw語(yǔ)句的作用是手動(dòng)中斷程序執(zhí)行,拋出一個(gè)錯(cuò)誤,一般用于有目的的拋出異常,也就是允許開(kāi)發(fā)者可以創(chuàng)建自定義錯(cuò)誤;

          throw可以拋出任何類(lèi)型的值,也就是說(shuō),它的參數(shù)可以是任何值;語(yǔ)法:throw error_object;error_object可以是字符串、數(shù)字、布爾或?qū)ο螅?/p>

          throw "出現(xiàn)一個(gè)錯(cuò)誤";
          throw 50666;
          throw true;
          throw new Object();
          throw {
              toString:function(){
                  return 'Error!';
              }
          }
          function getRectArea(width, height){
            if(isNaN(width) || isNaN(height))
              throw '參數(shù)應(yīng)該是number類(lèi)型';
            return width * height;
          }
          getRectArea("wangwei",10);

          對(duì)于Javascript引擎來(lái)說(shuō),只要遇到throw語(yǔ)句,程序就會(huì)終止;

          也可以拋出一個(gè)Error錯(cuò)誤對(duì)象;Error對(duì)象的構(gòu)造函數(shù)只有一個(gè)參數(shù),

          throw new Error("請(qǐng)?jiān)俅螄L試");

          其他Error子類(lèi)對(duì)象也可以拋出:

          throw new SyntaxError("...");
          throw new TypeError("...");
          throw new RangeError("...");
          throw new EvalError("...");
          throw new URIError("...");
          throw new ReferenceError("...");

          對(duì)于Error類(lèi)和其子類(lèi)來(lái)說(shuō),錯(cuò)誤對(duì)象的name就是其構(gòu)造函數(shù)的名稱(chēng),message是其構(gòu)造函數(shù)的參數(shù);

          當(dāng)拋出異常后,throw之后的語(yǔ)句將不會(huì)執(zhí)行,并跳到相關(guān)聯(lián)的catch語(yǔ)句中進(jìn)行處理,如:

          <h1>請(qǐng)輸入18-99之間的數(shù)字</h1>
          <input id="txtInput" type="text" />
          <button id="btn">確定</button>
          <p id="msg"></p>
          <script>
          function myFun(){
            var msg,x;
            msg=document.getElementById("msg");
            msg.innerHTML="";
            x=document.getElementById("txtInput").value;
            try{
              if(x=="") throw "空的";
              if(isNaN(x)) throw "不是數(shù)字";
              x=Number(x);
              if(x < 18) throw "太小";
              if(x > 99) throw "太大";
              msg.innerHTML="輸入的值正確:" + String(x);
            }
            catch(error){
              msg.innerHTML="輸入的值不正確:" + error; 
            }
          }
          var btn=document.getElementById("btn");
          btn.onclick=myFun;
          </script>

          也可以在某個(gè)語(yǔ)句塊的外部捕獲throw異常,如:

          function sum(a,b){
              if(arguments.length < 2)
                  throw new Error("需要兩個(gè)參數(shù)");
              else
                  return a + b;
          }
          try{
              console.log(sum(18));
          }catch(error){
              // Error:需要兩個(gè)參數(shù)
              console.log(error.name + ":" + error.message);
          }

          可以通過(guò)instanceof判斷異常的類(lèi)型來(lái)特定處理某一類(lèi)的異常,例如可以區(qū)分瀏覽器拋出的異常和開(kāi)發(fā)人員拋出的異常,如:

          function sum(a,b){
              if(arguments.length < 2)
                throw new Error("需要兩個(gè)參數(shù)");
              if(isNaN(a) || isNaN(b))
                throw "參數(shù)是不是Number類(lèi)型";
              return a + b;
          }
          try{
              console.log(sum(18,12));
          }catch(error){
            if(error instanceof SyntaxError)
              console.log("語(yǔ)法錯(cuò)誤:" + error.name + ":" + error.message);
            else if(error instanceof Error)
              console.log(error.name + ":" + error.message);
            else
                console.log(error);
          }

          注:判斷Error類(lèi)型要放到if的最后一個(gè)條件;

          即使在catch語(yǔ)句中,還可以根據(jù)實(shí)際情況,再次拋出異常,此時(shí),其可以被外部的try-catch語(yǔ)句塊捕獲(如果存在的話);

          當(dāng)發(fā)生異常時(shí),代碼會(huì)立即停止,僅當(dāng)有try-catch語(yǔ)句捕獲到異常時(shí),代碼才會(huì)繼續(xù)執(zhí)行;其背后運(yùn)行的原理是,當(dāng)發(fā)生異常,JavaScript解釋器會(huì)立即停止執(zhí)行的邏輯,并跳轉(zhuǎn)到就近的try-catch異常處理程序,如果發(fā)生異常的代碼塊中沒(méi)有相關(guān)聯(lián)的catch從句,解釋器會(huì)檢查更高層的閉合代碼塊,看它是否有相關(guān)聯(lián)的異常處理程序,以此類(lèi)推,直到找到一個(gè)異常處理程序?yàn)橹梗蝗绻l(fā)生異常的函數(shù)中沒(méi)有處理它的try-catch語(yǔ)句,異常將向上傳播到調(diào)用該函數(shù)的代碼,如此,異常就會(huì)沿著Javascript的語(yǔ)法結(jié)構(gòu)或調(diào)用棧向上傳播;如果沒(méi)有找到任何異常處理程序,JavaScript將把異常當(dāng)成程序錯(cuò)誤來(lái)處理,并通過(guò)瀏覽器報(bào)告給用戶(hù);

          自定義錯(cuò)誤類(lèi)型:

          可以基于Error類(lèi)來(lái)創(chuàng)建自定義的錯(cuò)誤類(lèi)型,此時(shí)可以使用throw拋出自定義的異常類(lèi),或通過(guò)instanceof來(lái)檢查這個(gè)異常類(lèi)的類(lèi)型,新類(lèi)型需要實(shí)現(xiàn)name和message屬性;

          function CustomError(message){
            this.name="CustomError";
            this.message=message || 'Default Message';
            this.stack=(new Error()).stack;
          }
          // CustomError.prototype=new Error();
          // 或者
          CustomError.prototype=Object.create(Error.prototype);
          CustomError.prototype.constructor=CustomError;
          try{
            var name="jingjing";
            if(name !=="wangwei")
              throw new CustomError("自定義的錯(cuò)誤類(lèi)型");
          }catch(error){
            console.log(error.message);
          }

          小示例:

          function UserException(message){
            this.name="UserException";
            this.message=message;
          }
          function getMothName(m){
            m=m - 1;  // 調(diào)整月份數(shù)字到數(shù)組索引(1=Jan,12=Dec)
            var months=['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
            if(months[m] !=undefined)
              return months[m];
            else
              throw new UserException("Invalid Month No");
          }
          try{
            var myMonth=15;
            var monthName=getMothName(myMonth);
          }catch(error){
            var monthName="未知";
            console.log(error.name + ":" + error.message);
          }

          小示例:驗(yàn)證電話號(hào)碼,如:

          function Telephone(num){
            num=String(num);
            var pattern=/^((0\d{2,3}-\d{7,8})|(1[3584]\d{9}))$/;
            if(pattern.test(num)){
              this.value=num.match(pattern)[0];
              this.valueOf=function(){
                return this.value;
              };
              this.toString=function(){
                return String(this.value);
              }
            }else{
              throw new TelephoneFormatException(num);
            }
          }
          function TelephoneFormatException(value){
            this.name="TelephoneFormatException";
            this.message="電話號(hào)碼格式不正確";
            this.value=value;
            this.toString=function(){
              return this.value + ":" + this.message;
            }
          }
          // 應(yīng)用
          var TELEPHONE_INVALID=-1;
          var TELEPHONE_UNKNOWN_ERROR=-2;
          function verifyTelephone(num){
            try{
              num=new Telephone(num);
            }catch(error){
              if(error instanceof TelephoneFormatException)
                return TELEPHONE_INVALID;
              else
                return TELEPHONE_UNKNOWN_ERROR;
            }
            return num.toString();
          }
          console.log(verifyTelephone("010-66668888"));
          console.log(verifyTelephone("13812345678"));
          console.log(verifyTelephone("138123456")); // -1
          console.log(verifyTelephone("wangwei")); // -1

          常見(jiàn)錯(cuò)誤:

          由于javaScript是松散類(lèi)型的,也不會(huì)驗(yàn)證函數(shù)的參數(shù),因此錯(cuò)誤只會(huì)在運(yùn)行時(shí)出現(xiàn);一般來(lái)說(shuō),需要關(guān)注三種錯(cuò)誤:類(lèi)型轉(zhuǎn)換錯(cuò)誤、數(shù)據(jù)類(lèi)型錯(cuò)誤、通信錯(cuò)誤;

          類(lèi)型轉(zhuǎn)換錯(cuò)誤:

          一般發(fā)生在使用某個(gè)操作符,或者使用其他可能自動(dòng)轉(zhuǎn)換值的數(shù)據(jù)類(lèi)型的語(yǔ)言結(jié)構(gòu)時(shí);

          function output(str1,str2,str3){
            var result=str1 + str2;
            if(str3)
              result +=str3;
            return result;
          }
          console.log(output(1,2,3));
          console.log(output(1,2));
          console.log(output(1,2,0));
          console.log(output(1,2,"wangwei"));

          這就是一個(gè)非常典型的與期望不一致的方式;

          數(shù)據(jù)類(lèi)型錯(cuò)誤:

          在流控制語(yǔ)句中使用非布爾值,是極為常見(jiàn)的一個(gè)錯(cuò)誤來(lái)源,為避免此類(lèi)錯(cuò)誤,就要做到在條件比較時(shí)確定傳入的是布爾值,例如,把if語(yǔ)句改成:if(typeof str3=='number');

          所以在使用某個(gè)變量或?qū)ο髸r(shí),一定要適當(dāng)?shù)貦z查它的數(shù)據(jù)類(lèi)型,如:

          function reverseSort(values){
            // if(values){
            //   values.sort();
            //   values.reverse();
            // }
            if(arguments.length > 0){
              if(!Array.isArray(values))
                return [];
              else{
                values.sort();
                values.reverse();
                return values;
              }
            }
            return [];
          }
          var arr=[3,2,6,9,4];
          // var arr=100;  // Uncaught TypeError: values.sort is not a function
          console.log(reverseSort(arr));

          另一個(gè)常見(jiàn)的錯(cuò)誤就是將參數(shù)與null值進(jìn)行比較。與null進(jìn)行比較只能確保相應(yīng)的值不是null和undefined。要確保傳入的值有效,僅檢測(cè)null值是不夠的;

          function reverseSort(values){
            // if(values !=null){  // 任何非數(shù)組值都會(huì)導(dǎo)致錯(cuò)誤
            if(values instanceof Array) // 非數(shù)組值被忽略
              values.sort();
              values.reverse();
            }
            return values;
          }
          var arr=[3,2,6,9,4];
          // var arr=100;  // Uncaught TypeError: values.sort is not a function
          console.log(reverseSort(arr));
          // 或
          function reverseSort(values, fun){
            if(values instanceof Array){
              if(fun !=null && typeof fun==="function")
                values.sort(fun);
              else
                values.sort();
            }
            return values;
          }
          var arr=[3,2,6,9,4];
          console.log(reverseSort(arr, function(a,b){
            return a > b ? -1 : 1;
          }));

          通信錯(cuò)誤:最典型的就是Ajax應(yīng)用,用其可以動(dòng)態(tài)加載信息,但是,javascript與服務(wù)器之間的任何一次通信,都有可能會(huì)產(chǎn)生錯(cuò)誤;

          調(diào)試技巧:

          使用警告框: 這是最簡(jiǎn)單、流行的方式,如:

          function test(){
              alert("函數(shù)內(nèi)");
              var iNum1=5, iNum2=10;
              alert(iNum1);
              var iResult=iNum1 + iNum2;
              alert(iResult);
          }
          test();

          拋出自定義錯(cuò)誤:

          function assert(bCondition, sErrorMessage){
              if(!bCondition)
                  throw new Error(sErrorMessage);
          }
          function divide(iNum1, iNum2){
              assert(arguments.length==2, "divide需要兩個(gè)參數(shù)");
              assert((!isNaN(iNum1) && !isNaN(iNum2)), "需要Number類(lèi)型");
              return iNum1 / iNum2;
          }
          console.log(divide(10,2));
          console.log(divide(10,"c"));  // 異常
          // 或
          try{
            console.log(divide(10,"c"));
          }catch(error){
            console.log(error.name + ":" + error.message);
          }

          Javascript校驗(yàn)器:

          jslint的主要目的是指出不合規(guī)范的js語(yǔ)法和可能的語(yǔ)法錯(cuò)誤,包括一些不良代碼;官網(wǎng):http://www.jslint.com/

          如以下,會(huì)給出警告:

          • 語(yǔ)句未使用塊標(biāo)記;
          • 一行的結(jié)尾未以分號(hào)結(jié)束;
          • 用var聲明一個(gè)已在使用的變量;
          • with語(yǔ)句;

          調(diào)試器:

          Javascript自身不具備調(diào)試器,但目前所有的瀏覽器可以使用自身的調(diào)試器;

          IE調(diào)試:

          啟用IE的調(diào)試功能:

          菜單“工具”|“Internet選項(xiàng)”命令,打開(kāi)“Internet選項(xiàng)”對(duì)話框,在“高級(jí)”選項(xiàng)卡中,找到兩個(gè)“禁用腳本調(diào)試”復(fù)選框并取消;開(kāi)始調(diào)試,調(diào)試的主要工作是反復(fù)地跟蹤代碼,找出錯(cuò)誤并修正;

          設(shè)置斷點(diǎn):

          在調(diào)試程序窗口中,將光標(biāo)移動(dòng)到需要添加斷點(diǎn)的行上,按一次F9鍵或單擊,當(dāng)前行的背景色變?yōu)榧t色,并且在窗口左邊界上標(biāo)上紅色的圓點(diǎn),當(dāng)程序運(yùn)行到斷點(diǎn)時(shí)就會(huì)暫停;

          運(yùn)行調(diào)試:

          單擊繼續(xù)或按F5進(jìn)行逐步運(yùn)行調(diào)試;F10步進(jìn)、F11步入,都可以繼續(xù)向下執(zhí)行;將鼠標(biāo)移動(dòng)到變量名上時(shí),會(huì)顯示變量當(dāng)前時(shí)刻的值;或者在右側(cè)的“監(jiān)視”窗格中可以觀察該變量的值;點(diǎn)擊變量信息框中的變量值或右側(cè)“監(jiān)視”空格中的變量值可以修改變量的當(dāng)前值;更多的調(diào)試操作:查看調(diào)用關(guān)系、監(jiān)視特定變量的值等;

          // 示例
          var balance=200.0;    //
          var willPay=20.0;
          function pay(_balance, _pay){
              return _balance - _pay;
          }
          function showBalance(){
              debugger;
              var blnc=pay(balance,willPay);
              alert("當(dāng)前余額:" + blnc);
          }
          showBalance();

          日志輸出:

          程序運(yùn)行時(shí),有些中間數(shù)據(jù)需要記錄,以便檢查程序運(yùn)行的狀態(tài);對(duì)于JavaScript記錄中間數(shù)據(jù)通常是以日志的形式記錄需要記錄的數(shù)據(jù),再發(fā)送到服務(wù)器上保存起來(lái);日志記錄的內(nèi)容可以是任意的信息,根據(jù)開(kāi)發(fā)者的需要而定;


          主站蜘蛛池模板: 人妻体内射精一区二区| 国模无码一区二区三区不卡| 精品一区二区三区四区在线播放| 国产一区二区三区在线免费 | 色噜噜狠狠一区二区三区| 色噜噜狠狠一区二区三区果冻 | 一区五十路在线中出| 国产萌白酱在线一区二区| 国产亚洲综合精品一区二区三区| 精品福利一区3d动漫| 亚洲中文字幕丝袜制服一区 | 亚洲一区二区三区无码中文字幕| 一区二区视频免费观看| 国语精品一区二区三区| 精品无码一区二区三区爱欲九九| 精品人伦一区二区三区潘金莲| 日本一区二区三区在线观看| AA区一区二区三无码精片| 四虎成人精品一区二区免费网站| 国产精品亚洲综合一区在线观看| 福利片福利一区二区三区| 国产成人无码一区二区三区在线| 精品无码AV一区二区三区不卡| 成人免费一区二区无码视频| 精品成人一区二区三区免费视频 | 久久精品亚洲一区二区| 亚洲综合一区无码精品| 一区二区高清在线观看| 亚洲乱色熟女一区二区三区丝袜 | 国产裸体舞一区二区三区| 中文字幕一区视频一线| 精品欧洲av无码一区二区三区| 日韩精品一区二三区中文| 亚洲国产老鸭窝一区二区三区| 精品国产一区二区三区在线| 国产精品无码一区二区三区电影| 国产高清一区二区三区| 欧洲精品无码一区二区三区在线播放 | 亚洲一区二区三区久久| 国产精品免费一区二区三区 | 中文字幕av一区|