【问题标题】:checking for typeof error in JS检查 JS 中的 typeof 错误
【发布时间】: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 错误?

标签: javascript node.js


【解决方案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方法时,会采取以下步骤:

  1. 如果 this 值未定义,则返回“[object Undefined]”。
  2. 如果this值为null,则返回“[object Null]”。
  3. 令 O 为调用 ToObject 并将 this 值作为参数传递的结果。
  4. 令 class 为 O 的 [[Class]] 内部属性的值。
  5. 返回字符串值,它是三个字符串“[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 */
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-11-18
            • 2010-11-02
            • 1970-01-01
            • 2017-01-18
            • 1970-01-01
            • 1970-01-01
            • 2012-08-02
            相关资源
            最近更新 更多