Skip to content
字数
1448 字
阅读时间
7 分钟

原型链继承

将父类的实例作为子类的原型

js
function Parent() {
   this.isShow = true
   this.info = {
       name: "yhd",
       age: 18,
   };
}

Parent.prototype.getInfo = function() {
   console.log(this.info);
   console.log(this.isShow); // true
}

function Child() {};
Child.prototype = new Parent();

let Child1 = new Child();
Child1.info.gender = "男";
Child1.getInfo();  // {name: "yhd", age: 18, gender: "男"}

let child2 = new Child();
child2.getInfo();  // {name: "yhd", age: 18, gender: "男"}
child2.isShow = false

console.log(child2.isShow); // false

优点:

  • 父类方法可以复用
    缺点:
  • 父类的所有 引用属性 会被子类共享,更改一个子类的引用属性,会导致其他子类的引用属性一同更改
  • 子类实例不能给父类构造函数传参

(盗用) 构造函数继承

在子类中调用父类的构造函数的同时,通过 applycall 绑定作用域

js
function Parent(name) {
    this.info = { name: name };
}
function Child(name) {
    //继承自Parent,并传参
    Parent.call(this, name);
    
     //实例属性
    this.age = 18
}

let child1 = new Child("yhd");
console.log(child1.info.name); // "yhd"
console.log(child1.age); // 18

let child2 = new Child("wxb");
console.log(child2.info.name); // "wxb"
console.log(child2.age); // 18

优点:

  • 可以在子类构造函数中向父类传参
  • 父类的引用类型不会被共享
    缺点:
  • 子类不能访问父类原型上定义的方法,即父类 Parent.prototype 上的方法

组合继承

结合原型链继承和构造函数继承
image.png

js
function Parent(name) {
   this.name = name
   this.colors = ["red", "blue", "yellow"]
}
Parent.prototype.sayName = function () {
   console.log(this.name);
}

function Child(name, age) {
   // 继承父类属性
   Parent.call(this, name)
   this.age = age;
}
// 继承父类方法
Child.prototype = new Parent();

Child.prototype.sayAge = function () {
   console.log(this.age);
}

let child1 = new Child("yhd", 19);
child1.colors.push("pink");
console.log(child1.colors); // ["red", "blue", "yellow", "pink"]
child1.sayAge(); // 19
child1.sayName(); // "yhd"

let child2 = new Child("wxb", 30);
console.log(child2.colors);  // ["red", "blue", "yellow"]
child2.sayAge(); // 30
child2.sayName(); // "wxb"

优点:

  • 父类的方法可以复用
  • 可以在 Child 构造函数中向 Parent 构造函数中传参
  • 父类构造函数中的引用属性不会被共享

原型继承

对参数对象的一种浅复制 —— Object.create()
image.png

js
function objectCopy(obj) {
  function Fun() { };
  Fun.prototype = obj;
  return new Fun()
}

let person = {
  name: "yhd",
  age: 18,
  friends: ["jack", "tom", "rose"],
  sayName:function() {
    console.log(this.name);
  }
}

let person1 = objectCopy(person);
person1.name = "wxb";
person1.friends.push("lily");
person1.sayName(); // wxb

let person2 = objectCopy(person);
person2.name = "gsr";
person2.friends.push("kobe");
person2.sayName(); // "gsr"

console.log(person.friends); // ["jack", "tom", "rose", "lily", "kobe"]

优点:

  • 父类方法可复用
    缺点:
  • 父类的引用会被所有子类所共享
  • 子类实例不能向父类传参

寄生式继承

在原型式继承上进行改造

js
function objectCopy(obj) {
  function Fun() { };
  Fun.prototype = obj;
  return new Fun();
}

function createAnother(original) {
  let clone = objectCopy(original);
  clone.getName = function () {
    console.log(this.name);
  };
  return clone;
}

let person = {
     name: "yhd",
     friends: ["rose", "tom", "jack"]
}

let person1 = createAnother(person);
person1.friends.push("lily");
console.log(person1.friends);
person1.getName(); // yhd

let person2 = createAnother(person);
console.log(person2.friends); // ["rose", "tom", "jack", "lily"]

寄生组合继承

image.png

js
function objectCopy(obj) {
  function Fun() { };
  Fun.prototype = obj;
  return new Fun();
}

function inheritPrototype(child, parent) {
  let prototype = objectCopy(parent.prototype); // 创建对象
  prototype.constructor = child; // 增强对象
  Child.prototype = prototype; // 赋值对象
}

function Parent(name) {
  this.name = name;
  this.friends = ["rose", "lily", "tom"]
}

Parent.prototype.sayName = function () {
  console.log(this.name);
}

function Child(name, age) {
  Parent.call(this, name);
  this.age = age;
}

inheritPrototype(Child, Parent);
Child.prototype.sayAge = function () {
  console.log(this.age);
}

let child1 = new Child("yhd", 23);
child1.sayAge(); // 23
child1.sayName(); // yhd
child1.friends.push("jack");
console.log(child1.friends); // ["rose", "lily", "tom", "jack"]

let child2 = new Child("yl", 22)
child2.sayAge(); // 22
child2.sayName(); // yl
console.log(child2.friends); // ["rose", "lily", "tom"]

优点:

  • 只调用一次父类构造函数
  • Child 可以向 Parent 传参
  • 父类方法可以复用
  • 父类的引用属性不会被共享

与 extends 继承区别

  1. 语法差异

    • 寄生组合继承:寄生组合继承是一种通过组合构造函数继承和原型链继承的方式来实现继承。在代码中,你需要手动编写继承的逻辑,包括借用构造函数继承和设置原型链。
    • ES6 中的 extends:ES6 引入了 extends 关键字,使得继承变得更加直观和简洁。你可以通过 classextends 关键字来声明一个类,并直接继承另一个类,不需要手动编写继承逻辑。
  2. 语法使用

    • 寄生组合继承:使用寄生组合继承时,你需要手动创建构造函数、调用父类构造函数、设置原型链等步骤。通常需要创建一个中间函数来实现继承。
    • ES6 中的 extends:使用 ES6 中的 extends 关键字,你只需要声明一个类,并使用 extends 关键字指定父类,就可以直接继承父类的属性和方法,更加简洁。
  3. super() 的处理

    • 寄生组合继承:在寄生组合继承中,需要手动调用父类的构造函数来设置继承的属性,通常通过在子类构造函数中使用 Parent.call(this, ...args) 来实现。
    • ES6 中的 extends:在 ES6 中,使用 extends 关键字继承的子类,会自动调用父类的构造函数,不需要手动处理。在子类的构造函数中,使用 super(...args) 来调用父类的构造函数,以初始化父类的属性。
  4. 兼容性

    • 寄生组合继承:寄生组合继承是一种在 ES5 中实现继承的方式,兼容性较好,可以在大多数浏览器中运行。
    • ES6 中的 extends:ES6 中的 extends 关键字是一种新的语法糖,需要支持 ES6 的运行环境才能使用,不支持 ES6 的浏览器可能会报错

参考

JS继承 原型链继承、构造函数继承、组合继承、原型继承、寄生式继承、寄生组合继承 - 掘金

图解JS中的六种继承:原型链、盗用构造函数、组合继承、原型式继承、寄生继承、组合寄生继承 - 掘金

贡献者

The avatar of contributor named as jiechen jiechen

页面历史

撰写