一、图解

未命名文件.png

  • this的绑定在创建执行上下文时确定。
  • 大多数情况函数调用的方式决定this的值,this在执行时无法赋值。
  • this的值为当前执行的环境对象,非严格下总是指向一个对象,严格下可以是任意值
  • 全局环境下this始终指向window,严格模式下函数的调用没有明确调用对象的情况下,函数内部this指向undefined,非严格下指向window
  • 箭头函数的this永远指向创建当前词法环境时的this
  • 作为构造函数时,函数中的this指向实例对象。
  • this的绑定只受最靠近调用它的成员的引用
  • 执行上下文在被执行的时候才会创建,创建执行上下文时才会绑定this,所以this的指向永远是在执行时确定。

二、普通函数中的this

直接调用

在没有明确调用者情况下函数内部this指向window,严格模式下都为undefined,除非绑定函数的this指向,才会改变this

直接调用函数

  1. // 直接调用函数
  2. function foo() {
  3. console.dir(this) //window,严格下 undefined
  4. function boo(){
  5. console.dir(this) //window,严格下 undefined
  6. }
  7. boo()
  8. }
  1. // 取出对象中的函数,再进行调用
  2. const obj = {
  3. foo: function foo() {
  4. console.dir(this) //window,严格下 undefined
  5. function boo() {
  6. console.dir(this) //window,严格下 undefined
  7. }
  8. return boo
  9. }
  10. }
  11. const foo = obj.foo
  12. foo()()
  1. // 直接通过对象调用函数,再调用返回的函数,可以看出this的指向随调用对象改变
  2. const obj = {
  3. foo: function foo() {
  4. console.dir(this) //obj,严格下 obj
  5. function boo() {
  6. console.dir(this) //window,严格下 undefined
  7. }
  8. return boo
  9. }
  10. }
  11. const foo = obj.foo()
  12. foo()
  1. // 基于回调函数也是如此
  2. function foo(func) {
  3. console.dir(this) // window ,严格下 undefined
  4. func()
  5. }
  6. foo(function () {
  7. console.dir(this) // window ,严格下 undefined
  8. })

基于调用者以及不同调用方式

函数调用也就是在函数名后面加个(),表示调用,如果函数名前没有加任何东西,那么默认为简单调用,在严格与非严格环境下,简单调用的函数内部this指向undefined与window,但是全局环境下的this永远为window

基于对象

当函数作为对象的方法调用时,不受函数定义方式或者位置影响

  1. // 函数this指向调用者对象
  2. const obj = {
  3. foo: function () {
  4. console.dir(this) // obj1,严格下 obj1
  5. function boo() {
  6. console.dir(this) // window,严格下 undefined
  7. }
  8. boo()
  9. return boo
  10. }
  11. }
  12. const obj1 = {}
  13. obj1.boo = obj.foo
  14. obj1.boo()
  1. // 不同调用对象时,this指向调用者
  2. const obj = {
  3. foo: function () {
  4. console.dir(this) // obj,严格下 obj
  5. function boo() {
  6. console.dir(this)
  7. }
  8. boo() // window,严格下 undefined
  9. return boo
  10. }
  11. }
  12. const obj1 = {}
  13. obj1.boo = obj.foo()
  14. obj1.boo() // obj1,严格下 obj1
  1. // this指向最近的调用者
  2. const obj = {
  3. name: 'obj',
  4. obj1: {
  5. name: 'obj1',
  6. foo: function () {
  7. console.dir(this.name) // obj1
  8. }
  9. }
  10. }
  11. obj.obj1.foo()

基于new关键字

  1. // 基于new关键字调用的函数内部this指向实例
  2. function foo() {
  3. console.dir(this) // foo实例
  4. console.log(this instanceof foo) //true
  5. console.log(foo.prototype.isPrototypeOf(this)) //true
  6. that = this
  7. }
  8. var that
  9. const f = new foo()
  10. console.log(that === f) // true
  1. // 嵌套函数内部this与调用函数所在环境的this无关
  2. function foo() {
  3. console.dir(this) // foo实例
  4. function boo() {
  5. console.dir(this) //window,严格下undefined
  6. }
  7. boo()
  8. }
  9. const f = new foo()

基于定时器与微任务

