在之前的【未经授权禁止转载】一篇转载文【版权所有】唐霜 www.tangshuang.net中,介绍了javascript中的thi【访问 www.tangshuang.net 获取更多精彩内容】原创内容,盗版必究。s,但是经过这么多年之后,我始终觉得,对原创内容,盗版必究。未经授权,禁止复制转载。this的解释太过复杂和神秘,似乎thi【未经授权禁止转载】【版权所有】唐霜 www.tangshuang.nets是不可把握的,像是玄学。但是,真的需要【未经授权禁止转载】【作者:唐霜】这么费劲去理解JavaScript中的t未经授权,禁止复制转载。【原创内容,转载请注明出处】his吗?我想,并不必。
本文作者:唐霜,转载请注明出处。本文版权归作者所有,未经授权不得转载。【作者:唐霜】在工作多年以后,再未遇到this带来的问【访问 www.tangshuang.net 获取更多精彩内容】原创内容,盗版必究。题。除了对this本身的理解比较自然之外【版权所有,侵权必究】转载请注明出处:www.tangshuang.net,我写代码的方式也起到了很大的帮助。th【原创内容,转载请注明出处】【版权所有】唐霜 www.tangshuang.netis出现的位置,由于代码的简洁明了,反而【原创不易,请尊重版权】本文作者:唐霜,转载请注明出处。让this出现bug的可能性降低。但,对【版权所有】唐霜 www.tangshuang.net【本文受版权保护】于没有花时间去理解this的同学而言,或【本文受版权保护】【本文受版权保护】许读完这篇文章可以更快的理解和把握thi【转载请注明来源】【原创内容,转载请注明出处】s的指向。
【未经授权禁止转载】【版权所有,侵权必究】著作权归作者所有,禁止商业用途转载。this的指向只有一种情况本文作者:唐霜,转载请注明出处。
未经授权,禁止复制转载。【转载请注明来源】原创内容,盗版必究。原创内容,盗版必究。对于网上五花八门的举例而言,对我来说,它【版权所有,侵权必究】【转载请注明来源】们全部都是一种情况:this指向func原创内容,盗版必究。【版权所有,侵权必究】tion的调用者。this指向谁并非在定【本文受版权保护】原创内容,盗版必究。义function时决定,而是在func【版权所有】唐霜 www.tangshuang.net【版权所有】唐霜 www.tangshuang.nettion被运行时决定的。而由谁来调用fu【原创不易,请尊重版权】【版权所有】唐霜 www.tangshuang.netnction,则其内部的this指向调用原创内容,盗版必究。【转载请注明来源】者。
转载请注明出处:www.tangshuang.net【原创不易,请尊重版权】本文作者:唐霜,转载请注明出处。【版权所有】唐霜 www.tangshuang.netthis指向所在作用域函数被运行时的直接本文作者:唐霜,转载请注明出处。【本文受版权保护】调用者。
【原创内容,转载请注明出处】本文作者:唐霜,转载请注明出处。本文版权归作者所有,未经授权不得转载。this究竟指向谁和它的定义时真的没有一【版权所有】唐霜 www.tangshuang.net转载请注明出处:www.tangshuang.net点关系,我们来看一个代码:
【原创内容,转载请注明出处】转载请注明出处:www.tangshuang.net【作者:唐霜】const prototypes = {
song: 'Hello!',
sing() {
alert(this.song)
},
}
const sing = prototypes.sing
sing()
这个例子是为了说明,你在写代码时,写下t转载请注明出处:www.tangshuang.net【作者:唐霜】his的那一刻,绝对不能去看this上有未经授权,禁止复制转载。【版权所有,侵权必究】什么,因为this上到底有什么是不可预测【本文首发于唐霜的博客】【本文首发于唐霜的博客】的,只有当this所在的函数被运行时,t【关注微信公众号:wwwtangshuangnet】未经授权,禁止复制转载。his上到底有什么才是可知的。这也是为什【关注微信公众号:wwwtangshuangnet】【本文受版权保护】么this难的原因,因为不可预测性。
本文作者:唐霜,转载请注明出处。转载请注明出处:www.tangshuang.net【访问 www.tangshuang.net 获取更多精彩内容】【作者:唐霜】需要强调的是,“作用域函数”是指用fun【原创不易,请尊重版权】本文版权归作者所有,未经授权不得转载。ction声明的函数(包括匿名函数),箭【未经授权禁止转载】【版权所有,侵权必究】头函数会在下文讨论。因为在某些情况下,这著作权归作者所有,禁止商业用途转载。本文作者:唐霜,转载请注明出处。个调用者很难被人察觉,开发者会认错调用者转载请注明出处:www.tangshuang.net【版权所有,侵权必究】。我们来举个例子,让你很难察觉调用者究竟【访问 www.tangshuang.net 获取更多精彩内容】本文版权归作者所有,未经授权不得转载。是谁。
【关注微信公众号:wwwtangshuangnet】未经授权,禁止复制转载。【作者:唐霜】原创内容,盗版必究。function invoke() {
function createName() {
return this.familyName + ' ' + this.customName
}
this.name = createName()
this.say = function() {
alert(this.name)
}
}
const person = {
familyName: 'Adrew',
customName: 'Karm',
invoke,
}
person.invoke()
person.say()
调用之后,程序能够正常运行,但是结果却非转载请注明出处:www.tangshuang.net著作权归作者所有,禁止商业用途转载。预期。这是因为createName函数中【访问 www.tangshuang.net 获取更多精彩内容】未经授权,禁止复制转载。的this指向了一个我们不知道的对象,而【原创内容,转载请注明出处】【未经授权禁止转载】非person这个对象。问题的根源在于在【未经授权禁止转载】【版权所有,侵权必究】这个程序运行过程中,createName【未经授权禁止转载】著作权归作者所有,禁止商业用途转载。的调用者并非person,具体是谁我们暂著作权归作者所有,禁止商业用途转载。【版权所有,侵权必究】且还不知道。既然createName的调原创内容,盗版必究。【版权所有,侵权必究】用者并非person,那么它所创建的作用本文作者:唐霜,转载请注明出处。【原创内容,转载请注明出处】域中的this,在其运行时指向的自然就不【关注微信公众号:wwwtangshuangnet】【关注微信公众号:wwwtangshuangnet】是person了。那么此时createN【原创内容,转载请注明出处】【访问 www.tangshuang.net 获取更多精彩内容】ame的调用者究竟是谁?
【原创不易,请尊重版权】【版权所有,侵权必究】【未经授权禁止转载】未经授权,禁止复制转载。本文版权归作者所有,未经授权不得转载。我们重新去观察createName的运行【转载请注明来源】本文作者:唐霜,转载请注明出处。过程,它是在person.invoke(未经授权,禁止复制转载。著作权归作者所有,禁止商业用途转载。)的时候被定义,且立即就执行的,但我们前【关注微信公众号:wwwtangshuangnet】转载请注明出处:www.tangshuang.net面说过,函数定义时并不决定this的指向【访问 www.tangshuang.net 获取更多精彩内容】【访问 www.tangshuang.net 获取更多精彩内容】,只有运行时决定了this的指向。因此,著作权归作者所有,禁止商业用途转载。【转载请注明来源】我们要去看函数运行时它的调用者。createName的运行时调用者并不存【版权所有】唐霜 www.tangshuang.net著作权归作者所有,禁止商业用途转载。在。这听上去有些荒唐,但事实如此。转载请注明出处:www.tangshuang.net
【本文受版权保护】【访问 www.tangshuang.net 获取更多精彩内容】著作权归作者所有,禁止商业用途转载。在JavaScript中,如果函数的调用【关注微信公众号:wwwtangshuangnet】未经授权,禁止复制转载。者不存在,那么它的调用者将会是windo本文版权归作者所有,未经授权不得转载。本文版权归作者所有,未经授权不得转载。w(或其他环境中的全局变量),所以,最终【版权所有】唐霜 www.tangshuang.net本文版权归作者所有,未经授权不得转载。,createName中this指向wi【访问 www.tangshuang.net 获取更多精彩内容】原创内容,盗版必究。ndow。
本文版权归作者所有,未经授权不得转载。原创内容,盗版必究。【原创不易,请尊重版权】我们怎么证明这件事呢?我们来写这样一段代未经授权,禁止复制转载。著作权归作者所有,禁止商业用途转载。码:
【版权所有,侵权必究】未经授权,禁止复制转载。【关注微信公众号:wwwtangshuangnet】【关注微信公众号:wwwtangshuangnet】【未经授权禁止转载】(function () {
function invoke() {
function createName() {
console.log(this.familyName + ' ' + this.customName)
return this.familyName + ' ' + this.customName
}
this.name = createName()
this.say = function() {
console.log(this.name)
}
}
const person = {
familyName: 'Adrew',
customName: 'Karm',
invoke,
}
person.invoke()
person.say()
console.log(this.familyName + ' ' + this.customName)
}).call({
familyName: 'Jimy',
customName: 'Hardsam',
})
你会发现,createName外层函数的转载请注明出处:www.tangshuang.net未经授权,禁止复制转载。this指向了call中传入的对象,但是【原创内容,转载请注明出处】【版权所有】唐霜 www.tangshuang.netcreateName中的this丝毫不受原创内容,盗版必究。本文作者:唐霜,转载请注明出处。影响,仍然指向window。怎么修复这个【版权所有】唐霜 www.tangshuang.net【原创不易,请尊重版权】问题呢?我们其实可以采取一个比较笨的办法【本文首发于唐霜的博客】【版权所有,侵权必究】:
原创内容,盗版必究。著作权归作者所有,禁止商业用途转载。著作权归作者所有,禁止商业用途转载。本文作者:唐霜,转载请注明出处。【转载请注明来源】this.createName = createName this.name = this.createName() delete this.createName // 等效于 this.name = createName.call(this)
在实际开发中,我们要注意另外一个事实,当【本文受版权保护】著作权归作者所有,禁止商业用途转载。我们在函数中声明了’use 著作权归作者所有,禁止商业用途转载。【版权所有】唐霜 www.tangshuang.netstrict’时,这种指向w【未经授权禁止转载】【关注微信公众号:wwwtangshuangnet】indow的行为会被改变,this将没有本文版权归作者所有,未经授权不得转载。【转载请注明来源】指向,也就是undefined,但是需要【原创内容,转载请注明出处】【本文受版权保护】注意的是’use stric【版权所有】唐霜 www.tangshuang.net【本文受版权保护】t’必须声明于当前作用域:
【版权所有,侵权必究】转载请注明出处:www.tangshuang.net【版权所有,侵权必究】【原创不易,请尊重版权】(function () {
'use strict'
function invoke() {
function createName() {
console.log(this.familyName + ' ' + this.customName)
return this.familyName + ' ' + this.customName
}
this.name = createName()
this.say = function() {
console.log(this.name)
}
}
const person = {
familyName: 'Adrew',
customName: 'Karm',
invoke,
}
person.invoke()
person.say()
console.log(this.familyName + ' ' + this.customName)
}).call({
familyName: 'Jimy',
customName: 'Hardsam',
})
上面这段代码运行时会报错,因为声明【版权所有】唐霜 www.tangshuang.net【本文受版权保护】217;use strict’【转载请注明来源】著作权归作者所有,禁止商业用途转载。;只之后this指向了undefined【本文受版权保护】【版权所有,侵权必究】。为了使代码更容易理解,我们应该声明【关注微信公众号:wwwtangshuangnet】本文作者:唐霜,转载请注明出处。8217;use strict̵【版权所有,侵权必究】未经授权,禁止复制转载。7;,因为没有调用者的this指向und【未经授权禁止转载】【本文首发于唐霜的博客】efined才更符合逻辑。
原创内容,盗版必究。【原创内容,转载请注明出处】【未经授权禁止转载】没有调用者的函数中this指向了unde原创内容,盗版必究。原创内容,盗版必究。fined。
著作权归作者所有,禁止商业用途转载。本文版权归作者所有,未经授权不得转载。【作者:唐霜】原创内容,盗版必究。为了让上述表示更精确,我们应该习惯于在每转载请注明出处:www.tangshuang.net【原创内容,转载请注明出处】一个文档中声明’use st【作者:唐霜】本文版权归作者所有,未经授权不得转载。rict’,因为’【原创不易,请尊重版权】原创内容,盗版必究。;use strict’具有【访问 www.tangshuang.net 获取更多精彩内容】原创内容,盗版必究。继承性,在同一作用域内声明一次̵转载请注明出处:www.tangshuang.net原创内容,盗版必究。7;use strict’,原创内容,盗版必究。原创内容,盗版必究。那么在该作用域内声明的所有函数都继承其特本文版权归作者所有,未经授权不得转载。【作者:唐霜】性。所以,大部分开发者会直接在js文件的【版权所有,侵权必究】原创内容,盗版必究。最顶端,或者一个模块的最开始声明R【关注微信公众号:wwwtangshuangnet】【作者:唐霜】17;use strict’【版权所有】唐霜 www.tangshuang.net著作权归作者所有,禁止商业用途转载。。
本文作者:唐霜,转载请注明出处。本文作者:唐霜,转载请注明出处。本文版权归作者所有,未经授权不得转载。控制调用者著作权归作者所有,禁止商业用途转载。
【本文首发于唐霜的博客】著作权归作者所有,禁止商业用途转载。【版权所有,侵权必究】【关注微信公众号:wwwtangshuangnet】我们已经知道call, apply, b【版权所有】唐霜 www.tangshuang.net转载请注明出处:www.tangshuang.netind这三个函数的方法可以修改一个函数内【原创内容,转载请注明出处】【原创不易,请尊重版权】this的指向。其中,call和appl【原创内容,转载请注明出处】本文版权归作者所有,未经授权不得转载。y是规定一次性运行时的调用者,例如fn.本文版权归作者所有,未经授权不得转载。【访问 www.tangshuang.net 获取更多精彩内容】call(some)即规定了在这次运行时本文版权归作者所有,未经授权不得转载。【转载请注明来源】,将fn的调用者规定为some,相当于s【访问 www.tangshuang.net 获取更多精彩内容】【访问 www.tangshuang.net 获取更多精彩内容】ome.fn()。因此,对于fn内的th转载请注明出处:www.tangshuang.net【版权所有】唐霜 www.tangshuang.netis,它的调用者就是some。而bind【转载请注明来源】著作权归作者所有,禁止商业用途转载。的作用是返回一个新函数,该新函数的调用者本文版权归作者所有,未经授权不得转载。【未经授权禁止转载】绑定为某个对象,而且该绑定是永久的,无法【原创内容,转载请注明出处】本文作者:唐霜,转载请注明出处。被改变的,例如const fn2 = f【原创内容,转载请注明出处】【本文首发于唐霜的博客】n.bind(some),那任何时候运行本文版权归作者所有,未经授权不得转载。著作权归作者所有,禁止商业用途转载。fn2(),它的调用者都是some,即使著作权归作者所有,禁止商业用途转载。【版权所有】唐霜 www.tangshuang.net你用call、apply去修改调用者,也未经授权,禁止复制转载。原创内容,盗版必究。是无效的。
【版权所有】唐霜 www.tangshuang.net【原创内容,转载请注明出处】著作权归作者所有,禁止商业用途转载。本文版权归作者所有,未经授权不得转载。【版权所有】唐霜 www.tangshuang.net隐含的调用者【版权所有,侵权必究】
转载请注明出处:www.tangshuang.net【版权所有,侵权必究】转载请注明出处:www.tangshuang.net本文版权归作者所有,未经授权不得转载。本文版权归作者所有,未经授权不得转载。从理论上讲,一个被声明的且没有调用者的函未经授权,禁止复制转载。【本文首发于唐霜的博客】数内的this指向undefined。但【版权所有】唐霜 www.tangshuang.net转载请注明出处:www.tangshuang.net是在浏览器(包括例如nodejs这样的环【原创不易,请尊重版权】【作者:唐霜】境)中执行某些特殊动作时,这些特殊动作会【关注微信公众号:wwwtangshuangnet】【访问 www.tangshuang.net 获取更多精彩内容】临时控制传入参数的函数的调用者,最为常见本文作者:唐霜,转载请注明出处。【原创不易,请尊重版权】的就是setTimeout,它接收一个函著作权归作者所有,禁止商业用途转载。原创内容,盗版必究。数作为参数,并且异步执行这个函数,在异步【版权所有】唐霜 www.tangshuang.net本文作者:唐霜,转载请注明出处。执行时,它会通过call的方式临时将参数【本文首发于唐霜的博客】【本文首发于唐霜的博客】函数的调用者切换为window。除了se【转载请注明来源】【未经授权禁止转载】tTimeout,DOM的事件回调函数在【版权所有,侵权必究】【访问 www.tangshuang.net 获取更多精彩内容】运行时,会将调用者切换为当前DOM节点。【版权所有,侵权必究】转载请注明出处:www.tangshuang.net总之,这种隐含的调用者在标准文档中都有说【关注微信公众号:wwwtangshuangnet】本文版权归作者所有,未经授权不得转载。明。
【版权所有】唐霜 www.tangshuang.net原创内容,盗版必究。原创内容,盗版必究。【原创内容,转载请注明出处】【版权所有,侵权必究】要主动控制这些传入参数的调用者,需要你在【版权所有】唐霜 www.tangshuang.net【作者:唐霜】编写代码时采用不同的策略。一般有两种方式未经授权,禁止复制转载。转载请注明出处:www.tangshuang.net,一种是使用bind将被传函数的调用者绑原创内容,盗版必究。【版权所有,侵权必究】定。另一种是使用箭头函数。
【本文受版权保护】转载请注明出处:www.tangshuang.net转载请注明出处:www.tangshuang.net箭头函数的调用者?【版权所有】唐霜 www.tangshuang.net
【版权所有】唐霜 www.tangshuang.net【版权所有,侵权必究】【原创不易,请尊重版权】【未经授权禁止转载】和通过function声明的函数不同,箭著作权归作者所有,禁止商业用途转载。【版权所有,侵权必究】头函数的性质并非作用域函数,在其他语言里著作权归作者所有,禁止商业用途转载。【未经授权禁止转载】称为Lambda,是一种表达式函数。在E【版权所有,侵权必究】本文版权归作者所有,未经授权不得转载。S6之后,js拥有了新的作用域范围,即“著作权归作者所有,禁止商业用途转载。著作权归作者所有,禁止商业用途转载。块级作用域”。而Lambda函数所创建的本文作者:唐霜,转载请注明出处。未经授权,禁止复制转载。作用域是块级作用域表达式,因此,它不具备原创内容,盗版必究。原创内容,盗版必究。调用者,其内的this指向的是它所在的作【原创内容,转载请注明出处】本文版权归作者所有,未经授权不得转载。用域函数的调用者。例如:
转载请注明出处:www.tangshuang.net【访问 www.tangshuang.net 获取更多精彩内容】原创内容,盗版必究。function invoke() {
const createName = () => this.familyName + ' ' + this.customName
this.name = createName()
}
person.invoke()
上面代码中,createName虽然是一【版权所有】唐霜 www.tangshuang.net【原创不易,请尊重版权】个函数,但它是一个表达式函数,并不具备作转载请注明出处:www.tangshuang.net【本文首发于唐霜的博客】用域函数的能力,其内部的this的所有者著作权归作者所有,禁止商业用途转载。【原创内容,转载请注明出处】是外层invoke这个函数,因此,当in【访问 www.tangshuang.net 获取更多精彩内容】【关注微信公众号:wwwtangshuangnet】voke被person调用时,this指【版权所有】唐霜 www.tangshuang.net原创内容,盗版必究。向了person。
【访问 www.tangshuang.net 获取更多精彩内容】【关注微信公众号:wwwtangshuangnet】【原创不易,请尊重版权】转载请注明出处:www.tangshuang.net在异步代码中,即时作用域函数被调用执行完【转载请注明来源】著作权归作者所有,禁止商业用途转载。毕,但是this的指针本保留下来。我们看【原创不易,请尊重版权】原创内容,盗版必究。一个例子:
【原创内容,转载请注明出处】本文作者:唐霜,转载请注明出处。【访问 www.tangshuang.net 获取更多精彩内容】【未经授权禁止转载】function invoke() {
setTimeout(() => this.name = 'tomy', 500)
// 以前的写法
// var that = this
// setTimeout(function() {
// that.name = 'tomy'
// }, 500)
}
person.invoke()
在invoke被person调用运行时,未经授权,禁止复制转载。【原创不易,请尊重版权】this的指针就指向了person,在s原创内容,盗版必究。【版权所有】唐霜 www.tangshuang.netetTimeout中的this保留了该指【转载请注明来源】【本文首发于唐霜的博客】向,因此,无论这个this在何时何处被使未经授权,禁止复制转载。【作者:唐霜】用,只要invoke运行时this明确了本文版权归作者所有,未经授权不得转载。【版权所有,侵权必究】自己的指向,就不会改变。
【本文受版权保护】【转载请注明来源】【版权所有,侵权必究】【本文首发于唐霜的博客】【版权所有】唐霜 www.tangshuang.net面对这样的疑问,你还会遇到这样的问题:【作者:唐霜】
本文作者:唐霜,转载请注明出处。本文作者:唐霜,转载请注明出处。原创内容,盗版必究。function invoke() {
const createName = () => this.familyName + ' ' + this.customName
return createName
}
const person = {
familyName: 'Adrew',
customName: 'Karm',
invoke,
}
const createName = person.invoke()
const some = {
familyName: 'Ximen',
customName: 'Sam',
createName,
}
const name = some.createName()
console.log(name)
你会发现,some.createName未经授权,禁止复制转载。【原创不易,请尊重版权】()的结果并没有得到some的正确结果,未经授权,禁止复制转载。【转载请注明来源】这是因为invoke的调用者是perso本文版权归作者所有,未经授权不得转载。本文版权归作者所有,未经授权不得转载。n,invoke内的this只会指向pe本文版权归作者所有,未经授权不得转载。【版权所有,侵权必究】rson,而不会指向任何人。(需要强调,本文版权归作者所有,未经授权不得转载。未经授权,禁止复制转载。上文中,多次提到函数内的this,是指该转载请注明出处:www.tangshuang.net转载请注明出处:www.tangshuang.netthis所在的直接作用域是该函数,而不是【本文受版权保护】【转载请注明来源】函数内函数中的this。)
著作权归作者所有,禁止商业用途转载。转载请注明出处:www.tangshuang.net原创内容,盗版必究。【原创内容,转载请注明出处】【本文首发于唐霜的博客】调用栈本文作者:唐霜,转载请注明出处。
【版权所有】唐霜 www.tangshuang.net本文版权归作者所有,未经授权不得转载。【本文受版权保护】栈是一种后进先出的结构,对于js中函数的【原创内容,转载请注明出处】未经授权,禁止复制转载。执行而言,在一个函数中调用另外一个函数,【转载请注明来源】本文作者:唐霜,转载请注明出处。就会形成调用栈。调用栈中,只有顶层的被调著作权归作者所有,禁止商业用途转载。【转载请注明来源】用函数在执行,底下的函数全部处于等待执行原创内容,盗版必究。【访问 www.tangshuang.net 获取更多精彩内容】状态,当顶部函数执行完毕,被弹出栈,才会【作者:唐霜】【本文受版权保护】回到下面一层的调用函数中继续执行。于是,著作权归作者所有,禁止商业用途转载。【原创内容,转载请注明出处】在这个调用栈中,this的指向也开始会变原创内容,盗版必究。【本文受版权保护】得扑朔迷离。但实际上,每一个函数在执行时【本文受版权保护】【版权所有】唐霜 www.tangshuang.net它都有自己的调用者(包括undefine本文作者:唐霜,转载请注明出处。【本文受版权保护】d和this),在一层又一层的调用栈中,未经授权,禁止复制转载。转载请注明出处:www.tangshuang.net你需要想象当前这一层调用的主人是谁,你只本文作者:唐霜,转载请注明出处。【版权所有,侵权必究】要确认当前执行的函数的调用者是谁,那么该【本文首发于唐霜的博客】原创内容,盗版必究。函数中的this指向谁就变得清晰可见。
本文版权归作者所有,未经授权不得转载。【版权所有,侵权必究】【访问 www.tangshuang.net 获取更多精彩内容】【本文首发于唐霜的博客】2019-04-01 3574 javascript, this


