最近在学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指向的是该构造函数。