`throw new Error`和`throw someObject`有什么区别?

623

我想编写一个通用的错误处理器,它可以捕获代码的任何实例中故意抛出的自定义错误。

当我像以下代码中那样执行throw new Error('sample')

try {
    throw new Error({'hehe':'haha'});
    // throw new Error('hehe');
} catch(e) {
    alert(e);
    console.log(e);
}

在 Firefox 中,日志显示为Error: [object Object],我无法解析这个对象。

对于第二个throw,日志显示为:Error: hehe

然而当我执行以下操作时

try {
    throw ({'hehe':'haha'});
} catch(e) {
    alert(e);
    console.log(e);
}

控制台显示为:Object { hehe="haha"},我能够访问错误属性。

有什么区别吗?

这个区别就像在代码中看到的一样吗?像字符串将只被传递为字符串,对象作为对象,但语法会有所不同?

我还没有探索过抛出错误对象...我只抛出了字符串。

除了上述两种方法之外,还有其他方法吗?


13
throw new Error({prop:val}) 的问题在于它不是一个有效的 Error 构造方式。正如 Hemant 所讨论的那样,Error 有已知的属性。 - grantwparks
3
相关问题:抛出字符串而不是Error - Bergi
4
基于ECMA262,它们是相同的:当作为函数而不是构造函数调用时,会创建和初始化一个新的Error对象。因此,函数调用Error(...)等同于具有相同参数的对象创建表达式new Error(...)。请参阅https://tc39.es/ecma262/#sec-error-constructor中的规范。 - kitimenpolku
13个回答

336

在javascript中,“throw new Error”和“throw someObject”的区别在于,“throw new Error”将错误包装在以下格式中:

{name:'Error',message:'您在构造函数中传递的字符串'}

“throw someObject”会像“throw new Error”一样直接抛出该对象,并且不允许从try块执行任何其他代码。

这里有一个很好的关于 错误对象和抛出自己的错误 的解释。

错误对象

在错误事件中,我们可以从中提取什么?所有浏览器中的Error对象支持以下两个属性:

  • name:错误的名称,更具体地说,是错误所属的构造函数的名称。

  • message:错误的描述,该描述因浏览器而异。

name属性可能返回6个可能的值,如上所述,它们与错误的构造函数名称相对应。它们是:

Error Name          Description

EvalError           An error in the eval() function has occurred.

RangeError          Out of range number value has occurred.

ReferenceError      An illegal reference has occurred.

SyntaxError         A syntax error within code inside the eval() function has occurred.
                    All other syntax errors are not caught by try/catch/finally, and will
                    trigger the default browser error message associated with the error. 
                    To catch actual syntax errors, you may use the onerror event.

TypeError           An error in the expected variable type has occurred.

URIError            An error when encoding or decoding the URI has occurred 
                   (ie: when calling encodeURI()).

抛出自己的错误(异常)

不必等待6种类型的错误之一发生后,控制自动从try块转移到catch块,您也可以显式地抛出自己的异常,以强制按需发生。这对于创建自己的错误定义以及何时将控制传递到catch非常有用。


7
好的。这是我在问问题之前错过的一些好东西。无论如何,搜索与此相关的信息的用户将会得到澄清。现在我明白了每个事物的含义。 :) 谢谢。我会在几天内回来投票。 - Jayapal Chandran
361
最高票的答案甚至没有回答问题? - user9993
2
@user9993 用户ho在当时的聊天中提出了问题,希望能够得到详细的理解,因此我们提供了相应的答案,并对用户有所帮助。这就是被接受并获得最多赞的原因。 - Hemant Metalia
15
@HemantMetalia 说得没错,这个回答甚至没有试图回答 OP 所提出的问题。如果在聊天中回答了一个非常不同的问题,那么它应该保留在聊天中,因为这里的问题和答案根本没有任何逻辑上的联系。 - Mörre
回答初始问题,对于JavaScript来说并不重要。但是,按照惯例,Error(及其子类)会被使用。它们还默认提供堆栈属性,尽管可以将其手动添加到任何其他对象中。所以这只是大多数人的惯例,程序流程不受你抛出什么的影响,只要你抛出了东西就行了。你甚至可以throw "grandmother down the stairs";,它也能正常工作,但是就没有附加的堆栈跟踪和错误处理函数、报告器、调试器等期望使用Error或者更准确地说是带有Error属性的对象。 - Mörre
如果将Error对象传递到另一个具有自己catch块的Promise中,那么您会再次抛出新的Error对象还是只抛出已经是Error的返回对象? - GHOST-34

