6.5 何时抛出异常

    到此为止,我们学习了 try/catch 括起来的异常处理结构语句是怎样产生和发展的,主要围绕异常被抛出来之后如何处理进行了解说。接下来我们要转移一下焦点,来学习异常是什么时候抛出来的。

    错误发生时,有返回返回值和抛出异常两种传达方法。那么,什么时候使用返回值的方法,什么时候使用异常的方法呢? 2000 年左右,有种观点认为,异常的方法仅限于异常的情况下使用 19,那么异常的情况又是指哪些情况呢?

    19参照 http://www.ibm.com/developerworks/jp/java/library/j-perf02104/index.html

    函数调用时参数不足的情况

    这里我们列举 Python、Ruby、JavaScript 这三种脚本语言,来比较各种语言分别在什么时候抛出异常。比如,调用一个带有两个参数的函数但只传递一个参数时会发生什么? Python 语言和 Ruby 语言会在函数调用的时刻抛出异常。但是 JavaScript 语言会把缺失的参数当作未定义的特殊值(undefined)继续执行。

    Python
    def foo(x, y):
    print x, y

    foo(1)

    结果(异常)
    Traceback (most recent call last):
    File "tmp.py", line 4, in <module>
    foo(1)
    TypeError: foo() takes exactly 2 arguments (1 given)

    Ruby
    def foo(x, y)
    p x, y
    end

    foo 1

    结果(异常)
    tmp.rb:1:in foo&#39;: wrong number of arguments (1 for 2) (ArgumentError)<br/> from tmp.rb:5:in<main>'


    JavaScript
    function foo(x, y){
    console.log(x, y);
    }

    foo(1)

    结果(成功)
    1 undefined

    数组越界的情况

    还有一种情况,比如,试图读取一个只有三个数的数组的第四个数值时会怎么样?这就是数组的界外操作。此时,Python 语言会抛出异常,Ruby 语言会返回一个指示不存在的特殊值(nil),而 JavaScript 语言会返回 undefined。

    Python
    x = [0, 1, 2]
    print x[3]

    结果(异常)
    Traceback (most recent call last):
    File "tmp.py", line 2, in <module>
    print x[3]
    IndexError: list index out of range

    Ruby
    x = [0, 1, 2]
    p x[3]

    结果(成功)
    nil

    JavaScript
    x = [0, 1, 2];
    console.log(x[3]);

    结果(成功)
    undefined

    以上三种语言的设计者都是具有高超技术能力的程序设计员,即便是他们,就何种情况应该抛出异常也不能达成一致。异常应该在何种情况下使用,何为异常的情况,这些问题是没有正确答案的。

    出错后就要立刻抛出异常

    笔者认为,在学习程序设计或者是一个人编写小规模程序时,像 Python 语 言这种立刻抛出异常的方式要比 JavaScript 语言那样返回 undefined 更好。

    人非圣贤,孰能无过,程序员也不例外,一不小心引入 bug 也不是不可能的。要保证代码的品质只能是尽早发现 bug 并及时修正它。这同时说明,能尽早意识到不对劲的地方是十分重要的 20。

    20为了提高软件的品质通过检查代码来确认程序员期待的程序行为,这一测评方法也是基于同样的想法。

    异常机制的优点就在于,它不会遗漏任何一处错误。当发生数组的界外读取时,我们希望程序能抛出异常,向程序员报告错误。只要不是程序员有意编写“界外读取时返回 undefined”这样的代码,界外读取的行为终究是异常事态。返回适当的值来推迟宣布异常事态的发生,这样也无法解决异常 21。

    21因为没有注意到返回值为 undefined 而继续做操作,接下来会碰到“TypeError: Cannot read property 'foo' of undeinfed”,这个对于 JavaScript 的程序员应该是家常便饭了吧。

    发生错误应该停止操作立刻报告,这一设计思想被称为错误优先(fail first)。软件的目的不一样,简单地停止操作有时可能不太妥当,但至少在学习和开发阶段发生错误后能立刻注意到,这已经是一个很大的优点了。