JavaScript高级-----4.继承

2. 继承

2.1 call()

  • 可以调用某个函数
  • 可以修改函数运行时的this指向(注意是修改此次运行时的this指向,而不是永久的this指向)

<script>
    // call 方法
    function fn(x, y) {
        console.log(this); //修改之前this指向的是window,修改之后this指向的是o这个对象
        console.log(x + y); //3
    }
    var o = {
        name: 'andy'
    };
    // fn();传统的函数调用方法
    // 1. call() 可以调用函数
    fn.call(); //Window {parent: Window, opener: null, top: Window, length: 0, frames: Window, …}

    // 2. call() 可以改变这个函数的this指向 此时这个函数的this 就指向了o这个对象
    fn.call(o, 1, 2); //{name: "andy"}
</script>

2.2 利用构造函数继承父类的属性

ES6之前是利用构造函数继承父类的属性;利用原型对象继承父类的方法。
利用构造函数继承父类的属性的核心方法是:通过call把父类型的this指向子类型的this,这样就可以实现子类型继承父类型的属性。

<script>
    //借用父构造函数继承属性
    //1. 父构造函数
    function Father(uname,age){
        //这里的this原本指向父构造函数的实例对象
        this.uname = uname;
        this.age = age;
    }

    //2. 子构造函数
    function Son(uname, age){
        //这里的this指向子构造函数的对象实例
    }
</script>

现在上述两个构造函数没有任何关系,那么子构造函数如何才可以使用父构造函数的两个属性呢?思路:将父构造函数中的this指向改成子构造函数的实例对象。

<script>
    // 借用父构造函数继承属性
    // 1. 父构造函数
    function Father(uname, age) {
        // this 指向父构造函数的对象实例father
        console.log(this);
        this.uname = uname;
        this.age = age;
    }


    // 2 .子构造函数 
    function Son(uname, age, score) {
        // this 指向子构造函数的对象实例
        Father.call(this, uname, age); //在调用父类构造方法的同时,将父类的this指向改为son
        this.score = score; //这是子构造函数独有的属性
    }
    var son = new Son('刘德华', 18, 100); //Son {uname: "刘德华", age: 18, score: 100}
    console.log(son); //Son {uname: "刘德华", age: 18, score: 100}

    var farther = new Father('张学友', 20); //父构造函数的this指向还是指向父构造函数的实例对象 Father {uname: "张学友", age: 20}
</script>

在子构造函数中写上Father.call(this, uname, age);这行代码就相当于将父构造函数的代码

console.log(this);
this.uname = uname;
this.age = age;

搬移到子构造函数中。子构造函数就相当于变成了

 // 2 .子构造函数 
    function Son(uname, age, score) {
        // this 指向子构造函数的对象实例
        console.log(this);
        this.uname = uname;
        this.age = age;
        this.score = score; //这是子构造函数独有的属性
    }

2.3 借用原型对象继承父类型方法

之前提到:共有的属性写在构造函数里面,共有的方法写在原型对象里
(1)在父亲的原型对象中添加一个公共方法money()

<script>
    // 借用父构造函数继承属性
    // 1. 父构造函数
    function Father(uname, age) {
        this.uname = uname;
        this.age = age;
    }
    //父构造函数的方法
    Father.prototype.money = function() {
        console.log(1000000);
    }


    // 2 .子构造函数 
    function Son(uname, age, score) {
        Father.call(this, uname, age);
        this.score = score;
    }

    var son = new Son('刘德华', 18, 100);
    console.log(son);
</script>

通过打印儿子可以看出,儿子没有继承父类的money(),儿子只是调用了父亲的构造函数,没有调用父亲的原型对象,所以儿子找不到父亲的money()

(2)若将父构造函数的原型对象赋值给子构造函数的原型对象,如下:

<script>
    // 借用父构造函数继承属性
    // 1. 父构造函数
    function Father(uname, age) {
        this.uname = uname;
        this.age = age;
    }
    //父构造函数的方法
    Father.prototype.money = function() {
        console.log(1000000);
    }


    // 2 .子构造函数 
    function Son(uname, age, score) {
        Father.call(this, uname, age);
        this.score = score;
    }

    //若将父构造函数的原型对象赋值给子构造函数的原型对象,如下
    Son.prototype = Father.prototype;

    var son = new Son('刘德华', 18, 100);
    console.log(son);
