vortesnail / blog

:blue_book: 个人技术小文章,旨在对知识的总结,能帮助到别人就更好啦。
551 stars 45 forks source link

原型链继承图解 #1

Open vortesnail opened 5 years ago

vortesnail commented 5 years ago

原型链继承详解

前言

在大多数面向对象编程语言中,继承是基于类来实现的,但是在JavaScript中是基于原型链来实现的,何为原型链,今天我们一步一步来解析它。

两个函数对象,父类型和子类型

我们首先来创建两个函数对象,并且希望子类型能继承父类型的某些方法,代码如下,很简单:

<script>
  //父类型
function Supper() {
  this.supProp = "Supper Property";
}

Supper.prototype.showSupperProperty = function() {
  console.log(this.supProp);
}

//子类型
function Sub() {
  this.subProp = "Sub Property";
}

Sub.prototype.showSubProperty = function() {
  console.log(this.subProp);
}

</script>

这段代码会在内存空间中有如图所示的内存分配: Untitled1.png

想要实现继承,我们根据 实例的隐式原型对象指向构造函数的显示原型对象 可知,要想要Sub的实例能访问Supper原型对象中的showSupperProperty函数对象,必须有一条原型链能指向上图中地址为0x234Object实例对象,我们先假如有一个Supper的实例对象已经创建,在图中体现如下: Untitled1 (1).png

好了,我们先不急,先创建一个Sub的实例看看,他在内存中如何指向的,在上述代码之后添加

var sub = new Sub();

有如下内存分配: Untitled1 (2).png

根据上图来说,我们的Sub的实例sub并没有与Object构成原型链,无法访问到Supper原型中的showSupperProperty方法,那我们到底该怎么呢? 

首先我们要清楚一点,sub.__proto__的值是怎么来的,Sub函数对象在定义时就已经有了Sub.prototype,而sub.__proto__是由Sub.prototype传递地址值给它而得到的值。我们要让sub._ ``_proto__`` === (Supper的某一个实例对象)不可以构成原型链了吗,需要下面关键的一句话:

Sub.prototype = new Supper();

这句话需要在子类型创建之后立马写上,为什么?你看之后画的图就知道了。 现在内存图如下: Untitled1 (3).png

由图已经可得到,有一条完整的原型链,故现在子类型的实例对象可以访问父类型的原型中的方法了。 若我们的

Sub.prototype.showSubProperty = function() {
  console.log(this.subProp);
}

不在Sub.prototype = new Supper();之后,那就等于白白丢掉了。。因为原来所指向的Object对象已经成为了垃圾对象。所以,完整的代码如下:

  <script>
    //父类型
    function Supper() {
      this.supProp = "Supper Property";
    }

    Supper.prototype.showSupperProperty = function() {
      console.log(this.supProp);
    }

    //子类型
    function Sub() {
      this.subProp = "Sub Property";
    }

    //子类型的原型为父类型的一个实例对象
    Sub.prototype = new Supper();
        //让子类型的原型的constructor指向子类型
        Sub.prototype.constructor = Sub;

    Sub.prototype.showSubProperty = function() {
      console.log(this.subProp);
    }

    var sub = new Sub();
    sub.showSupperProperty();
    sub.showSubProperty();

  </script>

得到完整图如下: Untitled1 (4).png

控制台输出验证了其正确性:

屏幕快照 2019-06-18 21.36.52.png

结语:这就是原型链继承了,图还是要多画多看几遍的,好好理解一下很有帮助。

下面简单给一个组合继承的代码,算是模板代码了,本身是借用构造函数继承(应该不能叫真正意义上的继承)和原型链继承的组合,重点是原型链继承,以上已经详细讲述:

  <script>
    function Person(name, age) {
      this.name = name;
      this.age = age;
    }

    Person.prototype.setName = function(name) {
      this.name = name;
    }

    function Student(name, age, price) {
      Person.call(this, name, age); //为了得到属性
      this.price = price;
    }

    Student.prototype = new Person();//为了能看到父类型的方法
    Student.prototype.constructor = Student;//修正constructor属性

    Student.prototype.setPrice = function(price) {
      this.price = price;
    }

    var stu1 = new Student('Bob', 18, 16000);
    stu1.setName('Jack');
    stu1.setPrice(20000);
    console.log(stu1.name, stu1.age, stu1.price);

  </script>