检查 JS 中的 typeof 错误

Posted

技术标签:

【中文标题】检查 JS 中的 typeof 错误【英文标题】:checking for typeof error in JS 【发布时间】:2015-08-08 18:13:59 【问题描述】:

在 JS 中,似乎无法检查传递给函数的参数实际上是“错误”类型还是错误实例。

例如,这是无效的:

typeof err === 'error'

因为只有 6 种可能的类型(以字符串的形式):

typeof 运算符以字符串形式返回类型信息。 typeof 返回六个可能的值:

“数字”、“字符串”、“布尔”、“对象”、“函数”和“未定义”。

MSDN

但是如果我有一个像这样的简单用例:

function errorHandler(err) 

    if (typeof err === 'error') 
        throw err;
    
    else 
        console.error('Unexpectedly, no error was passed to error handler. But here is the message:',err);
    

那么确定参数是否为 Error 实例的最佳方法是什么?

instanceof 运算符有什么帮助吗?

【问题讨论】:

是的,使用err instanceof Error @colecmc 如果错误可能来自框架或另一个窗口中的代码,那不会有问题吗?在这种情况下,会有不同的原型 Error 对象,并且 instanceof 不会按预期工作。 (见developer.mozilla.org/en-US/docs/Web/javascript/Reference/…) @Doin,我不这么认为。它只会验证实际的错误对象。 @colecmc,我想你会发现如果你在(比如说)一个 iframe 中抛出了err,然后将它传递给父窗口进行处理,你会得到@987654329 @。这是因为 iframe 及其父窗口具有不同的 Error 对象原型。类似地,像obj = ; 这样的对象,当传递给在不同窗口中运行的函数时,会产生(obj instanceof Object)===false。 (更糟糕的是,在 IE 中,如果您在 obj 的窗口被销毁或导航后保留对 obj 的引用,则尝试像 obj.hasOwnProperty() 这样调用对象原型 fns 会引发错误!) 如何检查错误类型?即catch((err)=>if (err === ENOENT) 返回ENOENT is not defined 错误? 【参考方案1】:

您可以使用instanceof 运算符(但请参阅下面的警告!)。

var myError = new Error('foo');
myError instanceof Error // true
var myString = "Whatever";
myString instanceof Error // false

如果错误是在与检查发生位置不同的窗口/框架/iframe 中引发的,则上述方法将不起作用。在这种情况下,instanceof Error 检查将返回 false,即使对于 Error 对象也是如此。在这种情况下,最简单的方法是鸭式打字。

if (myError && myError.stack && myError.message) 
  // it's an error, probably

但是,如果您有包含stackmessage 属性的非错误对象,则duck-typing 可能会产生误报。

【讨论】:

@AurélienRibon 它适用于 ReferenceError 实例。您正在检查全局 ReferenceError 对象。试试这个:try foo catch (e) console.log(e instanceof Error) 它记录true。如果您出于某种原因尝试检查全局 ReferenceError 对象,则可以使用 Error.isPrototypeOf(ReferenceError) @AurélienRibon(嗯,还有,是的,不确定您是否在说“这不适用于 ReferenceError”,这是不正确的,或者您只是指出“这不会” t 与全局 ReferenceError 对象一起工作”这是绝对正确的,虽然我很难想出一个可以检查的情况,但这可能更多地说明我缺乏想象力而不是用例的有效性)。 哈哈,对不起那些家伙,我刚刚陷入了一个奇怪的错误,我的所有ReferenceError 实例都不是Error 的实例。这是由于在节点中调用了vm.runInNewContext(),所有标准原型都被重新定义。我所有的结果都不是标准对象的实例。我一直在寻找这个,并陷入了这个线程:) 你可以试试const error = (err instanceof Error || err instanceof TypeError) ? err : new Error(err.message ? err.message : err); 你如何检查它是否是 instanceof 特定类型的错误?【参考方案2】:

我问了最初的问题 - @Trott 的回答肯定是最好的。

但是,由于 JS 是一种动态语言,并且有如此多的 JS 运行时环境,instanceof 运算符可能会在跨越 iframe 等边界时失败,尤其是在前端开发中。看: https://github.com/mrdoob/three.js/issues/5886

如果你对鸭子打字没问题,这应该很好:

let isError = function(e)
 return e && e.stack && e.message;

我个人更喜欢静态类型语言,但如果您使用的是动态语言,最好接受动态语言的本来面目,而不是强迫它表现得像静态类型语言。

如果你想更精确一点,你可以这样做:

   let isError = function(e)
     return e && e.stack && e.message && typeof e.stack === 'string' 
            && typeof e.message === 'string';
    

【讨论】:

在编写实用函数或库时,我更喜欢这个版本。它不会将用户限制在一个特定的全局上下文中,并且还可以正确处理来自 JSON 的反序列化错误。 谢谢,是的,在很多情况下,如果它看起来像一个错误,我们可以将其视为一个错误。在某些情况下,鸭子打字是完全可以接受的,在某些情况下它根本不可接受,在这种情况下,完全可以。 我使用调试器进行了一些调试,堆栈和消息确实似乎是错误实例上仅有的两个非本机属性。 @Trott 您可能有兴趣了解 instanceof 运算符在某些情况下会失败,请参阅链接文章。 这实际上是最简单的,因此它适用于不同类型的错误(即ErrorReferenceError,...)。在我的环境中,instanceof 在许多情况下都失败了。【参考方案3】:

您可以使用Object.prototype.toString 轻松检查对象是否为Error,这也适用于不同的帧。

