前言
JavaScript 中的 this 指向大致可以分为两大部分,即 作为独立函数调用 和 作为对象方法调用。
注意:箭头函数中没有 this,也就是箭头函数中的 this 是引用自外层作用域中的 this。
JavaScript 中的 this 指向
JavaScript 中的常见的 this 指向一般分为如下两大类:
第一大类:作为独立函数调用(隐式绑定 this)
不是通过 对象.
(对象方法)的方式来引用的函数中的 this 指向的是 globalThis。
注意:在 严格模式 下,独立函数中的 this 指向为 undefined。
常见的四种表现形式如下:
// 表现形式一:直接定义为一个独立函数
function foo() {
console.log(this === globalThis)
}
foo() // true
const obj = {
jump() {
console.log(this === globalThis)
},
}
// 表现形式二:赋值为一个纯 JavaScript 对象成员方法
const bar1 = obj.jump
bar1() // true
class Person {
run() {
console.log(this)
}
static staticMethod() {
console.log(this)
}
}
const p = new Person()
// 表现形式三:赋值为一个类实例对象方法
const bar2 = p.run
bar2()
// undefined
// 表现形式四:赋值为一个类静态方法
const bar3 = Person.staticMethod
bar3()
// undefined
需要注意一下表现形式三(赋值为类实例方法)和表现形式四(赋值为类静态方法),函数内部 this 指向的不是全局对象 globalThis,而是 undefined。
原因:class 是函数原型链继承的语法糖形式,底层最终会被编译成原型链继承,默认启用严格模式,而严格模式下的独立函数中的 this 指向 undefined。
globalThis 介绍 - MDN 链接:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/globalThis
globalThis
提供了一个标准的方式来获取不同环境下的全局 this
对象(也就是全局对象自身)。
-
在浏览器环境下,globalThis 指向的是全局的
window
对象。 -
在 Node.js 环境下,即通过
node
等命令运行 JavaScript 脚本文件时,globalThis 指向的是全局的global
对象。
其他环境以此类推,globalThis 都是指向全局中的 this(也就是全局对象自身)。
第二大类:作为对象方法调用(显式绑定 this)
作为对象方法,就是通过 对象.xxx()
的方式来调用的函数,其 this 指向引用其的对象。
对象方法的三种常见表现形式如下:
- 表现形式一:作为一个纯 JavaScript 对象的方法时,其 this 指向为该对象本身。
const obj = {
run() {
return this
}
}
const thisObj = obj.run()
console.log(thisObj === obj) // true
-
表现形式二:作为类实例方法时,其 this 指向类实例对象。
知识补给:class 类是 js 原型链继承的语法糖形式,类实例方法最终会被编译成一个与类同名的函数的原型对象
prototype
上的一个共享方法。参考链接:继承与原型链 - JavaScript | MDN (mozilla.org)
class Person { run() { return this } } const p = new Person() const thisObj = p.run() console.log(thisObj === p) // true // p 是 Person 类实例对象,所以根据继承原型链可知 p.__proto__.constructor === Person console.log(p.__proto__.constructor === Person) // true console.log(thisObj instanceof Person) // true console.log(thisObj instanceof p.__proto__.constructor) // true
-
表现形式三:作为类静态方法时,其 this 指向类本身。
知识补给:class 类是 js 原型链继承的语法糖形式,类静态实例方法最终会被编译成一个与类同名的函数对象上的一个成员方法。
class Person { static run() { return this } } const thisObj = Person.run() console.log(thisObj === Person) // true
简单理解就是:通过谁来调用该方法,那么该方法中的 this 就指向谁。
修改 this 指向
可以使用 Function.prototype.call()
、Function.prototype.apply()
或 Reflect.apply()
方法显式设置 this
的值。使用 Function.prototype.bind()
,你可以创建一个新的函数,无论函数如何被调用,其 this
的值都不会改变。当使用这些方法时,无论函数是处于严格模式还是非严格模式下,上述 this
替换规则仍然适用。
常用的几个修改函数中的 this 指向的方法就是 apply、call、和 bind。
apply、call、和 bind 三者的相同点:都是通过参数1来指定函数中的 this 指向。
apply、call、bind 的不同点:
-
apply 和 call 是修改 this 指向的同时调用该函数,而 bind 是返回一个绑定了指定的 this 后的新函数。
-
apply 是通过向参数2传递一个 参数数组 的方式来向目标函数传递实参,而 call 和 bind 则是通过一个 参数列表 的方式传递实参。
知识补给:参数列表就是一个以
,
分隔参数的表达式,形如:参数1, 参数2, ..., 参数n
function foo(name, age) {
console.log(name, age, this.hobbies)
return this
}
const thisObj = foo()
console.log(thisObj === globalThis) // true
const person1 = { hobbies: ['唱'] }
const person2 = { hobbies: ['跳'] }
const person3 = { hobbies: ['rap'] }
foo.apply(person1, ['zhangsan', 18])
// zhangsan 18 [ '唱' ]
foo.call(person2, 'zhangsan', 18)
// zhangsan 18 [ '跳' ]
const newFoo = foo.bind(person3, 'zhangsan', 18)
newFoo()
// zhangsan 18 [ 'rap' ]