js继承

什么是继承?

  A对象通过继承B对象,就可以拥有B对象的所有属性和方法。

 

原型链继承:

  子类的原型是父类的实例,子类继承父类的所有私有属性、私有方法和其原型上的属性和方法。

        // 定义父类Person
        function Person(name,age){
            this.name=name;
            this.age=age;
            this.eat=function(){
                console.log("Person的私有eat方法");
            };
        }
        Person.prototype.play=function(){
            console.log("Person的公有play方法");
        };
        Person.prototype.sex="男";
        // 定义子类Student
        function Student(score){
            this.score=score;
            this.eat1=function(){
                console.log("Student的私有eat1方法");
            };
        }

        // 子类继承父类
        Student.prototype=new Person("wql",23);    // 子类的原型是父类的实例  ----无法向父类构造函数传参

        Student.prototype.sex1="女";
        Student.prototype.play1=function(){
            console.log("Student的公有play1方法");
        };

        // 实例化子类
        var s1=new Student(100);

        console.log(s1.score)   // 100
        console.log(s1.sex1)    //
        s1.eat1()   // Student的私有eat1方法
        s1.play1()  // Student的公有play1方法
        console.log(s1.name)    // wql
        console.log(s1.age)     // 23
        console.log(s1.sex)     //
        s1.eat()    // Person的私有eat方法
        s1.play()   // Person的公有play方法

  缺点:

    1、无法实现多继承

    2、来自原型对象的所有属性被所有实例共享

    3、创建子类时,无法向父类构造函数传参

    4、要想为子类新增属性和方法,必须要在Student.prototype=new Person()之后执行,不能放到构造器中

 

借用构造函数继承:

  在子类构造函数中通过call()调用父类构造函数。

        function Person(name,age){
            this.name=name;
            this.age=age;
            this.play=function(){
                console.log("play")
            };
        }
        Person.prototype.eat=function(){
            console.log("eat")
        };

        function Student(name,age,score){
            Person.call(this,name,age);  // 调用父类构造,可以向父类传参
            this.score=score;
        }
        var s1=new Student("wql",23,100);

        console.log(s1)     // Student {name: "wql", age: 23, score: 100, play: ƒ}
        console.log(s1.score)   // 100
        console.log(s1.name)    // wql
        console.log(s1.age)     // 23
        s1.play()   // play
        s1.eat()    // 语法错误  ----仅调用构造函数的方法不能继承父类的原型上的属性和方法

  缺点:

    1、实例不是父类的实例,只是子类的实例

    2、只能继承父类的实例属性和方法,不能继承父类的原型上属性和方法

    3、无法实现函数复用,每个子类都有父类实例函数的副本,影响性能

 

组合继承(原型链+借用构造函数):

  通过调用父类构造,继承父类的属性并保留传参的优点,然后把父类的实例作为子类的原型,实现函数复用。

        function Person(name,age){
            this.name=name;
            this.age=age;
            this.play=function(){
                console.log("play")
            }
        }
        Person.prototype.eat=function(){
            console.log("eat")
        }

        function Student(name,age,price){
            Person.call(this,name,age); // 调用父类构造  ----好处是可以向父类传参
            this.price=price;
            this.setScore=function(){
                console.log("setScore")
            }
        }
        Student.prototype=new Person(); // 父类的实例作为子类的原型
        Student.prototype.constructor=Student;  // 修复构造函数的指向
        Student.prototype.sayHello=function(){
            console.log("sayHello")
        }
        var s1=new Student("wql",23,100);
        var s2=new Student("syz",30,150);

        console.log(s1)     // Student {name: "wql", age: 23, price: 100, play: ƒ, setScore: ƒ}
        console.log(s2)     // Student {name: "syz", age: 30, price: 150, play: ƒ, setScore: ƒ}
        console.log(s1.name)     // wql
        console.log(s1.age)      // 23
        s1.play()     // play
        s1.eat()      // eat  ----父类的实例作为子类的原型,可以继承父类的原型属性和方法
        s1.setScore()     // setScore
        s1.sayHello()     // sayHello
        console.log(s1.constructor)     // 打印Student函数

  缺点:调用了两次父类构造函数,生成了两份实例。

 

  通过父类原型和子类原型指向同一个对象,子类可以继承父类的公有方法作为自己的公有方法,而且不会初始化两次实例方法/属性,避免了组合继承的缺点。

        function Person(name,age){
            this.name=name;
            this.age=age;
            this.play=function(){
                console.log("play")
            }
        }
        Person.prototype.eat=function(){
            console.log("eat")
        }
        function Student(name,age,price){
            Person.call(this,name,age);
            this.price=price;
            this.setScore=function(){
                console.log("setScore")
            }
        }
        Student.prototype=Person.prototype;  // 父类原型和子类原型指向同一个对象
        Student.prototype.sayHello=function(){
            console.log("sayHello")
        }
        var s1=new Student("wql",23,100);
        console.log(s1)     // Student {name: "wql", age: 23, price: 100, play: ƒ, setScore: ƒ}
        console.log(s1.name)     // wql
        console.log(s1.age)      // 23
        console.log(s1.price)    // 100
        s1.play()     // play
        s1.eat()      // eat
        s1.setScore()     // setScore
        s1.sayHello()     // sayHello

  缺点:无法确定实例是子类还是父类创造的,子类和父类的构造函数指向是同一个。

 

  原型可以基于已有的对象来创建对象,var B=Object.create(A);----以A对象为原型,生成B对象,B继承了A的所有属性和方法。

        function Person(name,age){
            this.name=name;
            this.age=age;
        }
        Person.prototype.eat=function(){
            console.log("eat")
        }
        function Student(name,age,price){
            Person.call(this,name,age);
            this.price=price;
            this.setScore=function(){
                console.log("setScore")
            }
        }
        Student.prototype=Object.create(Person.prototype);  // 将Person的原型复制给Student的原型
        Student.prototype.constructor=Student;  // 修复构造函数的指向

        var s1=new Student("wql",23,100);

        console.log(s1)     // Student {name: "wql", age: 23, price: 100, setScore: ƒ}
        console.log(s1.name)    // wql
        console.log(s1.age)     // 23
        console.log(s1.price)   // 100
        s1.eat()    // eat
        s1.setScore()   // setScore
        console.log(s1 instanceof Student,s1 instanceof Person)     // true true
        console.log(s1.constructor)     // 打印Student函数

  几乎完美!

 

ES6的class继承:

  通过extends关键字实现继承。

        // 定义父类
        class Person{
            constructor(name,age){
                this.name=name;
                this.age=age;
                this.play=function(){
                    console.log("play")
                }
            }
            showName(){
                console.log("调用父类的方法")
                console.log(this.name,this.age)
            }
        }
        let p1=new Person("wql",23);
        console.log(p1)     // Person {name: "wql", age: 23, play: ƒ}

        // 定义一个子类
        class Student extends Person{
            constructor(name,age,salary){
                super(name,age);    // 通过super调用父类的构造方法,继承来自父类的name、age属性和play方法
                this.salary=salary;
            }
            showName(){
                console.log("调用子类的方法")
                console.log(this.name,this.age,this.salary)
            }
        }
        let s1=new Student("syz",30,10000000);
        console.log(s1)     // Student {name: "syz", age: 30, salary: 10000000, play: ƒ}
        s1.showName()   // 调用子类的方法 syz 30 10000000
        s1.play()   // play

  Student通过extends关键字继承Person,在constructor中通过super()调用父类的构造方法。

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(js继承)