抛出字符串而不是错误

IT技术 javascript
2021-01-18 02:40:52

既然我们可以throw在Javascript中用关键字抛出任何东西,那么我们不能直接抛出一个错误消息字符串吗?

有谁知道这有什么问题吗?

让我为此添加一些背景知识:在 JavaScript 世界中,人们通常依赖参数检查而不是使用 try-catch 机制,因此仅使用throw. 尽管如此,为了能够捕获一些系统错误,我必须为自己的错误使用不同的类,而不是创建 Error 的子类,我认为我应该只使用 String。

5个回答

虽然是好的可能引发任意值,它通常被认为是拙劣的形式抛出比的实例之外的任何Error或它的一个子类。有几个原因:

  1. 捕获代码可能期望抛出的对象有一般的messagestacktracename出现在性质Error秒。
  2. 缺乏堆栈跟踪会导致调试出现问题,尤其是在未捕获的异常/未处理的拒绝的情况下。例如,调试“Uncaught [Object object]”错误可能特别痛苦。
可以,但我不会说它“可以”
2021-03-24 02:40:52
例如,它可能假设 的.message属性Error是一个字符串,尝试对其调用字符串方法,然后爆炸。
2021-04-02 02:40:52
这是一个糟糕的主意,因为这意味着如果没有堆栈跟踪,您将很难调试错误。
2021-04-05 02:40:52
@Taurus 谢谢,我不知道 - 现在尝试使用未在 Firefox 中连接的调试器来执行此操作:)
2021-04-07 02:40:52
@BenjaminGruenbaum 如果你在 Chrome 中抛出一个字符串,你会得到一个堆栈跟踪。例如` function a() { throw "Hello World"; a();` 将有一个这样的堆栈跟踪:a @ main.js:4 (anonymous) @ main.js:7
2021-04-09 02:40:52

是的,您可以抛出其他值,但这不是一个好习惯

有谁知道这有什么问题吗?

字符串不是错误对象,并且不传达任何有用的调试信息。Devtools 依赖于它,例如创建错误的文件和行、该throw位置的堆栈跟踪等,这些都可用作Error对象的属性

每当您想到抛出原始字符串值时,请new Error("<the string>")改为抛出 a

@Bergi 感谢您的评论。但是,我刚刚扩展了性能测试,throw null;似乎只比throw "some string";以下内容快 1% jsbench.me/1gkqk6dm06/3也许最新版本的 V8 已经将它们优化到大致相当的位置?
2021-03-16 02:40:52
另请注意,抛出(和捕获)字符串而不是错误具有速度优势:jsbench.me/1gkqk6dm06/2我的结果:错误为 237k ops/s,字符串为 1.168m ops/s。这几乎从不重要,但理论上可以。(例如,如果您出于某种原因使用字符串抛出只是为了“转义”到更高级别的函数调用)
2021-03-27 02:40:52
值得注意的是,即使您抛出一个字符串,Chrome devtools 也会打印堆栈跟踪。您失去了.stack对某些自动错误处理有用属性(例如,当设置自动向服务器报告错误以让您调试生产中出现的错误时),但这更多是一种边缘情况;如果您抛出字符串,浏览器中的日常手动调试工作正常。
2021-04-01 02:40:52
@Venryx 我并不是说投掷null会更快,只是说它更干净。你不应该throw "Test1"因为它令人困惑。
2021-04-04 02:40:52
@Venryx 如果您想将异常用于控制流,而不是用于错误,我建议您也不要使用字符串(当然我建议您根本不要这样做)。throw null;,如果必须,或者如果您需要不同的返回码,请使用符号或常量对象。
2021-04-07 02:40:52

你可以用消息抛出错误,你知道的。

try {
    throw new Error("This is an error");
} catch (e) {
    alert(e.message); // This is an error
}

但你实际上可以抛出字符串:

try {
    throw "This is an error";
} catch (e) {
    alert(e); // This is an error
}
啊,好吧,那就看看Ianzz的评论吧。它总结了所有的警告。但我的建议仍然是抛出Error带有自定义消息的对象。它可以为您省去检查错误类型以从其他本机生成的错误中告诉“您的”错误的麻烦......当然,除非您想故意这样做(然后可能会Errorcatch范围内重新抛出对象) .
2021-03-15 02:40:52
抱歉没有把问题说清楚。我知道这些都有效,但我对直接使用字符串而不是使用 Error 对象的潜在问题感兴趣。
2021-04-11 02:40:52

正如其他人在上面提到的,如果你没有抛出一个 Error 对象,那么你必须有 try/catch 块来捕获这些对象并适当地处理它们,否则在调试时会受到伤害。

然而,当为了控制程序流等非错误处理目的而抛出错误时,这可能是一种throw没有错误的有用方法

当使用 throws 来控制程序流时,它在任何语言中都可能是低效的,因为运行时通常会做很多繁重的工作来展开调用堆栈信息并序列化数据,使其可用于用户范围。通过避免错误创建,您可以避免这种性能下降。关键是您必须在调用堆栈上有一个知道如何处理这种情况的处理程序。例如,如果您throw {isHardStop: true, stopCode: SOME_CODE}设计处理程序来检测这一点,您可能能够扁平化一些代码或选择更简洁的语法。

您用于此梯子案例的处理程序的结构可以如下:

try { ... } catch(thr) {
    if(!thr){
       // Is not Error or Json - Handle accordingly
    } else if(thr.isHardStop){
       // Handle the stop
    } else {
       // Most likely a real error. Handle accordingly
    }
}
我同意不应出于异常处理以外的目的抛出错误和异常。但是,在我的示例中,我只是抛出一个对象而不是实例化错误,因此它只是使用 try/catch 机制进行替代流控制。性能损失应该可以忽略不计,但可训练性并不理想。话虽如此,我自己从未这样做过,现在可能也不会这样做。相反,我可能会使用类似于 Erlang/Elixir 中使用的方法,将逻辑包装在一个返回成功标识符和结果值的 lambda 中。
2021-03-21 02:40:52
不,如果您需要驱动程序流,则返回/解析可以处理程序流的值。例外是例外。如果您无法恢复并需要系统抛出异常,那么这不是用于程序流程而是用于异常处理。
2021-04-12 02:40:52

尽管您可以抛出您想要的任何类型的数据,但这在调试时并不是最佳选择。JSError对象包含有关 Error 和消息的所有类型的信息。而字符串只能包含一条消息。

这些附加信息包括:

  1. fileName:从哪个文件抛出错误
  2. Linenumber :从哪一行抛出错误
  3. 堆栈跟踪:从哪个函数调用错误

例如,这是来自 chrome devtools 的堆栈跟踪:

在此处输入图片说明