function isError(obj)
    return Object.prototype.toString.call(obj) === "[object Error]";

function isError(obj)
    return Object.prototype.toString.call(obj) === "[object Error]";

console.log("Error:", isError(new Error));
console.log("RangeError:", isError(new RangeError));
console.log("SyntaxError:", isError(new SyntaxError));
console.log("Object:", isError());
console.log("Array:", isError([]));

ECMAScript 语言规范保证此行为。

Object.prototype.toString:

在调用toString方法时,会采取以下步骤:

    如果 this 值未定义,则返回“[object Undefined]”。 如果this值为null,则返回“[object Null]”。 令 O 为调用 ToObject 并将 this 值作为参数传递的结果。 令 class 为 O 的 [[Class]] 内部属性的值。 返回字符串值,它是三个字符串“[object”、class 和“]”连接的结果。

Properties of Error Instances:

错误实例从错误原型对象继承属性,其[[Class]] 内部属性值为“错误”。错误实例没有特殊属性。

【讨论】:

我不确定我是否喜欢这个。我不知道是否有任何标准规定,当字符串化时,错误必须完全是这种形式。我认为这将取决于实施...... @Aaron 这不是真的。我已经用 ECMAScript 语言规范的摘录编辑了我的答案。 这是具有最少(没有?)缺点的最佳解决方案。【参考方案4】:
var myError = new Error('foo');
myError instanceof Error // true
var myString = "Whatever";
myString instanceof Error // false

唯一的问题是

myError instanceof Object // true

另一种方法是使用构造函数属性。

myError.constructor === Object // false
myError.constructor === String // false
myError.constructor === Boolean // false
myError.constructor === Symbol // false
myError.constructor === Function // false
myError.constructor === Error // true

虽然需要注意的是,这个匹配是非常具体的,例如:

myError.constructor === TypeError // false

【讨论】:

【参考方案5】:

你可以像这样检查一个变量是否有错误

const isError = (err: unknown): err is Error => err instanceof Error;

然后像这样在 try catch 中进行验证

try 
  login(username, password);
 catch (err) 
  if (isError(err)) 
  console.log(err.message);

【讨论】:

我相信这是一个打字稿解决方案。【参考方案6】:

您可以使用 obj.constructor.name 来检查对象https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name#Function_names_in_classes的“类”

举例

var error = new Error("ValidationError");
console.log(error.constructor.name);

上面的行将记录“错误”,它是对象的类名。这可以与 javascript 中的任何类一起使用,如果该类没有使用名为“name”的属性

【讨论】:

这在缩小代码和使用错误子类时不可靠【参考方案7】:

感谢@Trott 提供您的代码,我只是使用了相同的代码并添加了一个实时工作示例,以造福他人。

<html>
<body >

<p>The **instanceof** operator returns true if the specified object is an instance of the specified object.</p>



<script>
	var myError = new Error("TypeError: Cannot set property 'innerHTML' of null"); // error type when element is not defined
	myError instanceof Error // true
	
	
	
	
	function test()
	
	var v1 = document.getElementById("myid").innerHTML ="zunu"; // to change with this
	
	try 
		  var v1 = document.getElementById("myidd").innerHTML ="zunu"; // exception caught
		   
		  
	catch (e) 
		  if (e instanceof Error) 
			console.error(e.name + ': ' + e.message) // error will be displayed at browser console
		  
  
  finally
		var v1 = document.getElementById("myid").innerHTML ="Text Changed to Zunu"; // finally innerHTML changed to this.
	
	
	
	
</script>
<p id="myid">This text will change</p>
<input type="button" onclick="test();">
</body>
</html>

【讨论】:

嘿,现在是 2020 年 - 请不要在 SO 帖子中使用 var【参考方案8】:

对于那些正在寻找一些“官方”方式的人(就像我一样),这是what MDN recommends:

try 
  myRoutine();
 catch (e) 
  if (e instanceof RangeError) 
    // statements to handle this very common expected error
   else 
    throw e;  // re-throw the error unchanged
  

【讨论】:

【参考方案9】:

或将其用于不同类型的错误

function isError(val) 
  return (!!val && typeof val === 'object')
    && ((Object.prototype.toString.call(val) === '[object Error]')
      || (typeof val.message === 'string' && typeof val.name === 'string'))

【讨论】:

【参考方案10】:

只需使用error.name

function _err(type = false) 
    if(type) 
        throw new TypeError('Oh crap!')
    
    throw new Error('Oh crap!')


try 
    _err(true)
 catch (error) 
    console.log(typeof error.name, error.name, error.name === 'TypeError')


try 
    _err()
 catch (error) 
    console.log(typeof error.name, error.name, error.name === 'Error')

【讨论】:

【参考方案11】:

您可以进一步从@iota 那里得到答案,并通过getPrototypeOf() 或已弃用的__proto__ 属性检查测试对象的内部[[Prototype]] 属性。

如果对象是一个错误,它继承自Error.prototype。所以可能是这样的:

// the object you want to check 
const objectToCheck = new Error();

// current way
console.log(Object.getPrototypeOf(objectToCheck) === Error.prototype);  /* true*/

// deprecated way
console.log(objectToCheck.__proto__ === Error.prototype);  /* true */

【讨论】:

以上是关于检查 JS 中的 typeof 错误的主要内容,如果未能解决你的问题,请参考以下文章

聊聊js中的typeof

浅谈JS中的typeof和instanceof的区别

js中的typeof和instanceof和===

javascript中的typeof()函数错误[重复]

检查值是不是是 JavaScript 中的符号

JS中typeof和instanceof的区别