前言
紧抓中心思想:谁调用,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 thisthis在全局作用域中是指向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!