说说JS如何实现继承

35 阅读2分钟

原型链

思想:基于原型链实现继承,将子类的原型对象设为父类的实例对象,使得子类可以访问父类的属性和方法。

function SuperType() {
  this.property = true;
}
SuperType.prototype.getSuperValue = function () {
  return this.property;
};
function SubType() {
  this.subproperty = false;
}
//实现继承
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function () {
  return this.subproperty;
};
let instance = new SubType();
console.log(instance.getSuperValue()); //true

痛点

1、父类对象的实例属性变成了子类的原型对象

2、子类在实例化对象时不能给父类的构造函数传参

function SuperType() {
  this.colors = ["red", "blue", "green"];
}
function SubType() {}
//实现继承
SubType.prototype = new SuperType();
let instance1 = new SubType();
let instance2 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors);//[ 'red', 'blue', 'green', 'black' ]
console.log(instance2.colors);//[ 'red', 'blue', 'green', 'black' ]

构造函数

思想:子类构造函数盗用父类的构造函数,利用call()改变this的指向。

SubType在生成实例对象时运行了SuperType构造函数中的所有初始代码,使得每个SubType对象都有自己的colors属性,同时SubType在生成实例对象时可以给SuperType的构造函数传递参数

function SuperType(_name) {
  this.colors = ["red", "blue", "green"];
  this.name = _name;
}
function SubType(_name) {
  SuperType.call(this, _name);
}

let instance1 = new SubType("lbw");
let instance2 = new SubType("white");
instance1.colors.push("black");
console.log(instance1.colors, instance1.name); //[ 'red', 'blue', 'green', 'black' ] lbw
console.log(instance2.colors, instance2.name); //[ 'red', 'blue', 'green' ] white

痛点:

1、函数必须定义在构造函数中,函数无法复用。每生成一个实例对象就会开辟新的内存空间,资源浪费

2、子类无法调用父类原型上的属性和方法

组合继承

原型链和构造函数结合体。

function SuperType(_name) {
  this.colors = ["red", "blue", "green"];
  this.name = _name;
}
SuperType.prototype.getName = function () {
  console.log(this.name);
};
function SubType(_name, age) {
  //盗用构造函数继承属性
  SuperType.call(this, _name);
  this.age = age;
}
//原型链继承方法
SubType.prototype = new SuperType();

SubType.prototype.getAge = function () {
  console.log(this.age);
};

let instance1 = new SubType("lbw", 18);
instance1.colors.push("black");
console.log(instance1.colors); //[ 'red', 'blue', 'green', 'black' ]
instance1.getName(); //lbw
instance1.getAge(); //18

let instance2 = new SubType("white", 28);
console.log(instance2.colors); //[ 'red', 'blue', 'green' ]
instance2.getName(); //white
instance2.getAge(); //28

痛点:

1、父类构造函数最终会执行两次,存在效率问题

2、子类原型对象上的属性和方法冗余了

function SuperType(_name) {
  this.colors = ["red", "blue", "green"];
  this.name = _name;
}
SuperType.prototype.getName = function () {
  console.log(this.name);
};
function SubType(_name, age) {
  SuperType.call(this, _name);//第二次执行
  this.age = age;
}

SubType.prototype = new SuperType();//第一次执行
//SubType.prototype存在多余的属性colors和name
SubType.prototype.getAge = function () {
  console.log(this.age);
};
const instance = new SubType("lbw", 18);
console.log(SubType.prototype.colors); //[ 'red', 'blue', 'green' ]

寄生组合继承

只调用一次父类函数的构造函数,同时避免了子类原型对象上多余的属性,目前实现继承的最优方式

function SuperType(_name) {
  this.colors = ["red", "blue", "green"];
  this.name = _name;
}
SuperType.prototype.getName = function () {
  console.log(this.name);
};
function SubType(_name, age) {
  SuperType.call(this, _name);
  this.age = age;
}

SubType.prototype = Object.create(SuperType.prototype);
//Object.create()返回一个空对象,函数参数将作为空对象的原型对象
SubType.prototype.constructor = SubType;

SubType.prototype.getAge = function () {
  console.log(this.age);
};

let instance1 = new SubType("lbw", 18);
instance1.colors.push("black");
console.log(instance1.colors); //[ 'red', 'blue', 'green', 'black' ]
instance1.getName(); //lbw
instance1.getAge(); //18

let instance2 = new SubType("white", 28);
console.log(instance2.colors); //[ 'red', 'blue', 'green' ]
instance2.getName(); //white
instance2.getAge(); //28

console.log(SubType.prototype.colors); //undefined