旧版JS运行时自定义异常类抛出实例丢失类型上下文问题求解
自定义异常类型校验失效问题原因及修复方案
问题核心原因
- 导入逻辑错误:ClassB默认导出的是
ClassB构造函数本身,ExceptionC仅作为ClassB.prototype上的属性存在,并未单独导出。ClassA中import ExceptionC from "./ClassB.js"拿到的实际是ClassB构造函数,和抛出的异常构造函数完全不是同一个引用,因此instanceof校验直接失效。 - 原型链继承完全缺失:自定义异常构造函数仅调用了父类的
call方法,未正确设置原型继承关系:AugmentedException没有继承Error.prototype,不满足JS原生异常的原型链要求ExceptionC没有继承AugmentedException.prototype,原型链断裂导致类型校验失效
- 作用域引用错误:
ClassB原型定义中直接调用AugmentedException、直接new ExceptionC时,会优先查找全局作用域下的对应变量,而非ClassB.prototype上挂载的属性,要么抛出引用错误,要么拿到错误的构造函数。 - 补充说明:
typeof返回object是正常表现,JS中所有非函数类型的对象(包括原生Error实例)执行typeof都会返回object,不属于异常问题。
修复方案
第一步:调整异常类定义与导出
不要将异常类挂载在ClassB.prototype上,单独定义并导出,同时补全原型继承逻辑(兼容旧JS运行环境,无需ES6+特性):
// ClassB.js // 定义基础自定义异常 function AugmentedException(errorMsg, origError) { Error.call(this); this.message = errorMsg; this.origError = origError; // 兼容支持堆栈捕获的环境 if (Error.captureStackTrace) { Error.captureStackTrace(this, AugmentedException); } } // 补全Error继承关系 AugmentedException.prototype = Object.create(Error.prototype); AugmentedException.prototype.constructor = AugmentedException; // 定义ExceptionC function ExceptionC(errorMsg, origError) { AugmentedException.call(this, errorMsg, origError); } // 补全AugmentedException继承关系 ExceptionC.prototype = Object.create(AugmentedException.prototype); ExceptionC.prototype.constructor = ExceptionC; // 导出内容 export default function ClassB() {}; export { ExceptionC, AugmentedException }; ClassB.prototype = { thrower: function() { console.log("throwing a new ExceptionC"); // 直接引用当前作用域的ExceptionC构造函数 throw new ExceptionC("foo", "bar"); } };
第二步:修正ClassA的导入逻辑
正确导入单独导出的ExceptionC构造函数:
// ClassA.js import ClassB from "./ClassB.js" // 正确导入ExceptionC构造函数 import { ExceptionC } from "./ClassB.js" export default function ClassA() {}; ClassA.prototype = { objB: new ClassB(), funcA: function() { try { this.objB.thrower(); } catch (thrownFromB) { console.log("Caught Exception. This exception instance should be an ExceptionC. It is type:" + typeof thrownFromB); if (thrownFromB instanceof ExceptionC) { console.log("ExceptionC message: " + thrownFromB.message + " origError: " + thrownFromB.origError); } // 极端旧环境兜底校验逻辑 else if (thrownFromB.constructor && thrownFromB.constructor.name === 'ExceptionC') { console.log("兜底校验生效,ExceptionC message: " + thrownFromB.message); } } } };
特殊场景兼容
如果必须将异常类挂载在ClassB.prototype上,可做如下调整:
- ClassB内部调用时增加
this前缀:
ExceptionC: function(errorMsg, origError) { this.AugmentedException.call(this, errorMsg, origError); }, thrower: function() { throw new this.ExceptionC("foo", "bar"); }
- ClassA中从
ClassB.prototype获取构造函数:
import ClassB from "./ClassB.js" const ExceptionC = ClassB.prototype.ExceptionC;
内容的提问来源于stack exchange,提问作者SPB
相关产品推荐
相关产品推荐

