最近在學react的時候,由於react都是用class來構建組件,對class中this的指向有一點迷惑。如今作些總結。react
this的綁定一共有四種弄方式,優先級逐級遞減。
this的本質就是: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 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的原型對象this
能夠看到,Cat所建立出來的實例,其方法掛載在實例的__proto__上面,即掛載在原型對象上。由於cat.__proto__與Cat.prototype指向同一個對象,因此當在cat.__proto__上掛載或者覆蓋其原有方法時,全部由Cat所建立出來的實例,都將會共享該方法,全部實例都是經過__proto__屬性產生的原型鏈到原型對象上尋找方法。spa
可是靜態方法不會共享給實例,由於沒有掛載在原型對象上面。prototype
而屬性是掛載在實例上的,即每個建立出來的實例,都擁有本身的不一樣值的屬性。code
前景提要:當咱們打印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指向的是該構造函數。