我研究JavaScript闭包(closure)已经有一段时间了。我之前只是学会了如何使用它们,而没有透彻地了解它们具体是如何运作的。那么,究竟什么是闭包?
Continue reading假设你想要非常快速地搭建一个React应用,或者你想快速地搭建用ES6学习React开发的环境,那么这篇文章你一定不想错过。
Continue reading我最近围绕着Express构建应用,尝试用不同的方法来对Express的中间件进行单元测试。今天通过Workshop的形式,一步一步地向大家介绍我的测试方式。
Continue reading上次在《JSON Web Token - 在Web应用间安全地传递信息》中我提到了JSON Web Token可以用来设计单点登录系统。我尝试用八幅漫画先让大家理解如何设计正常的用户认证系统,然后再延伸到单点登录系统。
Continue readingJSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。
Continue reading在程序员自己的小世界里,我们一直在和“量”打交道——变量和常量。可是常量真的是一成不变的吗?事实上,常量也分为两种,应用常量(application-specific constant)和环境常量(environment-specific constant)。
Continue reading在Node应用中,我们使用require
来加载模块。在目录层次相对复杂的应用中,总是会出现类似require('../../../../../module')
的调用,我把它称之为Dot Hell。我用了一些时间研究现有的解决方案,并介绍我个人认为最好的方法。
在编写JavaScript应用的时候,我们经常会使用this
关键字。那么this
关键字究竟是怎样工作的?它的设计有哪些好的地方,有哪些不好的地方?本文带大家全面系统地认识这个老朋友。
小明正在跑步,他看起来很开心
这里的小明是主语,如果没有这个主语,那么后面的代词『他』将毫无意义。有了主语,代词才有了可以指代的事物。
类比到JavaScript的世界中,我们在调用一个对象的方法的时候,需要先指明这个对象,再指明要调用的方法。
1 | var xiaoming = { |
在上面的例子中,第8行中的xiaoming
指定了run
方法运行时的主语。因此,在run
中,我们才可以用this
来代替xiaoming
这个对象。可以看到this
起了代词的作用。
同样的,对于一个JavaScript类,在将它初始化之后,我们也可以用类似的方法来理解:类的实例在调用其方法的时候,将作为主语,其方法中的this
就自然变成了指代主语的代词。
1 | class People { |
这就是我认为this关键字设计得精彩的地方!如果将调用方法的语句(上面代码的第16行)和方法本身的代码连起来,像英语一样读,其实是完全通顺的。
this
的绑定
句子的主语是可以变的,例如在下面的场景中,run
被赋值到小芳(xiaofang
)身上之后,调用xiaofang.run
,主语就变成了小芳!
1 | var xiaofang = { |
在这种情况下,句子还是通顺的。所以,非常完美!
但是如果小明很抠门,不愿意将run
方法借给小芳以后,this
就变成了小芳的话,那么小明要怎么做呢?他可以通过Function.prototype.bind让run
运行时候的this
永远为小明自己。
1 | var xiaofang = { |
那么同一个函数被多次bind
之后,到底this
是哪一次bind
的对象呢?你可以自己尝试看看。
call
与apply
Function.prototype.call允许你在调用一个函数的时候指定它的this
的值。
1 | var xiaoming = { |
Function.prototype.apply和Function.prototype.call
的功能是一模一样的,区别进在于,apply
里将函数调用所需的所有参数放到一个数组当中。
1 | var xiaoming = { |
那么call
/apply
和上面的bind
混用的时候是什么样的行为呢?这个也留给大家自行验证。但是在一般情况下,我们应该避免混用它们,否则会造成代码检查或者调试的时候难以跟踪this
的值的问题。
当方法失去主语的时候,this
不再有?
其实大家可以发现我的用词,当一个function
被调用的时候是有主语的时候,它是一个方法;当一个function
被调用的时候是没有主语的时候,它是一个函数。当一个函数运行的时候,它虽然没有主语,但是它的this
的值会是全局对象。在浏览器里,那就是window
。当然了,前提是函数没有被bind
过,也不是被apply
或call
所调用。
那么function
作为函数的情景有哪些呢?
首先,全局函数的调用就是最简单的一种。
1 | function bar() { |
立即调用的函数表达式(IIFE,Immediately-Invoked Function Expression)也是没有主语的,所以它被调用的时候this
也是全局对象。
1 | (function() { |
但是,当函数被执行在严格模式(strict-mode)下的时候,函数的调用时的this就是undefined
了。这是很值得注意的一点。
1 | function bar() { |
不可见的调用
有时候,你没有办法看到你定义的函数是怎么被调用的。因此,你就没有办法知道它的主语。下面是一个用jQuery添加事件监听器的例子。
1 | window.val = 'window val'; |
在事件的回调函数(第6行开始定义的匿名函数)里面,this
的值既不是window
,又不是obj
,而是页面上id
为text
的HTML元素。
1 | var obj = { |
这是因为匿名函数是被jQuery内部调用的,我们不知道它调用的时候的主语是什么,或者是否被bind
等函数修改过this
的值。所以,当你将匿名函数交给程序的其他部分调用的时候,需要格外地谨慎。
如果我们想要在上面的回调函数里面使用obj的val
值,除了直接写obj.val
之外,还可以在foo方法中用一个新的变量that
来保存foo
运行时this
的值。这样说有些绕口,我们看下例子便知。
1 | window.val = 'window val'; |
另外一种方法就是为该匿名函数bind
了。
1 | window.val = 'window val'; |
总结
在JavaScript中this
的用法的确是千奇百怪,但是如果利用自然语言的方式来理解,一切就顺理成章了。不知道你读完这篇文章时候理解了吗?还是睡着了?亲……醒醒……
如果有任何疑问,欢迎在评论区讨论。另外,欢迎在下方订阅我的半月刊,我将为你分享有趣的技术、产品、设计的片段。
你有没有好奇过,当你试图打开一个网页的时候,这个世界上都发生了一些什么事情?会不会因为你手气键落,产生了蝴蝶效应,指尖的风拂起千年后你梦中的那个女孩的刘海?咳,也不是没有可能。今天我就来告诉你会发生什么事情,你可以沏一壶茶,坐在躺椅上,慢慢品味……
Continue reading在互联网行业里面工作,能给我带来的一个乐趣就是“快”。天下武功,唯快不破。我们可以轻易地做到一天三次以上的产品更新速度,这是和许多传统行业的区别之一。如何利用好这个优势,在我眼里成为了产品发展的关键所在。
Continue reading