详解JavaScript的BUG和错误(6)

需要注意的是现在look函数可以完全忽略promptDirection出错的可能性。这就是使用异常的优势:只有在错误触发且必须处理的位置才需要错误处理代码。其间的函数可以忽略异常处理。

嗯,我们要讲解的理论知识差不多就这些了。

异常后清理

异常的效果是另一种控制流。 每个可能导致异常的操作(几乎每个函数调用和属性访问)都可能导致控制流突然离开你的代码。

这意味着当代码有几个副作用时,即使它的“常规”控制流看起来像它们总是会发生,但异常可能会阻止其中一些发生。

这是一些非常糟糕的银行代码。

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函数将一笔钱从一个给定的账户转移到另一个账户,在此过程中询问另一个账户的名称。 如果给定一个无效的帐户名称,getAccount将引发异常。

但是transfer首先从帐户中删除资金,之后调用getAccount,之后将其添加到另一个帐户。 如果它在那个时候由异常中断,它就会让钱消失。

这段代码本来可以更智能一些,例如在开始转移资金之前调用getAccount。 但这样的问题往往以更微妙的方式出现。 即使是那些看起来不像是会抛出异常的函数,在特殊情况下,或者当他们包含程序员的错误时,也可能会这样。

解决这个问题的一个方法是使用更少的副作用。 同样,计算新值而不是改变现有数据的编程风格有所帮助。 如果一段代码在创建新值时停止运行,没有人会看到这个完成一半的值,并且没有问题。

但这并不总是实际的。 所以try语句具有另一个特性。 他们可能会跟着一个finally块,而不是catch块,也不是在它后面。 finally块会说“不管发生什么事,在尝试运行try块中的代码后,一定会运行这个代码。”

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;
 }
 }
}

这个版本的函数跟踪其进度,如果它在离开时注意到,它中止在创建不一致的程序状态的位置,则修复它造成的损害。

请注意,即使finally代码在异常退出try块时运行,它也不会影响异常。finally块运行后,堆栈继续展开。

即使异常出现在意外的地方,编写可靠运行的程序也非常困难。 很多人根本就不关心,而且由于异常通常针对异常情况而保留,因此问题可能很少发生,甚至从未被发现。 这是一件好事还是一件糟糕的事情,取决于软件执行失败时会造成多大的损害。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/416.html