出于某种原因,在下面的代码段中,构造函数委托似乎不起作用:
function NotImplementedError() {
Error.apply(this, arguments);
}
NotImplementedError.prototype = new Error();
var nie = new NotImplementedError("some message");
console.log("The message is: '"+nie.message+"'")
运行该命令得到的消息是:"。有什么想法,为什么,或者是否有更好的方法来创建一个新的错误子类?是否有一个问题,应用到本机错误构造函数,我不知道?
这部分的标准可以解释为什么错误。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 = ''
以下是我支持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
}
以上所有的答案都很糟糕,真的。即使是107个向上的那个!真正的答案在这里:
从Error对象继承- message属性在哪里?
TL; diana:
a .没有设置message的原因是Error是一个函数,它返回一个新的Error对象,并且不以任何方式操作它。
B.正确的方法是从构造函数返回apply的结果,以及以通常复杂的javascript方式设置原型:
function MyError() {
var temp = Error.apply(this, arguments);
temp.name = this.name = 'MyError';
this.message = temp.message;
if(Object.defineProperty) {
// getter for more optimizy goodness
/*this.stack = */Object.defineProperty(this, 'stack', {
get: function() {
return temp.stack
},
configurable: true // so you can change it if you want
})
} else {
this.stack = temp.stack
}
}
//inherit prototype using ECMAScript 5 (IE 9+)
MyError.prototype = Object.create(Error.prototype, {
constructor: {
value: MyError,
writable: true,
configurable: true
}
});
var myError = new MyError("message");
console.log("The message is: '" + myError.message + "'"); // The message is: 'message'
console.log(myError instanceof Error); // true
console.log(myError instanceof MyError); // true
console.log(myError.toString()); // MyError: message
console.log(myError.stack); // MyError: message \n
// <stack trace ...>
//for EMCAScript 4 or ealier (IE 8 or ealier), inherit prototype this way instead of above code:
/*
var IntermediateInheritor = function() {};
IntermediateInheritor.prototype = Error.prototype;
MyError.prototype = new IntermediateInheritor();
*/
您可能会使用一些技巧来枚举tmp Error的所有不可枚举属性来设置它们,而不是显式地只设置stack和message,但是ie<9不支持这种技巧
更新代码,将原型分配给Error。Prototype和instanceof以及你的断言工作。
function NotImplementedError(message = "") {
this.name = "NotImplementedError";
this.message = message;
}
NotImplementedError.prototype = Error.prototype;
不过,我会抛出你自己的对象并检查name属性。
throw {name : "NotImplementedError", message : "too lazy to implement"};
根据评论进行编辑
在看了评论并试图记住为什么我将prototype分配给Error后。而不是像Nicholas Zakas在他的文章中所做的new Error(),我用下面的代码创建了一个jsFiddle:
function NotImplementedError(message = "") {
this.name = "NotImplementedError";
this.message = message;
}
NotImplementedError.prototype = Error.prototype;
function NotImplementedError2(message = "") {
this.message = message;
}
NotImplementedError2.prototype = new Error();
try {
var e = new NotImplementedError("NotImplementedError message");
throw e;
} catch (ex1) {
console.log(ex1.stack);
console.log("ex1 instanceof NotImplementedError = " + (ex1 instanceof NotImplementedError));
console.log("ex1 instanceof Error = " + (ex1 instanceof Error));
console.log("ex1.name = " + ex1.name);
console.log("ex1.message = " + ex1.message);
}
try {
var e = new NotImplementedError2("NotImplementedError2 message");
throw e;
} catch (ex1) {
console.log(ex1.stack);
console.log("ex1 instanceof NotImplementedError2 = " + (ex1 instanceof NotImplementedError2));
console.log("ex1 instanceof Error = " + (ex1 instanceof Error));
console.log("ex1.name = " + ex1.name);
console.log("ex1.message = " + ex1.message);
}
控制台输出如下。
undefined
ex1 instanceof NotImplementedError = true
ex1 instanceof Error = true
ex1.name = NotImplementedError
ex1.message = NotImplementedError message
Error
at window.onload (http://fiddle.jshell.net/MwMEJ/show/:29:34)
ex1 instanceof NotImplementedError2 = true
ex1 instanceof Error = true
ex1.name = Error
ex1.message = NotImplementedError2 message
这证实了我遇到的“问题”是错误的堆栈属性是创建new error()的行号,而不是抛出e发生的地方。然而,这可能比NotImplementedError.prototype.name = "NotImplementedError"行影响Error对象的副作用要好。
另外,注意NotImplementedError2,当我没有显式地设置.name时,它等于“Error”。然而,正如评论中提到的,因为该版本将prototype设置为new Error(),所以我可以设置NotImplementedError2.prototype.name = "NotImplementedError2"并OK。