出于某种原因,在下面的代码段中,构造函数委托似乎不起作用:

function NotImplementedError() { 
  Error.apply(this, arguments); 
}
NotImplementedError.prototype = new Error();

var nie = new NotImplementedError("some message");
console.log("The message is: '"+nie.message+"'")

运行该命令得到的消息是:"。有什么想法,为什么,或者是否有更好的方法来创建一个新的错误子类?是否有一个问题,应用到本机错误构造函数,我不知道?


当前回答

以下是我支持es2015之前版本浏览器的解决方案。它不做任何花哨的原型调整,也不会破坏调试器。

/**  Custom Errors
    // Depends on underscore js
    // This will declare an CustError() class in both 'this' and '_exports' namespaces
    // ctor is optional
    declare_cust_error(function CustError(){}, {ns: [this, _exports], ctor: 
        function cust_err_ctor(instance, clazz, name, msg, info){
            q$.called(arguments)
        }
    })

    // Usage:
    // Second param (pojso) is optional
    try {
        throw CustError.create("foo", {k1: 'v1', k2: 'v2'})
    }catch(ex){
        if(CustError.is_inst(ex)){
            console.error("its a CustError", ex)
        } else {
            throw ex
        }
    }

**/
function declare_cust_error(error_class, opts){
    var p, c, cp
    if(!error_class||!(p=error_class.prototype))throw new Error("error_class must be a Class")
    try{
        c = p.constructor; cp = c.toString()
    }catch(ex){}
    if(!cp || cp.indexOf('function ') != 0 || cp.indexOf('[native code]') > 0)
        throw new Error("error_class must be a classic proto class (pre-es6) but got: " + error_class.toString())

    opts=opts||{}
    
    error_class.__is_cust_error__ = true
    error_class.__cust_error_name__ = c.name

    error_class.create = function cust_error_create(msg, info){
        var instance = new Error(msg)
        instance.info = info
        instance.__is_cust_error__ = true
        instance.__cust_error_name__ = c.name
        if(_.isFunction(opts.ctor)){
            opts.ctor(instance, error_class, c.name, msg, info)
        }
        return instance
    }

    error_class.is_inst = function cust_error_is_inst(instanace){
        return ( (instanace instanceof Error) && instanace.__cust_error_name__ === error_class.__cust_error_name__ )
    }
    
    // Declare error in namespace(s)
    _.each(_.isArray(opts.ns)?opts.ns:[opts.ns], function(ns){ ns[c.name] = error_class })

    return error_class

}

其他回答

以下是我支持es2015之前版本浏览器的解决方案。它不做任何花哨的原型调整,也不会破坏调试器。

/**  Custom Errors
    // Depends on underscore js
    // This will declare an CustError() class in both 'this' and '_exports' namespaces
    // ctor is optional
    declare_cust_error(function CustError(){}, {ns: [this, _exports], ctor: 
        function cust_err_ctor(instance, clazz, name, msg, info){
            q$.called(arguments)
        }
    })

    // Usage:
    // Second param (pojso) is optional
    try {
        throw CustError.create("foo", {k1: 'v1', k2: 'v2'})
    }catch(ex){
        if(CustError.is_inst(ex)){
            console.error("its a CustError", ex)
        } else {
            throw ex
        }
    }

**/
function declare_cust_error(error_class, opts){
    var p, c, cp
    if(!error_class||!(p=error_class.prototype))throw new Error("error_class must be a Class")
    try{
        c = p.constructor; cp = c.toString()
    }catch(ex){}
    if(!cp || cp.indexOf('function ') != 0 || cp.indexOf('[native code]') > 0)
        throw new Error("error_class must be a classic proto class (pre-es6) but got: " + error_class.toString())

    opts=opts||{}
    
    error_class.__is_cust_error__ = true
    error_class.__cust_error_name__ = c.name

    error_class.create = function cust_error_create(msg, info){
        var instance = new Error(msg)
        instance.info = info
        instance.__is_cust_error__ = true
        instance.__cust_error_name__ = c.name
        if(_.isFunction(opts.ctor)){
            opts.ctor(instance, error_class, c.name, msg, info)
        }
        return instance
    }

    error_class.is_inst = function cust_error_is_inst(instanace){
        return ( (instanace instanceof Error) && instanace.__cust_error_name__ === error_class.__cust_error_name__ )
    }
    
    // Declare error in namespace(s)
    _.each(_.isArray(opts.ns)?opts.ns:[opts.ns], function(ns){ ns[c.name] = error_class })

    return error_class

}

以不能使用instanceof为代价,下面的代码保留了原始的堆栈跟踪,并且没有使用任何非标准的技巧。

// the function itself
var fixError = function(err, name) {
    err.name = name;
    return err;
}

// using the function
try {
    throw fixError(new Error('custom error message'), 'CustomError');
} catch (e) {
    if (e.name == 'CustomError')
        console.log('Wee! Custom Error! Msg:', e.message);
    else
        throw e; // unhandled. let it propagate upwards the call stack
}

这部分的标准可以解释为什么错误。Apply调用不会初始化对象:

15.11.1错误构造函数作为函数调用 当Error作为函数而不是构造函数调用时,它会创建和 初始化一个新的Error对象。因此函数调用Error(…)是 等价于对象创建表达式new Error(… 相同的参数。

在这种情况下,Error函数可能确定它没有作为构造函数被调用,因此它返回一个新的Error实例,而不是初始化this对象。

用下面的代码进行测试似乎证明了这就是实际发生的情况:

function NotImplementedError() { 
   var returned = Error.apply(this, arguments);
   console.log("returned.message = '" + returned.message + "'");
   console.log("this.message = '" + this.message + "'");
}
NotImplementedError.prototype = new Error();

var nie = new NotImplementedError("some message");

运行此命令时输出如下:

returned.message = 'some message'
this.message = ''

这是最快的方法:

    let thisVar = false

    if (thisVar === false) {
            throw new Error("thisVar is false. It should be true.")
    }

构造函数需要类似于工厂方法,并返回您想要的内容。如果你需要额外的方法/属性,你可以在返回之前将它们添加到对象中。

function NotImplementedError(message) { return new Error("Not implemented", message); }

x = new NotImplementedError();

不过我不明白你为什么要这么做。为什么不直接使用new Error…? 自定义异常在JavaScript(或任何非类型化语言)中并没有真正添加太多内容。