ImageVerifierCode 换一换
格式:DOCX , 页数:15 ,大小:44.61KB ,
资源ID:8222659      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/8222659.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(深入理解JavaScript Errors和Stack Traces.docx)为本站会员(b****6)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

深入理解JavaScript Errors和Stack Traces.docx

1、深入理解JavaScript Errors和Stack Traces深入理解JavaScript Errors和Stack Traces这次我们聊聊 Errors 和 Stack traces 以及如何熟练地使用它们。很多同学并不重视这些细节,但是这些知识在你写 Testing 和 Error 相关的 lib 的时候是非常有用的。使用 Stack traces 可以清理无用的数据,让你关注真正重要的问题。同时,你真正理解 Errors 和它们的属性到底是什么的时候,你将会更有信心的使用它们。这篇文章在开始的时候看起来比较简单,但当你熟练运用 Stack trace 以后则会感到非常复杂。所以在

2、看难的章节之前,请确保你理解了前面的内容。一、Stack是如何工作的在我们谈到 Errors 之前,我们必须理解 Stack 是如何工作的。它其实非常简单,但是在开始之前了解它也是非常必要的。如果你已经知道了这些,可以略过这一章节。每当有一个函数调用,就会将其压入栈顶。在调用结束的时候再将其从栈顶移出。这种有趣的数据结构叫做“最后一个进入的,将会第一个出去”。这就是广为所知的 LIFO(后进先出)。举个例子,在函数 x 的内部调用了函数 y,这时栈中就有个顺序先 x 后 y。我再举另外一个例子,看下面代码:1. functionc()2. console.log(c);3. 4. 5. fun

3、ctionb()6. console.log(b);7. c();8. 9. 10. functiona()11. console.log(a);12. b();13. 14. 15. a();上面的这段代码,当运行 a 的时候,它会被压到栈顶。然后,当 b 在 a 中被调用的时候,它会被继续压入栈顶,当 c 在 b 中被调用的时候,也一样。在运行 c 的时候,栈中包含了 a,b,c,并且其顺序也是 a,b,c。当 c 调用完毕时,它会被从栈顶移出,随后控制流回到 b。当 b 执行完毕后也会从栈顶移出,控制流交还到 a。最后,当 a 执行完毕后也会从栈中移出。为了更好的展示这样一种行为,我们用

4、 console.trace() 来将 Stack trace 打印到控制台上来。通常我们读 Stack traces 信息的时候是从上往下读的。1. functionc()2. console.log(c);3. console.trace();4. 5. 6. functionb()7. console.log(b);8. c();9. 10. 11. functiona()12. console.log(a);13. b();14. 15. 16. a();当我们在 Node REPL 服务端执行的时候,会返回如下:1. Trace2. atc(repl:3:9)3. atb(repl:

5、3:1)4. ata(repl:3:1)5. atrepl:1:1/-Fornowfeelfreetoignoreanythingbelowthispoint,theseareNodesinternals6. atrealRunInThisContextScript(vm.js:22:35)7. atsigintHandlersWrap(vm.js:98:12)8. atContextifyScript.Script.runInThisContext(vm.js:24:12)9. atREPLServer.defaultEval(repl.js:313:29)10. atbound(domai

6、n.js:280:14)11. atREPLServer.runBoundaseval(domain.js:293:12)从上面我们可以看到,当栈信息从 c 中打印出来的时候,我看到了 a,b 和 c。现在,如果在 c 执行完毕以后,在 b 中把 Stack trace 打印出来,我们可以看到 c 已经从栈中移出了,栈中只有 a 和 b。1. functionc()2. console.log(c);3. 4. 5. functionb()6. console.log(b);7. c();8. console.trace();9. 10. 11. functiona()12. console.

7、log(a);13. b();14. 15. 16. a();下面可以看到,c 已经不在栈中了,在其执行完以后,从栈中 pop 出去了。1. Trace2. atb(repl:4:9)3. ata(repl:3:1)4. atrepl:1:1/-Fornowfeelfreetoignoreanythingbelowthispoint,theseareNodesinternals5. atrealRunInThisContextScript(vm.js:22:35)6. atsigintHandlersWrap(vm.js:98:12)7. atContextifyScript.Script.r

8、unInThisContext(vm.js:24:12)8. atREPLServer.defaultEval(repl.js:313:29)9. atbound(domain.js:280:14)10. atREPLServer.runBoundaseval(domain.js:293:12)11. atREPLServer.onLine(repl.js:513:10)概括一下:当调用时,压入栈顶。当它执行完毕时,被弹出栈,就是这么简单。二、Error 对象和 Error 处理当 Error 发生的时候,通常会抛出一个 Error 对象。Error 对象也可以被看做一个 Error 原型,用

