问题描述
|
有人告诉我,在JavaScript中使用异常是“不好的”。还没有真正被告知为什么不好,但是相反,我应该使用break,continue和return代替。
很好,除了我不需要返回/中断/继续,我需要抛出。在一种情况下,我的迭代器函数相互嵌套,每个迭代器函数在调用时都返回下一个值,因此我使用异常来表示没有其他要迭代的方法:这似乎是一种合理的方法,代码干净并且在实践中完美运行。真的有什么理由不在js中使用异常吗?
第二个问题,当我使用异常时,应该抛出哪种对象?对于错误,我显然会抛出Error实例,但是对于特殊情况(停止迭代等),我需要一种快速的方法来检查那些特定的异常,而我所做的只是定义一个空的命名函数(函数stopiteration() {}),并且由于函数是通过引用进行比较的,因此我始终可以检查这是否是我的特例,还是应该重新抛出。在js中是否有更好或更理想的方式?我是否应该真正尝试重构代码以避免使用异常?
谢谢
解决方法
通常,异常处理比正常评估慢,并且仅在特殊情况下
为了停止迭代,最好为结束条件定义hasNext或isEmpty函数,或者在没有更多元素时返回前哨值(如
undefined
),这样循环就变成了
//with hasNext
while(it.hasNext()){
var val = it.next();
//...
}
//or with a sentinal undefined
while( (val = it.next()) !== undefined){
//...
}
,听起来您正在使用嵌套循环在多维空间中进行搜索,这在编程中是很常见的事情。试图在找到目标时从最内层循环引发异常,然后被最外层循环周围的“ catch”块捕获,这很诱人,但这通常被认为是较差的做法。
语言设计师意识到了这种缺陷,因此他们允许我们给标签(名称)提供循环结构,以便我们可以脱离它们(或继续使用它们)。考虑以下示例:
function findValueInMatrix(value,matrix) {
var r,c,coords,rows=matrix.length,cols=matrix[0].length;
found: // Label the outer loop as \"found\" so we can break from it.
for (r=0; r<rows; r++) {
for (c=0; c<cols; c++) {
if (matrix[r][c] == value) {
coords = [r,c]
break found; // Exit the loop labeled \"found\".
}
}
}
return coords;
}
您可以在这篇文章中找到有关打破嵌套循环的更多信息。
这个jsPerf测试用例还演示了打破标签的名义上比抛出异常要快(大概在大多数浏览器中)。
,对于某些人来说,抛出异常以指示没有更多的值是一种不好的风格。例外行为除外。
而且,异常处理通常比简单测试慢。
如果不是每次都创建抛出的实例,则差异实际上很小(甚至可能根本不存在)。所以拥有恒定的StopIteration
并扔出去实际上是非常快的。
对于您的问题,您可能需要查看https://developer.mozilla.org/en/JavaScript/Guide/Iterators_and_Generators(它们使用异常来停止迭代)。
我发现在动态类型的语言中,异常也用于这种目的。
,抛出异常以摆脱嵌套的递归搜索/遍历是最简单,更优雅的方法。
假设您走了一棵树,递归地调用一个函数来处理当前节点,再次为其每个子节点调用,依此类推。
在处理每个节点时,您可能希望完全破坏整个调用堆栈并返回找到的值(或总体上停止迭代),以打破先前调用中等待的所有循环。请注意,这些先前的调用可能会在处理和内存上花费很大,但是如果您“发现”了结果,那将毫无用处。抛出异常是实现此目的的最有效,最简单的方法,但是当然需要引起注意。
对于简单的(甚至是嵌套的)循环,它是一个过大的杀伤力,因此被正确地吹捧为反模式。