281

抛出异常 "I'm Evil"

throw终止程序的执行,并在捕获错误时暴露出错误信息字符串。

try {
  throw "I'm Evil"
  console.log("You'll never reach to me", 123465)
} catch (e) {
  console.log(e); // I'm Evil
}

抛出后的控制台将永远不会被执行,因为程序已经终止。


throw new Error("我很邪恶")

throw new Error 会触发一个带有两个参数 namemessage 的错误事件,并且会终止程序继续执行。

try {
  throw new Error("I'm Evil")
  console.log("You'll never reach to me", 123465)
} catch (e) {
  console.log(e.name, e.message); // Error I'm Evil
}

throw Error("我很邪恶")

只是为了完整起见,这也可以工作,尽管不是技术上正确的方法 -

try {
  throw Error("I'm Evil")
  console.log("You'll never reach to me", 123465)
} catch (e) {
  console.log(e.name, e.message); // Error I'm Evil
}

console.log(typeof(new Error("hello"))) // object
console.log(typeof(Error)) // function


53
"throw Error('whatever')" 和 "throw new Error('whatever')" 之间的区别是什么?这两种方式都可以使用。 - joedotnot
27
错误是功能性的,新的错误是一个构造函数。两者的作用相同。https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Error - Nishchit
2
好的,我明白了。这是一个函数。 - Nishchit
9
@Mörre你在说什么?这两个引语,“throw Error是函数式的”和“throw new Error是一个构造函数”都是事实准确且言之有物的。这就是它们之间唯一相关的区别。throw只是抛出某个东西,不关心是什么,可以是字符串、对象、类的实例(new Something())等等。这个答案因为是唯一正确的答案而得到了很多赞。 - JeneralJames
@Nishchit,你的评论对我来说非常清晰和有用。我只想指出一个显而易见的事实:Mozilla团队更新了那个页面,锚点#Used_as_a_function不再起作用了。 - wlf
显示剩余2条评论

88
以下文章或许会更详细地说明哪种方式更好:throw 'An error' 还是 throw new Error('An error'):

http://www.nczonline.net/blog/2009/03/10/the-art-of-throwing-javascript-errors-part-2/

它表明后者 (new Error()) 更可靠,因为像Internet Explorer和Safari(版本不确定)这样的浏览器在使用前者时无法正确报告消息。引起错误会抛出一个错误,但并非所有浏览器都会以您期望的方式响应。Firefox、Opera和Chrome每个都显示一个“未捕获异常”消息,然后包含消息字符串。Safari和Internet Explorer仅抛出一个“未捕获异常”错误,根本不提供消息字符串。显然,从调试的角度来看,这是次优的。

如果我正在使用ExpressJS编写REST API的代码,那么浏览器的这个问题将不再存在。但是,是否仍然最好使用throw new Error() - Mori

63

简而言之:它们是等价的 Error(x) === new Error(x)

// this:
const x = Error('I was created using a function call!');
​​​​// has the same functionality as this:
const y = new Error('I was constructed via the "new" keyword!');

来源:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Error

throwthrow Error 在功能上是等价的。但当你捕获它们并将它们序列化到 console.log 时,它们的序列化方式不完全相同:

throw 'Parameter is not a number!';
throw new Error('Parameter is not a number!');
throw Error('Parameter is not a number!');

上面的 Console.log(e) 将产生2个不同的结果:

Parameter is not a number!
Error: Parameter is not a number!
Error: Parameter is not a number!

