黑马程序员技术交流社区

标题: 关于this指向的问题 [打印本页]

作者: 天树123    时间: 2020-5-28 10:43
标题: 关于this指向的问题
一、前言
1、this的绑定在创建执行上下文时确定
2、大多数情况函数调用的方式决定this的值,this在执行时无法赋值
3、this的值为当前执行的环境对象,非严格下总是指向一个对象,严格下可以是任意值
4、全局环境下this始终指向window,严格模式下函数的调用没有明确调用对象的情况下,函数内5、部this指向undefined,非严格下指向window
6、箭头函数的this永远指向创建当前词法环境时的this
7、作为构造函数时,函数中的this指向实例对象
8、this的绑定只受最靠近调用它的成员的引用
9、执行上下文在被执行的时候才会创建,创建执行上下文时才会绑定this,所以this的指向永远是在执行时确定
function foo(){
  console.dir(this) // window ,严格下undefined
}
foo()
-----------------------------------------------
function foo(){
  console.dir(this) //非严格Number对象,严格模式 5
}
foo.call(5)

二、普通函数中的this
1、直接调用
在没有明确调用者情况下函数内部this指向window,严格模式下都为undefined,除非绑定函数的this指向,才会改变this
// 直接调用函数
function foo() {
  console.dir(this) //window,严格下 undefined
  function boo(){
    console.dir(this) //window,严格下 undefined
  }
  boo()
}
----------------------------------------------
// 取出对象中的函数,再进行调用
const obj = {
  foo: function foo() {
    console.dir(this) //window,严格下 undefined
    function boo() {
      console.dir(this) //window,严格下 undefined
    }
    return boo
  }
}
const foo = obj.foo
foo()()
----------------------------------------------
// 直接通过对象调用函数,再调用返回的函数,可以看出this的指向随调用对象改变
const obj = {
  foo: function foo() {
    console.dir(this) //obj,严格下 obj
    function boo() {
      console.dir(this) //window,严格下 undefined
    }
    return boo
  }
}
const foo = obj.foo()
foo()
----------------------------------------------
// 基于回调函数也是如此
function foo(func) {
  console.dir(this) // window ,严格下 undefined
  func()
}
foo(function () {
  console.dir(this) // window ,严格下 undefined
})
基于调用者以及不同调用方式
函数调用也就是在函数名后面加个(),表示调用,如果函数名前没有加任何东西,那么默认为简单调用,在严格与非严格环境下,简单调用的函数内部this指向undefined与window,但是全局环境下的this永远为window

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

// 函数this指向调用者对象
const obj = {
  foo: function () {
    console.dir(this) // obj1,严格下 obj1
    function boo() {
      console.dir(this) // window,严格下 undefined
    }
    boo()
    return boo
  }
}
const obj1 = {}
obj1.boo = obj.foo
obj1.boo()
----------------------------------------------
// 不同调用对象时,this指向调用者
const obj = {
  foo: function () {
    console.dir(this) // obj,严格下 obj
    function boo() {
      console.dir(this)
    }
    boo() // window,严格下 undefined
    return boo
  }
}
const obj1 = {}
obj1.boo = obj.foo()
obj1.boo() // obj1,严格下 obj1
----------------------------------------------
// this指向最近的调用者
const obj = {
  name: 'obj',
  obj1: {
    name: 'obj1',
    foo: function () {
      console.dir(this.name) // obj1
    }
  }
}
obj.obj1.foo()

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

4、基于定时器与微任务
微任务中的简单调用的函数this指向window严格下指向undefined,而定时器中的回调函数不管在严格还是非严格环境下this永远指向window,说明一点,调用window对象的方法时this指向window也就是全局对象,换句话说,简单调用的函数如果属于window本身自带的方法那么这个方法的this指向window
// 异步任务中简单调用的函数都是进入队列,最后由全局环境调用
const id = setInterval(function () {
  console.dir(this) // window ,严格下 window
  setTimeout(() => {
    console.dir(this) // window ,严格下 window
    clearInterval(id)
  });
})
----------------------------------------------
new Promise(function (resolve, reject) {
  console.dir(this) // window ,严格下 undefined
  resolve()
}).then(function (res) {
  console.dir(this) // window ,严格下 undefined
});
----------------------------------------------
(async function foo() {
  function boo() {
    console.dir(this) // window ,严格下 undefined
  }
  await boo()
  console.dir(this) // window ,严格下 undefined
})()
----------------------------------------------
// 定时器的回调最终都会被作为简单函数被执行,定时器属于window对象的方法
function foo(){
  setTimeout(function (){
    console.log(this) //window ,严格下window
  })
}
foo.call(5)
----------------------------------------------
// 函数内部的this就是指向调用者,并且可以看出简单调用的回调函数中的this也指向window
const obj = {
  foo(callback) {
    callback()
    console.log(this.foo === obj.foo) // true
    console.log(this === obj) // true
  }
}
obj.foo(function () {
  console.log(this) //window ,严格下undefined
})
----------------------------------------------
// 通过arguments调用的回调函数中的this指向调用者,注意严格与非严格下的arguments对象有所不同
const obj = {
  foo(callback) {
    arguments[0]()
    console.log(this.foo === obj.foo) // true
    console.log(this === obj) // true
  }
}
obj.foo(function () {
  console.log(this) //arguments对象 ,严格下 arguments对象
})

5、箭头函数中的this
es6引入的箭头函数,是不具有this绑定,不过在其函数体中可以使用this,而这个this指向的是箭头函数当前所处的词法环境中的this对象,可以理解为,this在箭头函数中是透明的,箭头函数包不住this,所以函数内部与外部的this为同一值

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

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





欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2