Eloquent JavaScript 笔记 八: Bugs and Error Handling
来源:互联网 发布:安卓去广告软件 编辑:程序博客网 时间:2024/06/03 23:44
原书:http://eloquentjavascript.net/08_error.html
1. Strict Mode
"use strict";
1.function内,放在第一行;
2.js文件,放在第一行;
3. <script> 标签,放在第一行;
4. 如果不放在第一行,没用。
未定义的变量
function canYouSpotTheProblem() { "use strict"; for (counter = 0; counter < 10; counter++) console.log("Happy happy");}canYouSpotTheProblem();// → ReferenceError: counter is not defined
如果在正常情况下,使用未定义的变量时,系统会自动定义一个全局变量。而strict模式下,不会定义全局变量,直接报错。
this
如果一个函数是object的方法,而调用它时,没有相应的object,会怎样?
正常模式下,this指向全局变量。strict模式下,this等于undefined。
function Person(name) { this.name = name; }var ferdinand = Person("Ferdinand"); // oopsconsole.log(name);// → Ferdinand
在这段代码中,调用Person构造函数时,忘了写new。那么,它就没有创建一个新的Person对象,而只是把Person(name)作为一个普通的函数来执行。这个时候,this是全局变量。也就是说,this.name = name; 会给全局变量变量创建一个属性name,并赋值。仔细看 console.log(name); 这里面的name变量是哪儿来的?
"use strict";function Person(name) { this.name = name; }// Oops, forgot 'new'var ferdinand = Person("Ferdinand");// → TypeError: Cannot set property 'name' of undefined
这段代码中,this是undefined,给一个undefined添加name属性,当然就报错了。
以上一章的Vector类为例
function Vector(x, y) { this.x = x; this.y = y;}Vector.prototype.plus = function(other) { return new Vector(this.x + other.x, this.y + other.y);};
写一组测试用例
function testVector() { var p1 = new Vector(10, 20); var p2 = new Vector(-10, 5); var p3 = p1.plus(p2); if (p1.x !== 10) return "fail: x property"; if (p1.y !== 20) return "fail: y property"; if (p2.x !== -10) return "fail: negative x property"; if (p3.x !== 0) return "fail: x from plus"; if (p3.y !== 25) return "fail: y from plus"; return "everything ok";}console.log(testVector());// → everything ok
已经有好些现成的Test Suite可以用了,功能与上例类似。会简化测试代码的书写,提供更好的输出格式。
1. Debugging
看一个例子,找出问题
给定radix(10进制、16进制等),把一个整数按这个基数打印出来。
function numberToString(n, base) { var 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…
用console.log 调试
在do .. while循环内部,加上 console.log(n);
可以看到,n /= base 之后,不是整数。所以,这一行应该写做:n = Math.floor(n/base);
用浏览器自带的调试功能,加断点、单步跟踪
这是chrome
或者,在代码中加入一行 debugger; 如果浏览器的Develop功能打开了,它会自动变成一个breakpoint
1. Error Propagation
并不是所有的代码和数据都是可控的,例如:用户的输入、使用第三方代码库、从网路上获取数据等。
看一个例子
这个函数返回用户输入数字:
function promptNumber(question) { var result = Number(prompt(question, "")); if (isNaN(result)) return null; else return result;}console.log(promptNumber("How many trees do you see?"));
不幸的是,用户可以输入任何字符,我们并不能每次都得到正确的输入。所以,这里做了错误处理,当不能得到合格的数字时,返回null。
在函数中返回一个特殊的值,用以代表出错/异常,是一种常用的方法。
但有时,这种方法也不太实用,因为:
1. 有时找不到合适的 “特殊数值” ,也许 null 也是一个合法的返回值呢?
2. 在调用该函数时,每次都要判断返回值是否合法,是否出错。也是一件麻烦的事情。尤其是,当函数调用的层次比较深,那么每一层都要写一些防御性代码。
1. Exceptions
回顾第三章的函数堆栈,每一次函数调用,都会压一层堆栈,每return一次,就会弹出一层堆栈。
exception类似于一个超级return,它可以一次弹出多层堆栈,一直弹到有catch它的那一层。
基本用法
function promptDirection(question) { var 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);}
1. throw
抛出异常
2. Error
预定义的类,主要有两个属性:
message :错误描述
stack: 当前的堆栈3. try
可能抛出异常的代码块
4. catch
异常发生后,跳到这里。之后的代码还会正常执行。
1. Cleaning Up After Exceptions
有这么一种情况
var context = null;function withContext(newContext, body) { var oldContext = context; context = newContext; var result = body(); context = oldContext; return result;}
注意,在执行 body() 之前,我们保存了context,执行完body()之后,恢复原来的context。如果在body()中有exception抛出,怎么办? 后面两行代码不会执行呀。
finally
function withContext(newContext, body) { var oldContext = context; context = newContext; try { return body(); } finally { context = oldContext; }}
不论是否抛出异常,finnally 代码块都会被执行。注意,即使是在 return 语句之后,也会执行。
看一个完整的例子:
var context = null;function withContext(newContext, body) { var oldContext = context; context = newContext; try { return body(); } finally { context = oldContext; }}try { withContext(5, function () { if (context < 10) throw new Error("Not enough context!"); });}catch(e) { console.log("Ignoring: " + e);}console.log(context);
1. Selective Catching
如果一个exception没有被catch,它会一直弹到栈底,此时,系统(浏览器)会捕获它,通常会在console中打印出错误描述。
有时,我们希望捕获某些种类的exception,而让其他的由系统处理。如果按照上个例子的写法,会捕获所有body()内部抛出的异常,这可能不是我们所希望的。
看一个例子:
for (;;) { try { var dir = promtDirection("Where?"); // ← typo! console.log("You chose ", dir); break; } catch (e) { console.log("Not a valid direction. Try again."); }}
该代码的本意是得到合法的用户输入就break,跳出for循环。可是,promtDirection 这个函数名拼错了,这个异常本来应该由系统处理,而下面的catch把它强行捕获,导致了死循环。
Error 由个message属性,也许我们可以通过不同的message来区分不同类型的exception。但这样不好,因为message是给人看的,不能严谨的定义一种类型,随意性比较强(被翻译成不同语言、被修改了,等等)。
定义不同类型的Error
就像系统预定义的:Error, SyntaxError, ReferenceError 等。
function InputError(message) { this.message = message; this.stack = (new Error()).stack;}InputError.prototype = Object.create(Error.prototype);InputError.prototype.name = "InputError";定义一个InputError,继承 Error.prototype,并且,通过Error的构造,得到当前的堆栈。
系统提供的Error类型都有name属性。我们也给它加上。
function promptDirection(question) { var result = prompt(question, ""); if (result.toLowerCase() == "left") return "L"; if (result.toLowerCase() == "right") return "R"; throw new InputError("Invalid direction: " + result);}for (;;) { try { var 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代码块:
1. 用instanceof 识别不同的Error类型;
2. 其他类型的继续throw 。
js本身没有提供assert,可以自己定义一个
function AssertionFailed(message) { this.message = message;}AssertionFailed.prototype = Object.create(Error.prototype);function assert(test, message) { if (!test ) { throw new AssertionFailed(message); }}很简单哈。
用它试一试:
function lastElement(array) { assert(array.length > 0, "empty array in lastElement"); return array[array.length -1];}
本章的两个练习题很简单,就是练习一下自定义Error类型和使用finnally。
- Eloquent JavaScript 笔记 八: Bugs and Error Handling
- Eloquent JavaScript 笔记 十四:Handling Event
- Eloquent JavaScript 笔记 四:Objects and Arrays
- Eloquent JavaScript 笔记 十二:Javascript and the Browser
- 《Eloquent JavaScript》笔记--函数;
- Eloquent JavaScript 笔记 一、Values, Types, and Operators
- Eloquent JavaScript 笔记 十八:Forms and Form Fields
- Error Handling and Debugging
- Debugging and Error Handling
- Eloquent JavaScript 笔记 三: Functions
- Eloquent JavaScript 笔记 十: Modules
- Eloquent JavaScript 笔记 十三:DOM
- Eloquent JavaScript 笔记 十七:HTTP
- 《Eloquent JavaScript》笔记--对象与数组
- 《Eloquent JavaScript》笔记--程序的结构;
- Eloquent JavaScript 笔记 二:Program Structure
- Eloquent JavaScript 笔记 五: High-Order Functions
- Eloquent JavaScript 笔记 七: Electronic Life
- Redis技术之旅十 高并发问题
- 数据结构---表达式求值
- Python快速排序算法
- varnish
- Android 获取各种路径的方法:
- Eloquent JavaScript 笔记 八: Bugs and Error Handling
- 对于hibernate懒加载模式报错处理
- 让linux也可以使用pbcopy
- Vert.x有毛病?
- spring动态多数据源实例Demo
- 数据结构----二叉树的遍历
- webrtc 音频视频数据流流程图
- android 自定义Button,抛弃写shape文件
- 数据结构----栈和队列的综合应用