前言
继承是 JavaScript的核心基础,今天主要介绍 3 种基础的 JavaScript 继承方法
1. 原型链继承
让子类的原型指向父类的实例
每一个JavaScript对象(null除外)在创建的时候就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型"继承"属性
function Parent () { // 父类构造函数
this.name = "Jarry";
}
Parent.prototype.getName = function () { // 给Parent添加getName的属性方法
console.log(this.name);
}
function Child () { // 定义子类
}
// 每一个JavaScript对象(null除外)在创建的时候就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型"继承"属性。
Child.prototype = new Parent();
var new_child = new Child();
console.log( new_child.getName() ); // Jarry
引入的问题:
- 引用类型的属性被所有实例共享。
function Parent () {
this.names = [ 'Tom', 'Jarry' ];
}
function Child () {
}
Child.prototype = new Parent();
var child1 = new Child();
console.log(child1,name); // [ 'Tom', 'Jarry' ];
child1.names.push( 'Ketong' );
console.log(child1,name); // [ 'Tom', 'Jarry', 'Ketong' ];
console.log(child2.names === child1.names); // true 属于同一个引用
var child2 = new Child();
console.log(child2.name);// [ 'Tom', 'Jarry', 'Ketong' ];
- 在创建 Child 实例的时候,不能向Parent 传参
2.借用构造函数(经典继承)
在子类的构造函数中使用call();
function Parent(){
this.names = [ 'Tom', 'Jarry' ];
}
function Child () {
Parent.call(this);
}
var child1 = new Child();
child1.names.push( 'Ketong' );
var child2 = new Child();
console.log( child2.names ); // [ 'Tom', 'Jarry' ];
优点:
- 避免了引用类型的属性被所有实例共享
- 可以在 Child 中向 Parent 传参
举个栗子:
function Parent (name) {
this.name = name;
}
function Child (name){
Parent.call(this, name);
}
var child1 = new Child("Jarry");
console.log(child1.name); // Jarry
var child2 = new Child('Tom');
console.log(child2.name); // Tom
缺点:
方法都在构造函数中定义,每次创建实例都会创建一遍方法。
3.组合继承
原型链继承和经典继承的双剑合璧
function Parent (name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.getName = function () {
console.log(this.name);
}
function Child (name, age) {
Parent.call(this, name);
this.age = age;
}
Child.prototype = new Parent();
Child.prototype.constructor = Child; // 修复构造函数的指向
// console.log(boy1.constructor); // Parent
// 你会发现实例的构造函数居然是Parent。
// 而实际上,我们希望子类实例的构造函数是Child,所以要记得修复构造函数指向
var child1 = new Child("Jarry", "16");
child1.colors.push("black");
console.log(child1.name); // Jarry
console.log(child1.age); // 16
console.log(child1.colors); // ['red', 'blue', 'green', 'black'];
var child2 = new Child('Tom', '18');
console.log(child2.name); // Tom
console.log(child2.age); // 18
console.log(child2.colors); // ['red', 'blue', 'green'];
child1 和 child2 两个没有相互关联,修改彼此都不会影响
优点:
融合了原型链继承和构造函数的优点,是js中最常用的继承模式。
缺点:
调用了两次父类构造函数
一次是设置子类实例原型的时候:
Child.prototype = new Parent();
一次是在创建子类实例的时候
var child1 = new Child("Jarry", "16");
因为 new 的实现过程中,我们会执行
Parent.call(this, name);
所以我们又会调用一次 Parent 的构造函数
结语
本篇主要介绍了原型链继承、借用构造函数(经典继承) 和 组合继承 3 种继承方式,以及各自的优缺点。
如有错误之处,望指正🙏🙏
下一篇继续介绍 js 继承 的其它方式。