这个怎么样:throw 'message',是一样的吗? - Menai Ala Eddine - Aladdin
2
抛出异常和抛出错误在功能上是等效的 - 事实并非如此。一个抛出字符串,像Bugsnag这样的工具会抱怨它无法从其中获取堆栈跟踪。您所说的“功能”意思是它将停止代码运行,或者它会抛出某些东西吗?无论哪种方式,它们在语义上都不同。 - nruth

45

你首先提到了这段代码:

throw new Error('sample')

然后在您的第一个例子中,您写道:

throw new Error({'hehe':'haha'}) 

第一个Error对象实际上是有用的,因为它期望一个字符串值,在这种情况下是“sample”。第二个对象则不会有用,因为您尝试传递一个对象,但它期望一个字符串,并且不会显示有用的错误信息。

Error对象将拥有“message”属性,其值为“sample”。


22
第二个方法确实起作用,但并不是很有用。它在传入的对象上执行toString()方法,在错误中得到了[object Object](正如原作者所写)。 - cjn

41
< p > Error 构造函数用于创建错误对象。当运行时错误发生时,会抛出错误对象。Error 对象也可用作用户定义异常的基对象。

通过 throw 语句抛出用户定义错误。程序控制将传递到调用栈中的第一个 catch 块。

使用 Error 对象和不使用 Error 对象抛出错误的区别:


throw {'hehe':'haha'};

在Chrome开发者工具中,它看起来是这样的:

enter image description here

Chrome告诉我们有一个未捕获的错误,它只是一个JS对象。该对象本身可能包含关于错误的信息,但我们仍然不知道它来自哪里。在我们编写和调试代码时,这并不是很有用。


throw new Error({'hehe':'haha'}); 

在Chrome开发者工具中,它看起来像这样:

enter image description here

当我们展开使用Error对象抛出的错误时,会给我们提供一个堆栈跟踪。这为我们提供了有价值的信息,可用于调试代码中错误的具体来源。此外,请注意错误消息显示为[object Object],这是因为Error构造函数期望作为第一个参数传入一条消息字符串。如果它收到一个对象,它将把它强制转换为一个字符串。


2
这个答案最有意义。 - Gianfranco Fertino

21

您可以将对象作为参数throw

throw ({message: 'This Failed'})

例如,在您的try/catch

try {
//
} catch(e) {
    console.log(e); //{message: 'This Failed'}
    console.log(e.message); //This Failed
}

或者只是抛出一个字符串错误

throw ('Your error')

try {
//
} catch(e) {
    console.log(e); //Your error
}

throw new Error //only accept a string

16

TLDR

throw new Error('problem')可以捕获错误发生位置的多个属性。

throw 'problem'则不能。

new Error('message')可以捕获执行堆栈和其他信息

使用Error对象可以在抛出错误的位置捕获执行堆栈。当错误传递到错误处理树时,这个堆栈快照也会一起传递。

因此,在我的代码库中插入throw "test error"会导致:

enter image description here

throw new Error('test error')会导致:

enter image description here

你可以看到,原生的Error对象在我抛出错误的位置捕获了堆栈,并使其可用于捕获错误的任何地方。这使得我在调试时更容易跟踪问题。

除此之外,它还捕获了fileNamelineNumbercolumnNumber等属性。

如果你使用堆栈跟踪,则异常跟踪器可以为你记录它

在这种情况下,堆栈被打印到浏览器控制台,但如果你使用像Appsignal或Bugsnag这样的Javascript错误日志工具,则该堆栈也将在其中可用。如果检查错误对象,则可以直接访问堆栈快照:

err = new Error('test')
err.stack

在这里输入图片描述

我使用的启发式方法来决定使用哪种格式

当我不打算捕获异常时,我使用new Error('problem')

如果在应用程序中发生了意外或越界的情况,例如本地数据存储已损坏,但我不想处理它,只想标记它。我会使用Error对象,以便拍摄堆栈快照。

通过使用throw new Error('数据存储已损坏'),更容易追踪到发生了什么。

当我打算捕获异常时,我使用throw '问题'