微任务中的简单调用的函数this指向window严格下指向undefined,而定时器中的回调函数不管在严格还是非严格环境下this永远指向window,说明一点,调用window对象的方法时this指向window也就是全局对象,换句话说,简单调用的函数如果属于window本身自带的方法那么这个方法的this指向window.

  1. // 异步任务中简单调用的函数都是进入队列,最后由全局环境调用
  2. const id = setInterval(function () {
  3. console.dir(this) // window ,严格下 window
  4. setTimeout(() => {
  5. console.dir(this) // window ,严格下 window
  6. clearInterval(id)
  7. });
  8. })
  1. new Promise(function (resolve, reject) {
  2. console.dir(this) // window ,严格下 undefined
  3. resolve()
  4. }).then(function (res) {
  5. console.dir(this) // window ,严格下 undefined
  6. });
  1. (async function foo() {
  2. function boo() {
  3. console.dir(this) // window ,严格下 undefined
  4. }
  5. await boo()
  6. console.dir(this) // window ,严格下 undefined
  7. })()
  1. // 定时器的回调最终都会被作为简单函数被执行,定时器属于window对象的方法
  2. function foo(){
  3. setTimeout(function (){
  4. console.log(this) //window ,严格下window
  5. })
  6. }
  7. foo.call(5)
  1. // 函数内部的this就是指向调用者,并且可以看出简单调用的回调函数中的this也指向window
  2. const obj = {
  3. foo(callback) {
  4. callback()
  5. console.log(this.foo === obj.foo) // true
  6. console.log(this === obj) // true
  7. }
  8. }
  9. obj.foo(function () {
  10. console.log(this) //window ,严格下undefined
  11. })
  1. // 通过arguments调用的回调函数中的this指向调用者,注意严格与非严格下的arguments对象有所不同
  2. const obj = {
  3. foo(callback) {
  4. arguments[0]()
  5. console.log(this.foo === obj.foo) // true
  6. console.log(this === obj) // true
  7. }
  8. }
  9. obj.foo(function () {
  10. console.log(this) //arguments对象 ,严格下 arguments对象
  11. })

三、箭头函数中的this

es6引入的箭头函数,是不具有this绑定,不过在其函数体中可以使用this,而这个this指向的是箭头函数当前所处的词法环境中的this对象,可以理解为,this在箭头函数中是透明的,箭头函数包不住this,所以函数内部与外部的this为同一值,判断箭头函数的this指向,我们可以把箭头函数看成透明,其上下文中的this就是它的this。

  1. // 可以看出箭头函数中的this就是其所在环境的this,箭头函数无法固定this,由其环境决定
  2. const foo = () => {
  3. console.dir(this) //window ,严格下还是window
  4. }
  5. foo()
  1. // 可见对象中的this指向window,箭头函数中的this指向对象中的this。
  2. // 由于只有创建执行上下文才会绑定this指向,而除了全局上下文,只有函数作用域才会创建上下文环境从而绑定this
  3. // 创建对象不会绑定this,所以还是全局this
  4. const obj={
  5. this:this,
  6. foo:()=>{
  7. console.dir(this) //window ,严格下 window
  8. }
  9. }
  10. console.dir(obj.this) //window ,严格下 window
  11. obj.foo()
  1. // 对象方法内部嵌套箭头函数,则此箭头函数的this属于外部非箭头函数this。当调用obj.foo时foo函数创建的执行上下文中的this绑定对象obj,而箭头函数并不会绑定this,所以其this属于foo下的this,即对象obj
  2. const obj = {
  3. foo: function () {
  4. return () => {
  5. console.dir(this) //obj ,严格下 obj
  6. }
  7. }
  8. }
  9. obj.foo()()

四、如何改变函数的this指向

最简单的方法通过apply、call、bind来给函数绑定this

  • apply方法中第一个参数为被调用的函数中的this指向,传入你想要绑定的this值即可,第二个参数为被调用函数的参数集合,通常是个数组
  • call与apply方法基本一致,区别在于传入参数形式不同,call传入的参数为可变参数列表,参数按逐个传入
  • bind方法与以上不同的是不会直接调用函数,只是先绑定函数的this,到要使用的时候调用即可,此方法返回一个绑定this与参数之后的新函数,其传入参数形式同call
  • 通过变量保留指定this来达到固定this
  1. // 通过变量保留父级this,进行对_this变量修改也就达到修改原this的效果
  2. const obj = {
  3. name: 'obj',
  4. foo: function () {
  5. let _this = this
  6. function boo() {
  7. _this.name = 'OBJ'
  8. console.dir(obj.name) // OBJ
  9. }
  10. return boo
  11. }
  12. }
  13. obj.foo()()