JavaScript基础之继承「上」 | 8月更文挑战

114 阅读2分钟

前言

继承是 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

引入的问题:

  1. 引用类型的属性被所有实例共享。
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'  ];
  1. 在创建 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' ];

优点:

  1. 避免了引用类型的属性被所有实例共享
  2. 可以在 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 继承 的其它方式。