JavaScript中的变色龙this

前言

本文为学习过程中的this小节,作为一名JavaScript自学未成才的编程人员,还没从“原型继承”中回过神来(可以参考笔者上一篇文章《大话JavaScript对象》),又发现另一个让人难受的事实:JavaScript中的this是动态变化的!啥意思?this并非在定义时就确定好指向,而是在运行时才确定,this最终指向调用他的那个对象。this随着环境的变化而变化,这完全就是自然界中的变色龙。

紧抓中心思想:谁调用,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指向

上篇文章说过,class的本质还是function,但是毕竟写法不一样,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。
--摘自MDN

也就是说:箭头函数没有自己的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的注意点:

由于 箭头函数没有自己的this指针,通过 call() 或 apply() 方法调用一个函数时,只能传递参数(不能绑定this---译者注),他们的第一个参数会被忽略。(这种现象对于bind方法同样成立---译者注)
--摘自MDN

也就是说,箭头函数无法通过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的描述:

类声明和类表达式的主体都执行在严格模式下。比如,构造函数,静态方法,原型方法,getter和setter都在严格模式下执行。
--摘自MDN

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

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

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


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

Have fun!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容