class中this的指向

最近在學react的時候,由於react都是用class來構建組件,對class中this的指向有一點迷惑。如今作些總結。react

this的綁定優先級

this的綁定一共有四種弄方式,優先級逐級遞減。
this的本質就是:this跟做用域無關的,只跟執行上下文有關。瀏覽器

一、new建立出來的實例去調用,this指向當前實例

eg.app

// 實例上的綁定
let cat = new Cat()
cat.speak()

二、顯示綁定

使用call,apply,或者bind函數

function jump() {
  console.log(this.name)
}
const obj = {
  name: '米粒',
  jump
}

jump = jump.bind(obj)

jump() // 米粒

三、對象中的方法綁定

function jump() {
  console.log(this.name)
}
let obj = {
  name: '米粒',
  jump
}

obj.jump() // 米粒

四、默認綁定

在嚴格模式下,this是undefined,不然是全局對象工具

Class中屬性與方法的綁定

class Cat {
    constructor(name, age) {
        // name作綁定 age未綁定
        this.name = name
    }
    jump() {
        console.log('jump',this)
    }
    // 能夠直接調用 靜態方法一般用於爲一個應用程序建立工具函數。
    static go() { 
        console.log(this)
    }
}
// 能夠直接調用 與使用static差很少
Cat.drink = function() {
    console.log('drink',this)
}

Cat.prototype.eat = function() {
    console.log('eat',this)
}
// 使用箭頭函數綁定
Cat.prototype.walk = () => {
    console.log('walk',this)
}

let cat = new Cat('cat中的米粒', '5個月')

打印出cat與Cat的原型對象
image.pngthis

能夠看到,Cat所建立出來的實例,其方法掛載在實例的__proto__上面,即掛載在原型對象上。由於cat.__proto__與Cat.prototype指向同一個對象,因此當在cat.__proto__上掛載或者覆蓋其原有方法時,全部由Cat所建立出來的實例,都將會共享該方法,全部實例都是經過__proto__屬性產生的原型鏈到原型對象上尋找方法。spa

可是靜態方法不會共享給實例,由於沒有掛載在原型對象上面。prototype

而屬性是掛載在實例上的,即每個建立出來的實例,都擁有本身的不一樣值的屬性。code

class中this的綁定

前景提要:當咱們打印typeof Cat可知是函數類型,ES6中的class類其實只是個語法糖,皆能夠用ES5來實現。由構造函數Cat建立的實例cat是一個對象。在初始化cat實例的時候,在constructor中就會把this上的屬性掛載到實例對象上面。對象

另外牢記,this的指向與做用域無關,與調用執行函數時的上下文相關。
示例一:

class Cat {
    constructor(name, age) {
        this.name = name
    }
    run() {
        console.log('run',this)
    }
}
let cat = new Cat('米粒', '5個月')
cat.name // '米粒'
cat.run() // run Cat {name: "米粒"}

當調用cat.run()的時候,當前上下文是cat,因此其this指向的是cat這個實例。
示例二:

class Cat {
    constructor(name, age) {
        this.name = name
        this.jump = this.jump.bind(this)
        this.drink = () => {
            console.log('drink',this)
        }
    }
    run() {
        console.log('run',this)
    }
    jump() {
      console.log('jump',this)
    }
    static go() { 
        console.log('go',this)
    }     
}

Cat.prototype.walk = () => {
    console.log('walk',this)
}

let cat = new Cat('米粒', '5個月')
let run = cat.run
let jump = cat.jump
let go = Cat.go
let walk = cat.walk
let drink = cat.drink


run() // run undefined
jump()  // jump Cat {name: "米粒", jump: ƒ}
Cat.go() // go class Cat {}
go() // go undefined
cat.walk() // walk Window
walk() // walk Window
cat.drink() // drink Cat {name: "米粒", jump: ƒ, drink: ƒ}
drink() // drink Cat {name: "米粒", jump: ƒ, drink: ƒ}

先看run方法:當把實例中的方法賦值給一個變量,可是隻是賦予了方法的引用,因此當變量在執行方法的時候,其實改變了方法的執行時的上下文。原來執行的上下文是實例cat,後來賦值以後再執行,上下文就變成了全局,this默認綁定。class中使用的是嚴格模式,在該模式下,全局的this默認綁定的是undefined,不是在嚴格模式下的時候,若在瀏覽器中執行,則this默認綁定window。

jump方法:由於在構造函數執行的時候,顯式綁定了jump執行的上下文——cat實例。由文章開頭this綁定的優先級可知,顯式綁定>默認綁定。因此jump的執行上下文依然是cat實例

go方法:go方法使用靜態方法定義,沒法共享個實例cat,只能在構造函數Cat上直接調用。

walk與drink方法:這兩個方法是用箭頭函數定義的。箭頭函數的this是在定義函數時綁定的,不是在執行過程當中綁定的。簡單的說,函數在定義時,this就繼承了定義函數的對象。

因此walk是在Cat.prototype.walk時定義的,此時的this指向是window。不管以後賦值給哪一個變量,也只是函數的引用,因此其this仍是window。

同理,drink在定義的時候,this指向的是該構造函數。

相關文章
相關標籤/搜索