9、户可以扩展其含义,以创建自己的 Error 对象。Error.prototype 对象通常包含下面属性: constructor - 一个错误实例原型的构造函数 message - 错误信息 name - 错误名称这几个都是标准属性,有时不同编译的环境会有其独特的属性。在一些环境中,例如 Node 和 Firefox,甚至还有 stack 属性,这里面包含了错误的 Stack trace。一个 Error 的堆栈追踪包含了从其构造函数开始的所有堆栈帧。如果你想要学习一个 Error 对象的特殊属性,我强烈建议你看一下在MDN上的这篇文章。要抛出一个 Error,你必须使用 throw 关键字。

10、为了 catch 一个抛出的 Error,你必须把可能抛出 Error 的代码用 try 块包起来。然后紧跟着一个 catch 块,catch 块中通常会接受一个包含了错误信息的参数。和在 Java 中类似,不论在 try 中是否抛出 Error, JavaScript 中都允许你在 try/catch 块后面紧跟着一个 finally 块。不论你在 try 中的操作是否生效,在你操作完以后,都用 finally 来清理对象,这是个编程的好习惯。介绍到现在的知识,可能对于大部分人来说,都是已经掌握了的,那么现在我们就进行更深入一些的吧。使用 try 块时,后面可以不跟着 catch 块,但是必

11、须跟着 finally 块。所以我们就有三种不同形式的 try 语句: try.catch try.finally try.catch.finallyTry 语句也可以内嵌在一个 try 语句中,如:1. try2. try3. /这里抛出的Error,将被下面的catch获取到4. thrownewError(Nestederror.);5. catch(nestedErr)6. /这里会打印出来7. console.log(Nestedcatch);8. 9. catch(err)10. console.log(Thiswillnotrun.);11. 你也可以把 try 语句内嵌在 ca

12、tch 和 finally 块中:1. try2. thrownewError(Firsterror);3. catch(err)4. console.log(Firstcatchrunning);5. try6. thrownewError(Seconderror);7. catch(nestedErr)8. console.log(Secondcatchrunning.);9. 10. 1. try2. console.log(Thetryblockisrunning.);3. finally4. try5. thrownewError(Errorinsidefinally.);6. ca

13、tch(err)7. console.log(Caughtanerrorinsidethefinallyblock.);8. 9. 这里给出另外一个重要的提示:你可以抛出非 Error 对象的值。尽管这看起来很炫酷,很灵活,但实际上这个用法并不好,尤其在一个开发者改另一个开发者写的库的时候。因为这样代码没有一个标准,你不知道其他人会抛出什么信息。这样的话,你就不能简单的相信抛出的 Error 信息了,因为有可能它并不是 Error 信息,而是一个字符串或者一个数字。另外这也导致了如果你需要处理 Stack trace 或者其他有意义的元数据,也将变的很困难。例如给你下面这段代码:1. func

14、tionrunWithoutThrowing(func)2. try3. func();4. catch(e)5. console.log(Therewasanerror,butIwillnotthrowit.);6. console.log(Theerrorsmessagewas:+e.message)7. 8. 9. 10. functionfuncThatThrowsError()11. thrownewTypeError(IamaTypeError.);12. 13. 14. runWithoutThrowing(funcThatThrowsError);这段代码,如果其他人传递一个带

15、有抛出 Error 对象的函数给 runWithoutThrowing 函数的话,将完美运行。然而,如果他抛出一个 String 类型的话,则情况就麻烦了。1. functionrunWithoutThrowing(func)2. try3. func();4. catch(e)5. console.log(Therewasanerror,butIwillnotthrowit.);6. console.log(Theerrorsmessagewas:+e.message)7. 8. 9. 10. functionfuncThatThrowsString()11. throwIamaString

16、.;12. 13. 14. runWithoutThrowing(funcThatThrowsString);可以看到这段代码中,第二个 console.log 会告诉你这个 Error 信息是 undefined。这现在看起来不是很重要,但是如果你需要确定是否这个 Error 中确实包含某个属性,或者用另一种方式处理 Error 的特殊属性,那你就需要多花很多的功夫了。另外,当抛出一个非 Error 对象的值时,你没有访问 Error 对象的一些重要的数据,比如它的堆栈,而这在一些编译环境中是一个非常重要的 Error 对象属性。Error 还可以当做其他普通对象一样使用,你并不需要抛出它。

