关于this
关于this的指向问题,很多像我一样从事前端工作不是很久的小伙伴总是一头雾水,在使用时分不清this到底指向什么。最近一段时间我下定决心好好学习一下this,在此进行一些记录。
一句话概括this的指向
通常,我们对this的指向描述为,谁调用this,他就指向谁。这句话表述起来没什么问题,但是不够规范和全面。我们用另一种方式来表达:
this的指向,是在调用函数时根据执行上下文所动态确定的。
this的使用场景总结
this的使用场景有很多,这里引用候策老师对于this使用的分类:
- 全局环境中的this
- 对象调用时的this
- new操作符与this
- bind/apply/call改变this的指向
- 箭头函数中this 的指向
- this的优先级
下面我们对这几种情况进行分析。
1.全局环境中的this
function fn(){
console.log(this.a)
}
var a = 1
fn() //1
此处,当调用fn()时,this.a被解析为了全局变量a。这是因为在函数调用时,fn()是直接使用不带任何修饰符的函数进行调用的,所以this指向全局对象,同时,声明在全局作用域中的属性a,本质上就是全局变量的一个属性。
需要注意的是,如果使用严格模式,则不能使用全局变量用于默认绑定,此时this会绑定到undefined。
function fn(){
'use strict'
console.log(this.a)
}
var a = 1
fn() //Uncaught TypeError: Cannot read property 'a' of undefined
2.对象调用时的this
function fn(){
console.log(this.a)
}
var obj = {
a: 2
fn: fn
}
obj.fn() //2
此时,当fn被调用时,加上了obj的引用,this会绑定到这个上下文对象,此时this.a为obj.a。
是不是觉得很简单,别急,我们再来看一道例题:
function fn(){
console.log(this.a)
}
var obj1 = {
a: 23,
fn: fn
}
var obj2 = {
a: 2,
obj1: obj1
}
obj2.obj1.fn() //23
对象属性引用时,只有最后一层在调用位置中起作用。
3.new绑定
使用new来调用函数,会发生下面的操作。(注意,此处也是一个常见的面试题哦,说一说new操作符都做了哪些工作?有没有觉得很熟悉)
- 创建一个新对象;
- 将构造函数的作用域赋给新对象(因此this就指向了这个新对象);
- 执行构造函数中的代码(为这个新对象添加属性);
- 返回新对象;
function fn(a){
this.a = a
}
var o = new fn(5)
console.log(o.a) //5
new 操作符会创建一个新对象,并将this指向这个新对象,从而改变this的指向。
4.bind/apply/call
function fn(){
console.log(this.a)
}
var obj = {
a: 2
}
fn.call(obj) //2
从this绑定的角度来说,apply和call的作用是一样的,知识在参数传递上有一些差别。 下面我们来看看bind。
function fn(){
console.log(this.a)
}
var obj = {
a: 2
}
var b = fn.bind(obj)
b()
bind与call和apply的区别在于,bind会返回一个已经绑定了this的函数,使用时需要进行手动调用。而call/apply会直接进行相关函数调用。
5.箭头函数中的指向
箭头函数不适合上述this的判断方法,而是根据外层作用域来决定的。
const foo = {
fn: function () {
setTimeout(() => {
console.log('箭头函数', this)
})
setTimeout(function(){
console.log('常规函数', this)
})
}
}
console.log(foo.fn())
//箭头函数 {fn: ƒ}
//常规函数 Window{}
6.this的优先级
在此先记录结论,判断this时可按照下面的顺序进行判断:
- 是否使用new绑定
- 是否通过call,apply或bind绑定
- 是否在某个上下文对象中调用
- 如果不是,使用默认绑定。严格模式下绑定到undefined,否则绑定到全局对象。
文章参考: 你不知道的JavaScript(上卷)