JavaScritp利用原型对象实现继承

205 阅读2分钟
  1. 通过使用父构造的原型对象实现继承

    function Person(name, age){
      this.name = name;
      this.age = age;
      this.run = function(){
        console.log(this.name, ' is runing');
      }
    }
    
    Person.prototype.eat = function(){
      console.log(this.name, ' is eating')
    }
    
    function Student(name, age, sex){
      this.name = name;
      this.age = age;
      this.sex = sex;
    }
    
    Student.prototype = Person.prototype;
    let student = new Student('wang',12,'mail');
    student.eat(); // wang is eating
    student.run(); // undefined   即 run方法未定义
    

    直接使用父构造函数原型对象的方法有一个弊端,不能访问父构造函数实例上的方法,因为构造函数本身没有被实例化

  2. 通过父构造函数实例实现继承

    function Person(name, age){
      this.name = name;
      this.age = age;
      this.run = function(){
        console.log(this.name, ' is runing');
      }
    }
    
    Person.prototype.eat = function(){
      console.log(this.name, ' is eating')
    }
    function Student(name, age, sex){
      this.name = name;
      this.age = age;
      this.sex = sex;
    }
    Student.prototype = new Person('wang', 12);
    Student.prototype.say = function(){
      console.log(this.name, ' is saying')
    }
    let student = new Student('li', 33, 'femail')
    student.eat(); // li is eating
    student.run(); // li is runing
    student.say(); // li is saying
    let student2 = new Student('jiang',32, 'mail');
    student2.__proto__ === student.__proto__ // true   这里为true说明并没有重复实例化Person对象
    

    从继承的功能实现上来说,并没有什么问题,问题在于这种写法太过于蹩脚,并且无法调用父类的构造方法。

  3. 借用构造函数

    function Person(name, age){
      this.name = name;
      this.age = age;
      this.run = function(){
        console.log(this.name, ' is runing');
      }
    }
    
    Person.prototype.eat = function(){
      console.log(this.name, ' is eating')
    }
    function Student(name, age, sex){
    	Person.call(this,name,age)
      this.sex = sex;
    }
    Student.prototype.say = function(){
      console.log(this.name, ' is saying')
    }
    let student = new Student('li', 33, 'femail')
    student.eat(); // Error: student.eat is not a function
    student.run(); // li is runing
    student.say(); // li is saying
    
    

    这种方式不能调用父构造函数的原型上定义的方法,因此,我们需要对此作出改进

    function Student(name, age, sex){
    	Person.call(this,name,age)
      this.sex = sex;
    }
    Student.prototype = Person.prototype;
    Student.prototype.say = function(){
      console.log(this.name, ' is saying')
    }
    let student = new Student('li', 33, 'femail')
    student.eat(); // li is eating
    student.run(); // li is runing
    student.say(); // li is saying
    

    这种方式保证了继承功能的完整性,似乎完美解决了继承的问题,但是还存在一个问题,Student.prototype的构造函数指向发生了改变,此时指向了Person构造函数,因此我们需要将构造函数重定向。

    function Student(name, age, sex){
    	Person.call(this,name,age)
      this.sex = sex;
    }
    Student.prototype = Person.prototype;
    Student.prototype.constructor = Student;
    Student.prototype.say = function(){
      console.log(this.name, ' is saying')
    }
    let student = new Student('li', 33, 'femail')
    student.eat(); // li is eating
    student.run(); // li is runing
    student.say(); // li is saying
    

    OK, 我们似乎又一次完美实现了JavaScript利用原型实现继承的问题。但是还没有,因为Student.prototype === Person.prototype,我们更改了Student.prototype的原型对象指向的构造函数,那么Person.prototype指向的构造函数也发生了改变,所以我们还需要做一点操作。

    function Student(name, age, sex){
    	Person.call(this,name,age)
      this.sex = sex;
    }
    Student.prototype = Object.create(Person.prototype);
    Student.prototype.constructor = Student;
    Student.prototype.say = function(){
      console.log(this.name, ' is saying')
    }
    let student = new Student('li', 33, 'femail')
    student.eat(); // li is eating
    student.run(); // li is runing
    student.say(); // li is saying
    Student.prototype  === Person.prototype
    

    这样,Person.prototype 和 Student.prototype都会指向各自的constructor,并且对各自的原型对象进行修改,都互不影响。

    至此,才算完美的实现了JavaScript利用原型对象实现继承的问题。

    补充:如果我们的需求只是想继承父构造函数通过原型共享出来的方法,那么做法如下

    function Person (name, age) {
        this.name = name;
        this.age = age;
        this.run = function(){
            console.log(this.name, ' is runing');
        }
    }
    Person.prototype.eat = () => {console.log('i can eat anything')}
    
    function Student(course){
        this.course = course;
    }
    Student.prototype = Person.prototype;
    Student.prototype.constructor= Student;
    let student = new Student('语文');
    student.eat() // 输出: i can eat anything