前端开发那些事儿

JavaScript的this(二)

2021-06-26  本文已影响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向父类传值的,子类再访问这个值,这才是类的意义所在。

上一篇下一篇

猜你喜欢

热点阅读