您的当前位置:首页JavaScript中的变色龙this

JavaScript中的变色龙this

2024-12-14 来源:哗拓教育

前言

紧抓中心思想:谁调用,this指向谁。下面通过以下几点来阐述这个思想:
1.直接调用函数
2.通过对象调用函数
3.通过bind、call、apply调用函数
4.通过new调用构造函数
5.class中的this指向
6.箭头函数中的this指向
7.class中带箭头函数的this指向


直接调用函数
function test() {
    console.log(this)
}
test()  // window

function strictTest() {
    'use strict'
    console.log(this)
}
strictTest()  // undefined

在严格模式下,this指向undefined,正常模式下指向window。思考下为什么正常模式会指向window?

先来看这样一个事实:

global this

this在全局作用域中是指向window的,而“function test”与“function strictTest”默认是加到全局this所指向的对象中的。换句话说,在全局环境中定义的函数默认与全局的this关联。同理,在全局作用域中调用的函数也默认与全局this关联。所以直接调用test函数与以下调用方式是等价的:

this.test()
window.test()

根据谁调用,this指向谁的思想指导,此时是window调用test函数,所以函数内部的this指向window。

通过对象调用
var obj = {
    a: function test() {
        console.log(this)
    }
}
obj.a()  // obj
var temp = obj.a
temp()  // window

a是obj对象的方法,通过obj调用a函数,函数内部的this指向obj。当重新用变量temp接收a函数,此时在全局环境中直接调用temp。上文说过,这种调用方式相当于this.temp()调用,而全局环境中的this指向window,所以也等价于window.temp()调用。此时是window调用temp函数,所以函数内部this指向window。

通过bind、call、apply调用函数
var obj = {}
function test() {
    return this
}
bind、call、apply

bind、call、apply这三个函数可以直接指定被调函数的调用对象,因为已经指定调用对象为obj,即,test函数是通过obj对象调用的,所以此时函数内部的this指向obj。

通过new调用构造函数
var that = undefined
function Test() {
    that = this
}
var t = new Test
构造函数

由于构造函数不能写return,此处用that来接收函数内部的this后再做比较。显然,构造函数内部的this指向构造函数创建的对象本身,这和我们的基本认知相符。

这里有个注意点:通过new命令调用的构造函数是无法与bind、call、apply这三个函数组合使用的。如果不通过new命令调用构造函数,而是当做普通函数调用,此时是可以与这三个函数组合使用的。验证如下:

无法组合使用 可以组合使用
class中的this指向
var that = undefined
class ClassA {
    constructor() {
        that = this
    }
    foo() {
        return this
    }
    static staticFoo() {
        return this
    }
}
var a = new ClassA
class

“new ClassA”会调用ClassA的constructor(构造函数),此时this的指向为ClassA创建的对象本身,这点和上文中的通过new调用构造函数一致。
class中函数存储在class的原型中,此时foo函数通过a对象调用,所以函数内部的this指向a对象。
class中static函数存储在class中,此时staticFoo函数通过ClassA调用,所以函数内部的this指向ClassA。
以上结论同样适用于通过extends class创建的类,此处不再赘述。

箭头函数中的this指向
var that = undefined
var obj = {
    a: () => that = this
}
obj.a()
箭头函数

根据“谁调用,this指向谁”,此时obj调用a,为啥this不指向obj反而指向window?

也就是说:箭头函数没有自己的this,他的this是其作用域链上一层中的this。

回到刚才的例子,箭头函数所属作用域为obj对象,而obj对象所属作用域为全局作用域。所以此时通过“obj.a()”来调用,函数内部的this指向全局作用域中的this,也就是window。

再举个例子强化一下箭头函数中的this:

var that = undefined
var obj = {
    test() {
        return () => that = this
    }
}
箭头函数

“obj.test()()”调用方式中,箭头函数的作用域为test函数,而test函数的作用域为obj对象,所以箭头函数中的this指向obj对象。
“f()()”调用方式中,箭头函数的作用域为f函数(f函数是test函数的引用),f函数的作用域为全局作用域,所以箭头函数中的this指向window。

箭头函数中关于this的注意点:

也就是说,箭头函数无法通过bind、call、apply这三个函数改变this的指向。简单验证下:

var that = undefined
var obj = {
    a: () => that = this
}
箭头函数无法指定this

基于箭头函数中的this会指向作用域链上一层中的this,并且无法指定箭头函数中的this这两个特点,通常在定时器如“setTimeout”调用时采用箭头函数来填坑。

到这里已经完全掌握了箭头函数中的this吗?笔者脑洞开了一下,如果class与箭头函数组合会产生怎样的化学反应?

class中带箭头函数的this指向
var that = undefined
class A {
    test () {
        return () => that = this
    }
}
var a = new A
class中带箭头函数

“a.test()()”调用方式中,箭头函数的作用域为test函数,而test函数的作用域为“class A”创建的对象a,所以此时箭头函数内部的this指向a。
为啥“f()()”调用后that变成undefined?不应该是window吗?其实这里并没有违背箭头函数中的this指向作用域链上一层的this这个说法,来看一段MDN中关于class的描述:

根本原因在于class中的函数是在严格模式下执行的,当用“f”接收“a.test”函数时,其实相当于在全局作用域这样写:

var f = function test() {
    'use strict'
    return () => that = this
}

文章开头我们说过,全局作用域严格模式下函数内部的this指向undefined,因此采用“f()()”方式调用时,箭头函数中的this指向undefined。


“this”,想说爱你不容易。

Have fun!

显示全文