this的四种绑定之详细介绍


this是一个关键字,不是变量,也不是属性名,JavaScript的语法不允许给this赋值。和变量不同,关键字this没有作用域的限制,嵌套的函数不会从调用它的函数中继承this。如果嵌套函数作为方法调用,其this的值指向调用它的对象。如果嵌套函数作为函数调用,其this值不是全局对象就是undefined(非严格模式下是全局对象(即window对象),严格模式下是undefined)。很多人误以为调用嵌套函数时,this会指向调用外层函数的上下文。如果你想访问这个外部函数的this值,需要将this的值保存在一个变量中,这个变量和内部函数都同在一个作用域内。通常使用变量self来保存this。

记住一点:函数中的this总指向调用它的对象。(金句)

对应函数的四种调用方法(函数调用、方法调用、通过call()和apply()方法的间接调用和构造函数调用),this有四种绑定方式,即:默认绑定、隐式绑定、显式绑定和new绑定。


默认绑定

当一个函数没有明确的调用对象的时候,也就是单纯作为独立函数调用的时候,将对函数的this使用默认绑定:绑定到全局的window对象

注:以函数形式调用的函数通常不使用this关键字,不过,此时的“this”可以用来判断当前是否是严格模式

代码说明:

本代码第16行是IIFE(立即执行函数)的写法。

严格模式下,this指向undefined,所以第13行代码打印出false,14行打印出true

非严格模式下(即注释掉第9行代码),this指向window,所以第13行代码打印出true,14行打印出false。

注:除非特别说明,本文的代码默认运行在非严格模式下。

一、简单的函数调用写法


代码说明:

第19-24行代码很容易就能看懂,第24行就是普通的函数调用,a()作为独立函数调用,此时,this指向window或者undefined。

二、嵌套函数调用

此时的代码你看懂了吗?b()是什么方式调用?obj.foo()是什么方式的调用?

代码说明:

第36行,b()依然是独立调用,它只是嵌套在foo函数中。所以它的指向依然是是默认指向(window or undefined),而全局变量a的值是0(从第27行代码可看出来),所以此时34行代码打印出 0.

Q:如果将34行代码复制到36行代码的下面一行呢?会出现什么效果?

运行代码会发现第37行代码打印出1,这是为什么呢?

这就是马上要讲的函数的另一种调用方式-----方法调用,方法调用引发this的隐式绑定。


隐式绑定

首先解释下函数的方法调用

一个方法无非是个保存在一个对象的属性里的JavaScript函数。白话说,就是在一个对象中定义函数,调用该函数就是方法调用,还记得前文提到的金句吗?“函数中的this总指向调用它的对象”,此时的this就指向调用该函数的对象。

还是用上面的代码(第27行---40行)

代码说明:

先说一下第一个this:

第27行定义了一个变量a,很显然,此时的a是全局变量。第28行定义了一个obj对象,在obj对象中,第29行定义了一个变量a,此时,a是obj对象中的局部变量,第30行定义一个foo函数,这里的foo函数作用域也是obj对象,第32行是在foo函数里定义了一个b函数,这里的b函数的作用域是foo函数,第36行是在foo函数中,调用b函数,注意哦,此时的调用是独立调用哦,就是普通的函数调用哦,所以当40行调用foo函数时,b函数被调用,b函数里的this指向全局,所以,打印出 0 。

现在介绍第二个this:

当代码流执行到第40行时,调用obj对象中的foo函数,正如前文所讲,此时的调用就称作函数的方法调用。函数的使用方法调用时,this指向调用该方法的对象,在这里就是this指向obj对象。而obj对象中,a的值在第29行被赋值为1,所以第37行代码打印出 1 。

隐式丢失

前文之所以称作隐式绑定,是因为此时的this隐式绑定在obj对象上。所以this的指向是obj对象。然而,当绑定至上下文对象的函数被赋值给一个新的函数,或者传递给回调函数时,函数中的 this容易丢失掉绑定对象,此时this执行默认绑定规则

一、赋值丢失(绑定至上下文对象的函数被赋值给一个新的函数)

代码说明:

第56行定义全局变量a,并赋值为1,57行定义obj对象,58行在obj对象中定义局部变量a,并赋值为2,59行在obj对象中定义foo函数。63行很奇怪是吧,为了方便介绍,我们将其等价成a=b,

深度解释“a=b”赋值语句:

第一步,计算表达式a,得到a的地址refa;

第二步,计算表达式b, 得到b的值valueb;

第三步:将valueb赋给refa

第四步:返回valueb

也就是说,obj.foo = obj.foo会返回第二个obj.foo所指向的函数表达式,所以第63行代码就等价于

很显然,64行代码时定义了一个新的函数,并且是匿名函数,而在红宝书(P182)中介绍过,匿名函数的执行环境具有全局性,所以此时的this指向全局,即打印出 1.

再深入理解一下,你知道为什么说第64行是定义了一个新函数吗?

在64行的前面插入代码console.log(obj.foo),你会发现浏览器会打印出一个函数表达式,也就是obj.foo是一个函数表达式,第64行是把该函数表达式赋值给fooo,所以此时的fooo是一个函数

上述代码等价于

注意:此时浏览器打印出 2是因为70行代码将变量重新赋值成2,覆盖了68行的赋值,即此时的a仍然是全局变量。上述代码是一个函数的独立调用。所以此时的this指向全局。

要不要再尝试一下?现在把65行改成var fooo = obj.foo()

可以发现此时第66行报错,这是为什么呢?聪明的同学已经发现,obj.foo()是一个函数的方法调用,此时的返回值是obj中的局部变量 2,此时的fooo是一个被声明的变量,那么它就不再是一个函数的形式了,所以也就不能使用fooo()了。

二、传参丢失

1,语言内置的函数传参,比如使用setTimeout()时

大家都知道,setTimeout()的用法如下

例如:setTimeout(function(){....}, 3000);

在经过3000ms后执行{....}的内容,第83行代码的执行过程是先将obj.foo赋值给setTimeout的第一个参数,问题就出现在这里,请看前文的赋值丢失情况介绍,是不是同一个道理?

2,自定义函数传参时

其实这就是第一种情况的变种,实际上参数传递就是一种隐式赋值

显式绑定

函数的间接调用引发this的显示绑定。

JavaScript中的函数也是对象,和其他JavaScript对象没什么两样,函数对象也可以包含方法,其中的两个方法call()和apply()可以作为任何对象的方法来调用。两个方法都允许显式指定调用所需的this值。

bind()是ES5中新增的方法,当在函数foo()上调动bind()方法并传入一个对象obj作为参数,这个方法将返回一个新的函数,当然此时this也便是方法调用了,所以会指向当前对象obj。

注:普通的显式绑定无法解决隐式丢失问题

硬绑定

硬绑定是显式绑定的一个变种,固定this的指向,上例中的bind()是硬绑定的内置函数

代码说明:

在bar函数内部手动调用foo.call(obj)。因此,无论之后如何调用函数bar,它总会手动在obj上调用foo

new绑定

如果函数或者方法调用之前带有关键字new,它就构成构造函数调用。对于this绑定来说,称为new绑定。

构造函数的四个步骤:

第一步:创建一个新对象

第二步:将构造函数的作用域赋给新对象(因此this就指向了这个新对象)

第三步:执行构造函数中的代码(为这个新对象添加属性)

第四步:返回新对象

最后,他们的优先级是:new > 显式>隐式>默认


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

推荐阅读更多精彩内容