</script>

打印儿子,可以发现在儿子的对象原型的指向中(指向Son构造函数的对象原型)果然出现了money方法

(3)再追加子构造函数专门的方法

<script>
    // 借用父构造函数继承属性
    // 1. 父构造函数
    function Father(uname, age) {
        this.uname = uname;
        this.age = age;
    }
    //父构造函数的方法
    Father.prototype.money = function() {
        console.log(1000000);
    }


    // 2 .子构造函数 
    function Son(uname, age, score) {
        Father.call(this, uname, age);
        this.score = score;
    }

    //若将父构造函数的原型对象赋值给子构造函数的原型对象,如下
    Son.prototype = Father.prototype;
    //子构造函数专门的方法
    Son.prototype.exam = function() {
        console.log("考试");
    }

    var son = new Son('刘德华', 18, 100);
    console.log(son);
    //此时再打印一下父构造函数的原型对象
    console.log(Father.prototype);
</script>

此时出现问题:子构造函数的原型对象有从父构造函数中继承过来的money()也有自己独有的exam(),但是父构造函数的原型对象中也有子构造函数原型对象独有的方法exam()

出错原因:Son.prototype = Father.prototype;相当于将父原型对象的地址给子原型对象,即让子原型对象指向父原型对象。此时若修改子原型对象,那么父原型对象回一起被修改。那么正确的继承方法是什么呢?

<script>
    // 借用父构造函数继承属性
    // 1. 父构造函数
    function Father(uname, age) {
        // this 指向父构造函数的对象实例father

        this.uname = uname;
        this.age = age;
    }
    //父构造函数的方法
    Father.prototype.money = function() {
        console.log(1000000);
    }


    // 2 .子构造函数 
    function Son(uname, age, score) {
        // this 指向子构造函数的对象实例
        Father.call(this, uname, age); //在调用父类构造方法的同时,将父类的this指向改为son
        this.score = score; //这是子构造函数独有的属性
    }
    
    //正确的继承方法
    Son.prototype = new Father(); 
 
    //子构造函数专门的方法
    Son.prototype.exam = function() {
        console.log("考试");
    }

    var son = new Son('刘德华', 18, 100);
    console.log(son); 
    console.log(Father.prototype);
</script>

说明:Son.prototype = new Father();

  • 父构造函数创建了一个实例对象,同时将其地址赋值给Son的原型对象,即让Son的原型对象指向这个Father的实例对象
  • 而Father的实例对象可以通过自己的对象原型__proto__访问Father的原型对象prototype(链式查找)
  • 而Father的原型对象prototype中存放着money这个方法


上述代码console.log(son);的执行结果

console.log(Father.prototype);打印结果如下,父亲的原型对象中没有多添加子类的exam()

但是打印console.log(Son.prototype.constructor);的时候,发现Son.prototype中的constructor指回的是父亲的构造函数

原因:Son.prototype = new Father();等同于Son.prototype={}说明Son.prototype中的方法完全被覆盖了,Son就没有自己原先的constructor,修改办法:Son.prototype.constructor = Son;如下

<script>
    // 借用父构造函数继承属性
    // 1. 父构造函数
    function Father(uname, age) {
        // this 指向父构造函数的对象实例father

        this.uname = uname;
        this.age = age;
    }
    //父构造函数的方法
    Father.prototype.money = function() {
        console.log(1000000);
    }


    // 2 .子构造函数 
    function Son(uname, age, score) {
        // this 指向子构造函数的对象实例
        Father.call(this, uname, age); //在调用父类构造方法的同时,将父类的this指向改为son
        this.score = score; //这是子构造函数独有的属性
    }

    Son.prototype = new Father(); 

    //修改办法:如果利用对象的形式修改了原型对象,别忘了用constructor指回原来的构造函数
    Son.prototype.constructor = Son;

    //子构造函数专门的方法
    Son.prototype.exam = function() {
        console.log("考试");
    }

    var son = new Son('刘德华', 18, 100);
    console.log(son); //Son {uname: "刘德华", age: 18, score: 100}
    console.log(Father.prototype);
    console.log(Son.prototype.constructor); //!!!指向了父构造函数
</script>
posted @ 2020-02-29 18:19  deer_cen  阅读(139)  评论(0编辑  收藏  举报