一、继承的本质
- 继承:子对象可以自动拥有父对象的属性和方法,就像孩子继承父母的基因。
- JavaScript 的继承:通过原型链实现(原型和原型链是底层核心)。
二、4 种常见继承方式
1. 原型链继承(传家宝模式)
- 原理:将父类的实例作为子类的原型
- 优点:子类能继承父类原型上的方法
- 缺点:所有子类实例共享父类的引用属性(如数组、对象)
function 父亲() {this.财产 = ["房子", "车子"];
}
父亲.prototype.姓氏 = "张";function 儿子() {}
儿子.prototype = new 父亲(); const 大儿子 = new 儿子();
大儿子.财产.push("存款");
const 二儿子 = new 儿子();
console.log(二儿子.财产);
2. 构造函数继承(复制家产模式)
- 原理:在子类构造函数中调用父类构造函数
- 优点:每个子类实例有独立的属性
- 缺点:无法继承父类原型上的方法
function 父亲(姓) {this.姓 = 姓;
}
父亲.prototype.说姓氏 = function() {console.log("我姓" + this.姓);
};function 儿子(姓) {父亲.call(this, 姓);
}const 小明 = new 儿子("王");
console.log(小明.姓);
小明.说姓氏();
3. 组合继承(家产+家训双传模式)
- 原理:原型链继承 + 构造函数继承组合
- 优点:既能继承属性,又能继承原型方法
- 缺点:父类构造函数被调用两次,效率略低
function 父亲(姓) {this.姓 = 姓;
}
父亲.prototype.说姓氏 = function() {console.log("我姓" + this.姓);
};function 儿子(姓, 名) {父亲.call(this, 姓); this.名 = 名;
}
儿子.prototype = new 父亲();
儿子.prototype.constructor = 儿子; const 小明 = new 儿子("张", "小明");
小明.说姓氏();
4. 寄生组合继承(完美模式)
- 原理:通过拷贝父类原型,避免重复调用父类构造函数
- 优点:解决组合继承的效率问题
- 推荐:ES6
class
继承的底层实现方式
function 继承原型(子类, 父类) {const 原型副本 = Object.create(父类.prototype); 原型副本.constructor = 子类; 子类.prototype = 原型副本;
}function 父亲(姓) {this.姓 = 姓;
}
父亲.prototype.说姓氏 = function() {console.log("我姓" + this.姓);
};function 儿子(姓, 名) {父亲.call(this, 姓); this.名 = 名;
}
继承原型(儿子, 父亲); const 小明 = new 儿子("李", "小明");
小明.说姓氏();
三、现代写法(ES6 class
语法糖)
class 父亲 {constructor(姓) {this.姓 = 姓;}说姓氏() {console.log(`我姓${this.姓}`);}
}class 儿子 extends 父亲 {constructor(姓, 名) {super(姓); this.名 = 名;}
}const 小明 = new 儿子("赵", "小明");
小明.说姓氏();
- 本质:
class
和 extends
是语法糖,底层基于寄生组合继承。
四、继承方式对比
继承方式 | 优点 | 缺点 | 适用场景 |
---|
原型链继承 | 简单 | 共享引用属性 | 小型项目 |
构造函数继承 | 独立属性 | 无法继承原型方法 | 需要属性隔离 |
组合继承 | 属性独立 + 继承原型方法 | 父类构造函数调用两次 | 传统项目 |
寄生组合继承 | 最优解 | 实现稍复杂 | 中大型项目 |
ES6 class | 语法简洁 | 需支持 ES6 | 现代项目 |
总结
- 核心原则:通过原型链实现属性和方法的传递。
- 推荐方案:
- 现代项目:直接用 ES6
class
- 传统项目:手动实现寄生组合继承
- 一句话记忆:继承就是「子承父业」,JavaScript 靠原型链实现! 👨👦