老生常谈:JS中的This

每个JS函数都有自己的运行环境,这个环境在函数内部用this来指代。this对象是在运行时根据函数的执行环境来绑定的,在全局环境中,this指向window对象;而当函数被当作一个对象的方法来调用时,this就是这个对象。

值得注意的一点是,函数的执行环境是它的直接调用对象。也就是说,即便是通过两层或多层对象调用了一个函数,它的执行环境仍然是离它最近的那个对象:

Animal.action.eat();	//eat函数的执行环境是action,而不是Animal

所以this对象是用来指代函数的运行环境的

let user = {
name: 'paykan',
    say: function(){
     console.log(this.name)
    }
}
user.say();	//paykan,此时的运行环境是user对象
let func = user.say;
func();	//undefined,此时的运行环境是window

在编码的过程中,我们很容易将函数的运行环境搞错,除非特别仔细,那么可以通过解答这个问题来消除这种担心:如何为函数指定运行环境?

设置函数的运行环境:call、apply、bind

还是上面的代码,如果我不太确定一个函数的运行环境将会是什么,那我可以在调用它时明确地指定(this是在执行时才确定的)。

/*方法一:call*/
document.body.onclick = function(){
 functin fun(){ ... }
 
 fun.call(this)	//onclick函数执行环境肯定是body,所以此处的this是body
 				    //写成fun.call(document.body)也一样
}

/*方法二:apply*/
document.body.onclick = function(){
 functin fun(){ ... }
 
 fun.apply(this)	
}

/*方法三:bind*/
document.body.onclick = function(){
 functin fun(){ ... }
 
 fun.bind(this)()	//bind返回了一个经过改装的函数
}

其实call、apply、bind只是在写法上不同而已:

  • call(对象, 参数1, 参数2, ...)
  • apply(对象, [参数1, 参数2, ...])
  • bind(对象, 参数1, 参数2, ...),返回了一个经过改装的函数。

箭头函数中的this

在this的问题上,箭头函数让事情变得简单了一点——函数的this就是定义函数的那个执行环境。

比如在下面的代码中:

let user = {
	id: "123456",
  sayId: ()={
		console.log(this.id)
	}
}
user.sayId()	//undefined

虽然this出现的位置是user对象,但this指向的绝不是user,而是它所在的那个箭头函数被定义时的执行环境:window——整个user对象是在window执行环境下定义的。

为了验证这一点,我们这样改写:

let user = {
	id: "123456",
  sayId: function(){
    let fun = ()={ console.log(this.id) }
    fun()
  }
}
user.sayId()	//123456

根据普通函数的特性,执行user.sayId时,该函数的执行环境就是user对象。在user这个执行环境里,我们定义了一个箭头函数。根据箭头函数的特性,它的执行环境在定义时就已经被绑定为user对象,所以该箭头函数内的this肯定指向了user对象。