JavaScript实现继承的三种方式

147 阅读2分钟

JavaScript中主要有三种实现继承的方式,分别是 

  1. 构造函数继承 
  2. 原型继承 
  3. 组合继承

其中前两种方式都有其缺陷。第三种方式组合继承则将前两种方式结合起来,取长补短,是JS继承最常用的最佳实践。本文结合代码和注释逐一阐述三种继承方式。

1.构造函数继承 

构造函数继承的关键: 在Child构造函数中执行Parent.call(this)。  

缺点:

  • new多个Child时,Parent构造函数中的方法会在每个Child中拷贝一份,浪费内存

  • Parent原型对象上的方法不会被Child继承

    function Parent(name) { this.name = name; this.hobby = []; this.speak = function() { console.log("Parent speak"); } // 缺点1:new多个Child时,Parent构造函数中的方法会在每个Child中拷贝一份,浪费内存 }

    Parent.prototype.say = function() { console.log("Parent say"); } // 缺点2:Parent原型对象上的方法不会被Child继承

    function Child(name, type) { Parent.call(this, name); // 构造函数继承的关键 this.type = type; }

2.原型继承

原型继承的关键: 设置Child原型指向Parent,即Child.prototype = new Parent()。  

缺点:

  • Parent的引用属性会被所有Child实例共享,互相干扰

    function Parent(name) { this.name = name; this.hobby = []; // 缺点:Parent的引用属性会被所有Child实例共享,互相干扰 }

    Parent.prototype.say = function() { console.log("Parent say"); }

    function Child(type) { this.type = type; }

    Child.prototype = new Parent(); // 原型继承的关键

3.组合继承(最佳实践)

组合继承的关键:

  • 属性使用构造函数继承 —— 避免了原型继承中Parent引用属性被所有Child实例共享的缺陷。 

  • 方法使用原型继承 —— 避免了构造函数继承中方法重复拷贝、浪费内存的缺陷

    function Parent(name) { this.name = name; this.hobby = []; // 属性放在构造函数中 }

    Parent.prototype.say = function() { // 方法放在原型中 console.log("Parent say"); }

    function Child(name, type) { Parent.call(this, name); // Child继承Parent属性(构造函数继承) this.type = type; // Child扩展属性 }

    Child.prototype = Object.create(Parent.prototype); // Child继承Parent方法(原型继承) Child.prototype.speak = function() { // Child扩展方法 console.log("Child speak"); }

    Child.prototype.constructor = Child; // 修复Child的constructor指向,否则Child的constructor会指向Parent

补充:

对于组合继承代码中的Child.prototype = Object.create(Parent.prototype),还有两种常见的类似写法是Child.prototype = Parent.prototype和Child.prototype = new Parent(),但这两种写法都是有缺陷的,需要避免: 

Child.prototype = Parent.prototype,修改Child.prototype就等于修改Parent.prototype,会干扰所有Parent实例。 Child.prototype = new Parent(),Parent构造函数重复调用了两次(另一处调用是Child构造函数中的Parent.call(this)),浪费效率,且如果Parent构造函数有副作用,重复调用可能造成不良后果。

  1. Object.create()的使用-MDN

  2. 继承与原型链-MDN

  3. 讲解继承