关于this的指向
create by db on 2021-3-29 18:47:18
Recently revised in 2022-7-20 16:38:42闲时要有吃紧的心思,忙时要有悠闲的趣味
前言
正文
this是js中的一个的关键字,在函数运行时自动生成一个内部对象。this的指向不是在函数创建的时候确定下来的,而是在函数执行时确定的。
关于 this 的指向
如果要判断一个运行中函数的 this 指向,就需要找到这个函数的直接调用位置。找到之后可以应用四条规则来判断 this 的绑定对象。
1. new 绑定
构造函数,使用 new 来调用 foo(..)时,我们会构造一个新对象并且把它绑定到 foo(..)调用中的 this 上,即此时的 this 指向新创建的对象。此时的绑定行为我们称之为new 绑定。 示例如下:
var a = 2
function foo(a) {
this.a = a
}
var obj = new foo(3)
console.log('a的值是', obj.a)
// a的值是 3
2. 显示绑定
当使用函数的 call 、 apply 或者 bind 方法绑定,会改变传入函数的 this。此时 this 指向被绑定的对象,即函数的第一个参数。
因为可以直接指定 this 的绑定对象,因此我们称之为显式绑定。
示例如下:
var a = 2
var obj = {
a: 3,
}
function foo() {
console.log('当前this指向', this)
console.log('a的值是', this.a)
}
foo.call(obj)
// 当前this指向 {a: 2, foo: ƒ} --> 直接调用形成隐式绑定,指向obj
// a的值是 3
3. 隐式绑定
即被对象直接调用,此时 this 指向当前上下文对象。
示例如下:
var a = 2
var b = 3
var obj = {
a: 4,
foo: foo,
}
function foo() {
console.log('当前this指向', this)
console.log('a的值是', this.a)
console.log('b的值是', this.b)
}
obj.foo()
// 当前this指向 {a: 2, foo: ƒ} --> 直接调用形成隐式绑定,指向obj
// a的值是 4
// b的值是 undefined --> obj没有b这个属性,所以输出undefined
4. 默认绑定
如果以上绑定都不存在,或者说函数不带任何修饰被直接调用,那就属于默认绑定,此时分为两种情况:
- 非严格模式下,
this指向 全局对象,即window
示例如下:
var a = 2
let b = 3
function foo() {
console.log('当前this指向', this)
console.log('a的值', this.a)
console.log('b的值', this.b)
}
foo()
// 当前this指向 Window {window: Window, self: Window, document: document…}
// a的值是 2
// b的值是 undefined --> 因为let在全局作用域中声明的变量不会成为window对象的属性
- 严格模式下,
this指向 undefind
示例如下:
'use strict'
var a = 2
function foo() {
console.log('当前this指向', this)
console.log('a的值是', this.a)
}
foo()
// 当前this指向 undefined
// TypeError: Cannot read property 'a' of undefined
箭头函数调用
不得不说,ES6 提供了箭头函数,增加了我们的开发效率,但是在箭头函数里没有 this ,箭头函数里面的 this 是继承外面的环境。
普通函数中:
var a = 2
var obj = {
a: 3,
fn: function() {
console.log('外部函数this指向', this)
console.log('外部函数a的值是', this.a)
setTimeout(function() {
console.log('内部函数当前this指向', this)
console.log('内部函数a的值是', this.a)
})
},
}
obj.fn()
var a = 2
var obj = {
a: 3,
fn: () => {
console.log('外部函数this指向', this)
console.log('外部函数a的值是', this.a)
},
}
obj.fn()
// 外部函数this指向 {a: 3, fn: ƒ}
// 外部函数a的值是 3
// 内部函数当前this指向 Window {0: Window, 1: global…}
// 内部函数a的值是 2
不难发现,虽然 fn() 里面的 this 是指向 obj ,但是,传给 setTimeout 的是普通函数, this 指向是 widow 。
换成箭头函数:
var a = 2
var obj = {
a: 3,
fn: function() {
console.log('外部函数this指向', this)
console.log('外部函数a的值是', this.a)
setTimeout(() => {
console.log('内部函数当前this指向', this)
console.log('内部函数a的值是', this.a)
})
},
}
obj.fn()
// 外部函数this指向 {a: 3, fn: ƒ}
// 外部函数a的值是 3
// 内部函数当前this指向 {a: 3, fn: ƒ}
// 内部函数a的值是 3
这次两个函数输出相同,以为传给 setTimeout 的是箭头函数,然后箭头函数里面没有 this ,所以要向上层作用域查找,在这个例子上, setTimeout 的上层作用域是 fn。而 fn 里面的 this 指向 obj ,所以 setTimeout 里面的箭头函数的 this ,指向 obj 。
优先级
直接说结论:new 绑定 > 显示绑定 > 隐式绑定 > 默认绑定
总结
我们可以根据以下顺序进行判断this指向:
-
构造函数中,
this指向新创建的对象。 -
当使用函数的
call、apply或者bind方法绑定this指向被绑定的对象,即函数的第一个参数。 -
被对象直接调用,谁调用该函数this指向就指向谁
-
以上都不是的话,在非严格模式下,
this指向 window -
在严格模式下,
this指向 undefined -
箭头函数里没有自己的
this,箭头函数里面的this继承外面的环境。
后记:Hello 小伙伴们,如果觉得本文还不错,记得点个赞或者给个 star,你们的赞和 star 是我编写更多更丰富文章的动力!GitHub 地址
文档协议
db 的文档库 由 db 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。
基于github.com/danygitgit上的作品创作。
本许可协议授权之外的使用权限可以从 creativecommons.org/licenses/by… 处获得。