编辑-重新阅读后,我认为下一部分需要特别注意。在选择要捕获的错误时,最好非常具体,否则您可能会捕获您真正希望将其传递到最高层的东西。通常,创建特定的错误类型并捕获该特定错误(或消息字符串)可能更好。这允许您未预料到的错误浮出水面。

如果错误是我计划捕获和处理的预期错误,那么我不会从堆栈快照中获得太多有用信息。

例如,假设我使用http服务并返回500个HTTP代码。我可能将其视为错误throw "responseCode=500",然后随后捕获和处理。


12
Error类包含调试信息,例如错误的调用堆栈,作为其实例的属性。JS解释器知道如何将Error实例序列化为信息丰富的错误消息字符串,并且其结构也可以被调试软件(如浏览器开发工具)消耗,以构建更详细的错误GUI表示。这就是为什么通常更有用的是抛出Error类的实例,而不仅仅是抛出描述错误的字符串或表示错误代码的数字。

使用自定义错误

制作自己的Error子类特别有用,它允许您使用描述性名称和可机器读取的方式唯一标识不同类型的错误...
  • 用于调试的线索,
  • 用于更好的面向用户的错误消息,或
  • 用于帮助从错误中恢复的信息。
然后,在处理错误时,您可以使用漂亮而干净的instanceof运算符来检查发生了什么类型的错误。例如:
class DangerousWaterCurrent extends Error {
    constructor(waterSpeed){
        super(`These waters are moving at ${waterSpeed} metres per second - too fast to cross!`) // Provide a `message` argument to the Error() constructor
        this.waterSpeed = waterSpeed // This passes some context about why/how the error occurred back to whichever function is going to catch & handle it
    }
}

// ...later...

try {
    swimAcrossRiver(river)
} catch (thrownValue) {
    if (thrownValue instanceof DangerousWaterCurrent) {
        if (thrownValue.waterSpeed <= 3){ 
            paddleKayak(river)
        } else {
            constructBridge(river)
        }
    } else {
        throw thrownValue // "Re-throw" the error back up the execution chain, for someone else to handle
    }
}

new Error() vs Error()

有一种“方便”的简写方式可以创建一个Error的实例:通过调用Error(message),而不是像创建普通类的实例那样使用new Error(message)。这是一种故意的例外,由语言设计者插入的规则。对于其他内部语言类,如Number()String(),也有类似的简写方式。它们也允许您像调用函数而不是类一样使用()来调用这些类。尽管它们在“类”的语法糖下实际上都是函数,但JS不允许普通类这样做。在REPL中尝试一下:

> class E extends Error {}
> Error(); "a value"
"a value"
> E(); "a value"
Uncaught TypeError: Class constructor E cannot be invoked without 'new'
    at <anonymous>:2:1

设计的更广泛观点:个人而言,我认为这个设计决定是一个错误,因为它给JavaScript的规则增加了更多的例外情况 - 这意味着程序员需要学习更多的知识,同时也增加了语言翻译/解释器的工作量。与C++/Java的new关键字不同,简单地将一个类像调用函数一样使用(例如Number("abc123"))应该具有new关键字当前具有的属性:类的constructor函数应该被执行。在该函数内部,this应该绑定到实例并隐式返回。然后可以将new关键字从语言中丢弃。这就是Python的语法工作方式,它更简单、更易读、更方便。

5
这是唯一一个充分涵盖了new ErrorError之间差异的答案。 - Oleksandr Danylchenko
4
这是唯一一个充分涵盖了new ErrorError之间差异的答案。 - Oleksandr Danylchenko

7

React行为

除了其他的答案之外,我想要展示React中的一个区别。

如果我使用new Error()抛出错误,并且处于开发模式下,我将会得到一个错误页面和控制台日志。如果我仅仅抛出字符串字面量,在控制台里可能只能看到它,容易忽略。

例子

在开发模式下,抛出错误既会记录到控制台也会显示一个错误页面 (生产环境下不会显示)。

throw new Error("The application could not authenticate.");

React中错误提示页面

相比之下,以下代码仅将日志记录到控制台:

throw "The application could not authenticate.";

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接