一、普通函数的 this
普通函数的 this 可视为一个隐藏参数,为函数内部的私有变量,在函数调用时才绑定 this 的值,在调用前 this 的指向无法确定。
考虑如下函数:
function fn(name){
console.log(name)
console.log(this)
}
1、常规调用
常规调用 fn('jack'),这种调用形式没传 this,即 this 为 undefined ,但在浏览器中this会自动变成 window。在严格模式下,this的值才是undefined。
2、用 call 方法调用
用 call 方法调用时,第一个参数会传给 this,后面的是函数真正的参数
fn.call(a, 'jack') //this 会绑定为 a
【注】:若传入的 a 不是对象,会自动转化为对象
3、在对象中调用
考虑如下对象:
const obj = {
name: 'jack',
say(){
console.log(this.name)
}
}
obj.say() //打印出 'jack'
调用方法 obj.say(),会自动将obj传给this,即obj.say() 等价于 obj.say.call(obj)
4、在数组中调用
数组也是一种对象,在数组中调用函数和在对象中调用一样,this会绑定为当前数组。
const arr = [
'jack',
function(){console.log(this)}
]
arr[1]() //打印出 arr
5、用 new 调用构造函数
用 new 调用函数会得到一个对象,在函数中,this 绑定为这个待返回的对象。例:
function fn(name){
this.name = name
}
const obj = new fn('jack')
obj.name //'jack'
二、箭头函数的 this
箭头函数内部没有 this,它使用的 this 就是箭头函数所在作用域的 this,即箭头函数的 this 在函数创建的时候就已经确定了。
【例1】如下函数:
function fn(){
const arrowF = ()=>{console.log(this)}
return arrowF
}
const f1 = fn()
f1() //打印出 window
const f2 = fn.call({})
f2() //打印出空对象 {}
- 调用
fn()时,fn中的this绑定为window,然后创建箭头函数arrowF,它内部的this就是window,将这个函数作为返回值赋给f1。调用f1(),则打印出window。 - 调用
fn.call({})时,fn中的this绑定为{},然后创建箭头函数arrowF,它内部的this就是{},将这个函数作为返回值赋给f2。调用f2(),则打印出{}。
【注】:即便用 call 方法调用箭头函数,依然无法改变其 this 的绑定
【例2】尽量不要在对象中使用箭头函数,如:
const obj = {
name: 'jack',
say: ()=>{console.log(this.name)}
}
obj.say() //undefined
say 方法为箭头函数,其在定义时 this 就被确定为 window。
三、事件监听器中 this 的确定
事件监听器中的this同样由其被调用的方式确定。
1、Event Handler
btn.addEventListener('click', function handler(){console.log(this)})
handler被调用时,其中的this是被监听的元素,相当于传入的event参数的currentTarget属性,可以假定浏览器调用handler的方式为:
handler.call(event.currentTarget, event)
2、jQuery 的 Event Handler
jQuery 调用handler时,this指向当前执行事件的元素。
- 对于直接事件:
$div.on('click', function handler(){console.log(this)})
this就是被绑定事件的元素,即$div所对应的元素。
- 对于代理事件:
$ul.on('click', 'li', function handler(){console.log(this)})
this就是被代理的元素,即相应的<li>元素。
3、Vue
Vue 中handler的this很简单,其指向始终绑定为当前实例/组件。
4、React
React 的类组件中,handler为全局调用,this为undefined,并且使用了严格模式,不会转为window。