最稳妥方案是用object.create兜底+构造器修复,兼容ie9+及现代环境:先检测object.create可用性,不可用则用空函数模拟;子类原型设为parent.prototype副本而非实例;显式修复constructor;配合parent.call(this)实现实例属性隔离。

要实现一个跨环境兼容的原型继承函数,核心是绕开不同运行时对 __proto__、Object.setPrototypeOf 或 Object.create 的支持差异,同时确保子构造函数的 constructor 指向正确、原型链完整、不污染父类原型,并能安全处理引用类型属性共享问题。
用 Object.create 兜底 + 构造器修复
这是最稳妥的方案,兼容 IE9+ 及所有现代环境。关键在于:不直接赋值 Child.prototype = new Parent()(会执行父构造函数且共享引用属性),而是用 Object.create(Parent.prototype) 建立干净原型链,再手动修正 constructor。
- 先检测
Object.create是否可用;不可用则降级为临时构造函数模拟 - 设置子类原型为父类原型的副本,而非实例
- 显式恢复
Child.prototype.constructor = Child - 避免在原型上直接添加方法前未重置 constructor
兼容老环境的 Object.create 替代方案
IE8 及更早版本不支持 Object.create,可用空函数桥接:
- 定义一个空构造函数
function F() {} - 令
F.prototype = Parent.prototype - 返回
new F()作为子类原型 —— 这等效于浅拷贝原型对象,不触发父构造逻辑 - 注意:该方式无法继承父类原型上的
hasOwnProperty等自有属性,但可继承方法和可枚举属性
确保实例属性隔离与原型方法复用
仅靠原型继承不能解决实例属性(如数组、对象)共享问题。实际使用中必须配合构造函数继承(call/apply):
- 子类构造函数内调用
Parent.call(this, ...args),保证每个实例拥有独立的属性副本 - 原型上只放方法和不可变值(如字符串、数字常量),避免在
Parent.prototype上定义引用类型初始值 - 例如:
this.hobby = ['吃饭'];必须写在构造函数里,不能放在Parent.prototype中
ES5+ 推荐封装成可复用工具函数
把上述逻辑收拢为一个稳定函数,比如 inherit(Child, Parent):
- 内部自动判断环境能力,优先用
Object.create,fallback 到空函数方案 - 统一处理
constructor修复、__proto__避免(因非标准且部分环境不可写) - 返回布尔值标识是否成功,便于调试或降级提示
- 不依赖外部 polyfill,最小侵入,适合嵌入遗留系统











