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

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

}

其他回答

另一种替代方法可能并不适用于所有环境。至少可以保证它在nodejs 0.8中工作 这种方法使用了一种非标准的方法来修改内部原型道具

function myError(msg){ 
      var e = new Error(msg); 
      _this = this; 
      _this.__proto__.__proto__ = e;
}

如果有人想知道如何创建一个自定义错误并获得堆栈跟踪:

function CustomError(message) {
  this.name = 'CustomError';
  this.message = message || '';
  var error = new Error(this.message);
  error.name = this.name;
  this.stack = error.stack;
}
CustomError.prototype = Object.create(Error.prototype);

try {
  throw new CustomError('foobar');
}
catch (e) {
  console.log('name:', e.name);
  console.log('message:', e.message);
  console.log('stack:', e.stack);
}

这是我的实现:

class HttpError extends Error {
  constructor(message, code = null, status = null, stack = null, name = null) {
    super();
    this.message = message;
    this.status = 500;

    this.name = name || this.constructor.name;
    this.code = code || `E_${this.name.toUpperCase()}`;
    this.stack = stack || null;
  }

  static fromObject(error) {
    if (error instanceof HttpError) {
      return error;
    }
    else {
      const { message, code, status, stack } = error;
      return new ServerError(message, code, status, stack, error.constructor.name);
    }
  }

  expose() {
    if (this instanceof ClientError) {
      return { ...this };
    }
    else {
      return {
        name: this.name,
        code: this.code,
        status: this.status,
      }
    }
  }
}

class ServerError extends HttpError {}

class ClientError extends HttpError { }

class IncorrectCredentials extends ClientError {
  constructor(...args) {
    super(...args);
    this.status = 400;
  }
}

class ResourceNotFound extends ClientError {
  constructor(...args) {
    super(...args);
    this.status = 404;
  }
}

示例用法#1:

app.use((req, res, next) => {
  try {
    invalidFunction();
  }
  catch (err) {
    const error = HttpError.fromObject(err);
    return res.status(error.status).send(error.expose());
  }
});

示例用法#2:

router.post('/api/auth', async (req, res) => {
  try {
    const isLogged = await User.logIn(req.body.username, req.body.password);

    if (!isLogged) {
      throw new IncorrectCredentials('Incorrect username or password');
    }
    else {
      return res.status(200).send({
        token,
      });
    }
  }
  catch (err) {
    const error = HttpError.fromObject(err);
    return res.status(error.status).send(error.expose());
  }
});

尝试为用户定义的错误类型的每个实例创建一个新的原型对象。它允许instanceof检查像往常一样运行,并且在Firefox和V8 (Chome, nodejs)中正确报告类型和消息。

function NotImplementedError(message){
    if(NotImplementedError.innercall===undefined){
        NotImplementedError.innercall = true;
        NotImplementedError.prototype = new Error(message);
        NotImplementedError.prototype.name = "NotImplementedError";
        NotImplementedError.prototype.constructor = NotImplementedError;

        return new NotImplementedError(message);
    }
    delete NotImplementedError.innercall;
}

注意,一个额外的条目将在正确的堆栈之前。

以下是我支持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

}