扩展JavaScript内置对象时如何同时覆写两种调用形式的构造函数
实现方案
要兼容内置对象的两种调用方式,不能直接使用纯ES6 Class继承的写法,需要在外层包装一层普通函数做调用判断,核心实现如下:
// 自定义扩展Date构造器 function CustomDate(...args) { // 判断是否为new调用 if (new.target) { // 绑定正确的原型指向,生成自定义类的实例 return Reflect.construct(Date, args, new.target); } // 普通函数调用,直接返回时间字符串,和原生Date()行为一致 return new CustomDate(...args).toString(); } // 手动绑定原型链,完成对原生Date的继承 Object.setPrototypeOf(CustomDate, Date); CustomDate.prototype = Object.create(Date.prototype); CustomDate.prototype.constructor = CustomDate; // 新增自定义扩展方法 CustomDate.prototype.getFormattedDate = function() { return `${this.getFullYear()}-${String(this.getMonth() + 1).padStart(2, '0')}-${String(this.getDate()).padStart(2, '0')}`; }
效果验证
- 普通函数调用:
CustomDate()和原生Date()行为完全一致,返回当前时间的字符串表示 - new关键字调用:
const date = new CustomDate(2024, 5, 20)会生成CustomDate实例,可正常调用原生Date的所有方法,也可调用扩展方法date.getFormattedDate(),返回结果为2024-06-20
可选写法
如果更习惯用Class写扩展逻辑,可以把Class封装在内部,对外暴露普通函数做兼容:
// 内部用Class实现扩展逻辑 class _CustomDate extends Date { getFormattedDate() { return `${this.getFullYear()}-${String(this.getMonth() + 1).padStart(2, '0')}-${String(this.getDate()).padStart(2, '0')}`; } } // 对外暴露的兼容构造器 function CustomDate(...args) { if (new.target) return new _CustomDate(...args); return new _CustomDate(...args).toString(); } // 绑定原型链继承 Object.setPrototypeOf(CustomDate, _CustomDate); CustomDate.prototype = _CustomDate.prototype;
核心原理
- 普通函数支持带/不带new两种调用方式,规避了ES6 Class必须用new调用的语法限制
- 通过
new.target可以准确识别当前的调用形式,分别走不同的处理逻辑 - 手动设置原型链保证自定义构造器可以继承原生对象的所有静态方法、实例方法,新增的扩展能力也能正常挂载,不会污染全局内置对象的原型
内容的提问来源于stack exchange,提问作者barneyAgumble
相关产品推荐
相关产品推荐

