美文网首页前端开发那些事儿
JavaScript的this(二)

JavaScript的this(二)

作者: 踏莎行 | 来源:发表于2021-06-26 21:09 被阅读0次

    this在类中的表现

    类的本质还是一个函数
    先来写一个简单的类,通过new出来的实例是一个对象

    class Test {
    
    }
    const t = new Test()
    console.log(t);  // Test {}
    

    我们在类中先添加一个静态方法

    class Test {
      say(){
        console.log(this); // Test {}
      }
    }
    const t = new Test()
    t.say()
    

    结果就是类里面的方法指向的是这个类实例化出来的对象
    类里面还有一个构造器,在类的构造器中也定义一个say方法

    class Test {
      constructor(){
        this.say = function(){
          console.log("非静态方法" + this);
        }
      }
      say(){
        console.log("静态方法" + this);
      }
    }
    const t = new Test()
    t.say() // 非静态方法[object Object]
    

       打印的结果表明,t.say()执行的是构造器里面的say方法。因为在类的构造器中添加的方法叫做非静态方法,在类实例化的时候会生成一个this指向一个空对象,构造器的非静态方法以及添加的属性都会添加到这个空对象中;而静态方法就是不是在构造器中定义的方法在类定义的时候就放到Test.prototype这个对象中去了

    new Test()  -> this -> {
      say(){
        console.log("静态方法" + this);
      }
    }
    
    class Test { constructor () {...}  say () {...}} 
    ->此时 Test.prototype = {
      say (){
         console.log("静态方法" + this);
      }
    }
    

       而类在new的时候,生成了this的新的指向,指向了一个空对象{},这个空对象是有自己的 __ proto __ 属性,这个属性又指向了Test.prototype,就是原型链的一个过程

    new 产生的this = {
      __ proto __ : Test.prototype
    }
    

       就是类的实例在执行say的时候,先在自己this指向的这个对象中找,如果有就执行,如果没有say方法就顺着 __ proto __ 指向的Test.prototype对象中找,如果有就执行,没有就继续沿着 __ proto __ 想上找,直到 Object.prototype停止

       一般情况下,对象都有 __ proto __ 这个属性,指向构造他的类或者构造函数的原型属性,除非是通过方法修改了对象的原型,如通过Object.create(null)将创建的对象的原型指定为null


    Snipaste_2021-06-26_20-21-27.png

    继承

    定义一个父类,一个子类继承父类,各定义一个方法,子类的方法中调用父类的方法,子类能否执行父类方法,子类的this指向哪?

    class Father {
      shop () {
        console.log('go shopping');
      }
    }
    
    class Son extends Father {
      study () {
        console.log(this); // Son {}
        this.shop() // go shopping
      }
    }
    
    const s1 = new Son()
    s1.study()
    

       子类可以调用父类方法,this指向的实例化的对象。而且Son{}上面只有 __ proto __ ,指向了class Father,而且shop方法也不是在Father对象里面,而是在Father的原型上


    Snipaste_2021-06-26_20-37-19.png

       所以s1.study()执行顺序就是先访问Son原型上面的study方法,然后访问Father原型属性上面的shop方法,说白了就是沿着原型链向上找。现在在Father的构造器的this上添加一个age属性

    class Father {
      constructor(){
        this.age = 44
      }
      shop () {
        console.log('go shopping');
      }
    }
    

       Son的实例对象是访问不到的,基类在继承的过程中是没有this绑定的,并没有对Father进行实例化。实际上Son的this是继承了Father的原型而来的,指向还是Son的实例。那现在在子类的构造器的this上添加一个属性

    class Son extends Father {
      constructor(){
        this.name = 'zhang'
      }
      study () {
        console.log(this);
        this.shop()
      }
    }
    

    一执行就会报错

    ReferenceError: Must call super constructor in derived
     class before accessing 'this' or returning from derived constructor
    

    意思就是在访问派生类就是子类在构造器中访问this之前要调用supper。不论Father有无constructor,子类要访问this,都必须调用supper

    class Father {
      constructor(){
        this.age = 44
      }
      shop () {
        console.log('go shopping');
      }
    }
    
    class Son extends Father {
      constructor(){
        super()
        this.name = 'zhang'
      }
      study () {
        console.log(this); // Son { age: 44, name: 'zhang' }
        this.shop() // go shopping
      }
    }
    
    const s1 = new Son()
    s1.study()
    

    此时就不报错了,而且this中也有了两个属性,一个是父类的age,一个是我们自己添加的name,在访问this.age就有东西了

    class Son extends Father {
      constructor(){
        super()
        this.name = 'zhang'
        console.log(this.age); // 44
      }
      ...
    }
    

    那么supper在这里做了什么呢??
       首先时调用了父类的constructor,生成了this绑定,通过上面的stydy方法打印出的this:Son { age: 44, name: 'zhang' }可以看出Father内部的this指向了Son的实例,就是相当于当前this执行了new
    Father,生成了一个对象{},然后父类构造器的属性也会添加到这个{}中去 -> {age: 44},然后我们在Son的构造器中添加了name,所以this -> { age: 44, name: 'zhang' }
    如果调换 super(); this.name = 'zhang'的位置

    class Son extends Father {
      constructor(){
        this.name = 'zhang'
        super()
      }
      ...
    }
    

    同样会报那个错误

    ReferenceError: Must call super constructor in derived class
     before accessing 'this' or returning from derived constructor
    

       在访问this之前必须先supper,在调用supper之前不能访问this的原因就是你的子类在继承之后,就是为了访问到父类的非静态属性和方法,你先对this进行了操作,然后supper之后,生成了新的this指向对象,那原来的该怎么办,通过supper向父类传值的,子类再访问这个值,这才是类的意义所在。

    相关文章

      网友评论

        本文标题:JavaScript的this(二)

        本文链接:https://www.haomeiwen.com/subject/urwzyltx.html