• Animal.call(this, name);到底是什么?(理解JS中的继承)


    前言

    在学习继承相关的知识点时,遇到了一个问题。
    下面这段代码中的Animal.call(this, name);是什么意思?为什么它就表示继承到了父类Animal的属性呢?

    function Animal(name) {
        this.name = name;
    }
    Animal.prototype.say = function() {
        return this.name;
    }
    function Cat(name, color) {
        Animal.call(this, name);
        this.color = color;
    }
    Cat.prototype = Object.create(Animal.prototype);
    Cat.prototype.constructor = Cat;
    var cat = new Cat('小黄', 'yellow');
    

    后来回顾了new操作符的知识点,这才豁然开朗。

    一、了解new操作符

    通过构造模式来创建对象的关键一步就是new操作符,它会根据构造函数创建实例对象。
    另外,很重要的是,实例化的过程中,this会指向实例对象,这样就可以将对应的属性和方法挂载到实例对象上。
    我们打印一下this,发现它确实是实例对象,而非默认的window。

    function Foo(name) {
        console.log(this);
        this.name = name;
    }
    var foo = new Foo('mm');
    

    二、了解call方法

    call方法是函数特有的,它用来改变this的指向。
    这背后的意思就是:把一个函数的方法绑定到相应的对象上,于是这个对象就就可以调用它。
    具体啥意思呢?看下代码:

    function add(x, y) {
        console.log('绑定对象是', this);
        return x + y;
    }
    var res1 = add(1, 2);
    console.log(res1);
    var obj = {};
    var res2 = add.call(obj, 3, 4);
    console.log(res2);
    


    虽然obj没有add方法,但是它通过call方法就可以调用了。观察发现,this从window改变到了obj身上!

    三、call方法与继承

    大家想想,利用this被改变的这个特点,我们只要写成构造函数的样子,不就能把一系列的属性和方法悄无声息地挂载到obj上了吗?!
    什么意思呢?比如this.name = name; 既然this改变成obj了,那么原来的代码就变成这样了 ===> obj.name = name;
    看下代码:

    function foo(name) {
        console.log('绑定对象是', this);
        this.name = name;
        this.say = function() {
            console.log(`我叫${this.name},我喜欢编程!`);
        }
    }
    foo(); // 直接调用foo,this指向window
    var obj = {};
    console.log('调用call方法前的obj:',obj);
    foo.call(obj, 'mm');
    console.log('调用call方法后的obj:',obj);
    


    在改变了this指向后,属性和方法挂载到了对象上,这不就相当于一种继承吗?

    四、继承

    再回过头来看下起初的代码:

    function Animal(name) {
        this.name = name;
    }
    Animal.prototype.say = function() {
        return this.name;
    }
    function Cat(name, color) {
        Animal.call(this, name);
        this.color = color;
    }
    Cat.prototype = Object.create(Animal.prototype);
    Cat.prototype.constructor = Cat;
    var cat = new Cat('小黄', 'yellow');
    

    第一步,将Cat函数的原型指向Animal的原型对象,这样Cat就继承了Animal原型上的say方法;

    Cat.prototype = Object.create(Animal.prototype);
    

    在改变了Cat的原型对象后,它的constructor属性也随之改变,指向了Animal,因此必须要把它转回来,所以,
    第二步,将Cat的原型对象指向本身(Cat);

    Cat.prototype.constructor = Cat;
    

    第三步,实例化Cat对象。

    var cat = new Cat('小黄', 'yellow');
    
    • 首先实例化对象,此时的this绑定到了Cat的实例对象;(然后依次执行以后的代码)
    • Animal.call(this, name);此时实例对象调用Animal方法,传入参数name,这样实例对象上就有了name;
    • this.color = color;接下来是Cat自身的属性color。将color挂载到实例对象上,这样实例对象上就有了color。
    • 返回实例对象。

    第四步,将实例对象赋值给cat。
    最后,cat是否继承了Animal,又是否有自己的属性color呢?

    console.log(cat);
    console.log(cat.say());
    


    OK,我们成功了。

    五、总结

    综上,理解Animal.call(this, name);的关键是:this绑定对象绑定的是那个实例对象,call方法实现了属性的继承。
    当然,如果是共享的属性或方法,还是利用原型链来继承比较好~

  • 相关阅读:
    多播委托与事件
    Linq 查询的演变过程
    Lamda表达式的前世今生
    微服务架构学习
    委托IL解析-----封装逻辑和代码复用
    ORM框架学习(四)
    ORM框架学习(三)
    Visual Studio 2010 旗舰版安装图解
    Microsoft SQL Server 2008 R2 中文安装说明
    3.0 面向对象 委托和事件 异常和错误
  • 原文地址:https://www.cnblogs.com/buildnewhomeland/p/12821258.html
Copyright © 2020-2023  润新知