17、这就是为什么它通常作为回调函数的第一个参数,就像 fs.readdir 函数这样:1. constfs=require(fs);2. 3. fs.readdir(/example/i-do-not-exist,functioncallback(err,dirs)4. if(errinstanceofError)5. /readdir将会抛出一个异常,因为目录不存在6. /我们可以在我们的回调函数中使用Error对象7. console.log(ErrorMessage:+err.message);8. console.log(See?WecanuseErrorswithoutusingtrys

18、tatements.);9. else10. console.log(dirs);11. 12. );最后,你也可以在 promise 被 reject 的时候使用 Error 对象,这使得处理 promise reject 变得很简单。1. newPromise(function(resolve,reject)2. reject(newError(Thepromisewasrejected.);3. ).then(function()4. console.log(Iamanerror.);5. ).catch(function(err)6. if(errinstanceofError)7.

19、console.log(Thepromisewasrejectedwithanerror.);8. console.log(ErrorMessage:+err.message);9. 10. );三、使用 Stack Traceok,那么现在,你们所期待的部分来了:如何使用堆栈追踪。这一章专门讨论支持 Error.captureStackTrace 的环境,如:NodeJS。Error.captureStackTrace 函数的第一个参数是一个 object 对象,第二个参数是一个可选的 function。捕获堆栈跟踪所做的是要捕获当前堆栈的路径(这是显而易见的),并且在 object 对象上

20、创建一个 stack 属性来存储它。如果提供了第二个 function 参数,那么这个被传递的函数将会被看成是本次堆栈调用的终点,本次堆栈跟踪只会展示到这个函数被调用之前。我们来用几个例子来更清晰的解释下。我们将捕获当前堆栈路径并且将其存储到一个普通 object 对象中。1. constmyObj=;2. 3. functionc()4. 5. 6. functionb()7. /这里存储当前的堆栈路径,保存到myObj中8. Error.captureStackTrace(myObj);9. c();10. 11. 12. functiona()13. b();14. 15. 16. /首

21、先调用这些函数17. a();18. 19. /这里,我们看一下堆栈路径往myObj.stack中存储了什么20. console.log(myObj.stack);21. 22. /这里将会打印如下堆栈信息到控制台23. /atb(repl:3:7)-SinceitwascalledinsideB,theBcallisthelastentryinthestack24. /ata(repl:2:1)25. /atrepl:1:1-Nodeinternalsbelowthisline26. /atrealRunInThisContextScript(vm.js:22:35)27. /atsigi

22、ntHandlersWrap(vm.js:98:12)28. /atContextifyScript.Script.runInThisContext(vm.js:24:12)29. /atREPLServer.defaultEval(repl.js:313:29)30. /atbound(domain.js:280:14)31. /atREPLServer.runBoundaseval(domain.js:293:12)32. /atREPLServer.onLine(repl.js:513:10)我们从上面的例子中可以看到,我们首先调用了a(a被压入栈),然后从a的内部调用了b(b被压入栈,

23、并且在a的上面)。在b中,我们捕获到了当前堆栈路径并且将其存储在了 myObj 中。这就是为什么打印在控制台上的只有a和b,而且是下面a上面b。好的,那么现在,我们传递第二个参数到 Error.captureStackTrace 看看会发生什么?1. constmyObj=;2. 3. functiond()4. /这里存储当前的堆栈路径,保存到myObj中5. /这次我们隐藏包含b在内的b以后的所有堆栈帧6. Error.captureStackTrace(myObj,b);7. 8. 9. functionc()10. d();11. 12. 13. functionb()14. c();

24、15. 16. 17. functiona()18. b();19. 20. 21. /首先调用这些函数22. a();23. 24. /这里,我们看一下堆栈路径往myObj.stack中存储了什么25. console.log(myObj.stack);26. 27. /这里将会打印如下堆栈信息到控制台28. /ata(repl:2:1)-Asyoucanseehereweonlygetframesbeforebwascalled29. /atrepl:1:1-Nodeinternalsbelowthisline30. /atrealRunInThisContextScript(vm.js:

25、22:35)31. /atsigintHandlersWrap(vm.js:98:12)32. /atContextifyScript.Script.runInThisContext(vm.js:24:12)33. /atREPLServer.defaultEval(repl.js:313:29)34. /atbound(domain.js:280:14)35. /atREPLServer.runBoundaseval(domain.js:293:12)36. /atREPLServer.onLine(repl.js:513:10)37. /atemitOne(events.js:101:20

26、)当我们传递 b 到 Error.captureStackTraceFunction 里时,它隐藏了 b 和在它以上的所有堆栈帧。这就是为什么堆栈路径里只有a的原因。看到这,你可能会问这样一个问题:“为什么这是有用的呢?”。它之所以有用,是因为你可以隐藏所有的内部实现细节,而这些细节其他开发者调用的时候并不需要知道。例如,在 Chai 中,我们用这种方法对我们代码的调用者屏蔽了不相关的实现细节。四、真实场景中的 Stack Trace 处理正如我在上一节中提到的,Chai 用栈处理技术使得堆栈路径和调用者更加相关,这里是我们如何实现它的。首先,让我们来看一下当一个 Assertion 失败的时候,AssertionError 的构造函数做了什么。1. /ssfi代表起始堆栈函数,它是移除其他不相关堆栈帧的起始标记2. functionAssertionError(message,_props,ssf)3. varextend=exclude(name,message,stack,constructor,toJSON)4. ,props=extend(_props|);5. 6. /默认值7.

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1