在JavaScript世界里有强制——一个侦探故事
内森,你不知道你发现了什么。
我已经调查了好几周了。这一切都始于去年十月的一个暴风雨之夜。我偶然发现了Number类——我的意思是,为什么JavaScript会有一个Number类呢?
我对接下来要发现的事情毫无准备。
事实证明,JavaScript在不告诉你的情况下,已经在你眼皮底下把你的数字变成对象,把对象变成数字。
JavaScript希望没有人会发现,但人们已经报告了奇怪的意想不到的行为,现在多亏了你和你的问题,我有了我需要的证据来揭露这件事。
这是我们目前所发现的。我不知道我是否应该告诉你这个——你可能想要关闭你的JavaScript。
> function dis() { return this }
undefined
当你创建这个函数时,你可能不知道接下来会发生什么。一切看起来都很好,目前一切都很好。
没有错误消息,控制台输出中只有“未定义”一词,这正是您所期望的。毕竟,这是一个函数声明——它不应该返回任何东西。
但这仅仅是个开始。接下来发生的事,谁也没有预料到。
> five = dis.call(5)
Number {[[PrimitiveValue]]: 5}
是的,我知道,你期待的是5分,但这不是你得到的,是吗?你得到了别的东西,不同的东西。
同样的事情也发生在我身上。
我不知道这是怎么回事。我都快疯了。我睡不着,吃不下,我试着把它喝掉,但再多的激浪也不能让我忘记。这完全说不通!
就在那时,我发现了真正发生的事情——这是胁迫,它就发生在我眼前,但我太瞎了,看不到它。
Mozilla试图把它埋藏在他们知道没有人会看到的地方——他们的文档。
经过数小时的反复阅读,我发现了这个:
“…原始值将被转换为对象。”
它就在那里,可以用Open Sans字体拼写出来。它是call()函数-我怎么能这么愚蠢?!
我的号码已经不再是一个号码了。当我将它传递给call()时,它变成了其他东西。它变成了……一个对象。
一开始我简直不敢相信。这怎么可能呢?但我不能忽视周围越来越多的证据。如果你仔细看,它就在那里:
> five.wtf = 'potato'
"potato"
> five.wtf
"potato"
WTF是对的。数字不能有自定义属性——我们都知道!这是他们在警校教你的第一件事。
我们应该在看到控制台输出的那一刻就知道——这不是我们认为的数字。这是一个冒名顶替者——一个把自己冒充成我们可爱的无辜数字的物体。
这是……(5)新号码。
当然!这完全说得通。Call()有工作要做,他必须调用一个函数,要做到这一点,他需要填充这个,他知道他不能用一个数字来做——他需要一个对象,他愿意做任何事情来得到它,即使这意味着强制我们的数字。当call()看到数字5时,他看到了一个机会。
这是一个完美的计划:等到没人注意的时候,把我们的号码换成一个看起来和它一模一样的东西。我们得到一个数字,函数被调用,没有人会知道。
这真是个完美的计划,但就像所有的计划一样,即使是完美的计划,上面有个洞,我们马上就要掉进去了。
看,call()不明白的是,他不是镇上唯一一个可以胁迫数字的人。这毕竟是JavaScript——强制无处不在。
call()拿走了我的号码,直到我把他的小冒名顶替者的面具拉下来,并将他暴露给整个Stack Overflow社区,我才会停止。
但如何?我需要一个计划。当然它看起来像一个数字,但我知道它不是,一定有办法证明这一点。就是这样!它看起来像一个数字,但它能像一个数字吗?
我告诉五个人我需要他变大五倍——他没有问为什么,我也没有解释。然后我做了任何优秀程序员都会做的事情:相乘。当然,他不可能通过造假来摆脱这一切。
> five * 5
25
> five.wtf
'potato'
该死的!不仅5乘起来很好wtf还在那里。该死的家伙和他的土豆。
到底发生了什么事?我错了吗?五真的是个数字吗?不,我一定遗漏了什么,我知道,我一定忘记了什么,一些如此简单和基本的东西,以至于我完全忽略了它。
这看起来不太好,我已经写了几个小时的答案,但我仍然没有更接近我的观点。我不能再坚持下去了,最终人们会停止阅读,我必须想出点什么,而且必须要快。
等等,就是这个!5不是25,25是结果,25是一个完全不同的数字。当然,我怎么会忘记呢?数字是不可变的。当你用5乘5的时候,什么都没有分配给任何东西你只是创建了一个新的数字25。
这肯定就是这里发生的事情。当我用5乘5的时候,5一定会变成一个数字这个数字一定是用来做乘法的。输出到控制台的是乘法运算的结果,而不是5本身的值。5没有分配到任何东西,所以它当然不会改变。
那么我如何让5把操作的结果赋给自己呢?我明白了。五点还没来得及思考,我就大喊“++”。
> five++
5
啊哈!我抓到他了!每个人都知道5 + 1 = 6,这就是我需要证明5不是数字的证据!那是个骗子!一个不会数数的骗子。我可以证明这一点。实数是这样的:
> num = 5
5
> num++
5
等待?这里发生了什么?唉,我太忙了,都忘了后期操作员是怎么工作的了。当我在5的末尾使用++时,我说的是返回当前值,然后增加5。它是将操作发生之前的值打印到控制台。num实际上是6,我可以证明它:
>num
6
是时候看看5到底是什么了:
>five
6
...这正是它应该有的样子。5个不错,但我比他更好。如果5仍然是一个对象,这意味着它仍然具有wtf属性,我愿意赌它没有的一切。
> five.wtf
undefined
啊哈!我是对的。我抓到他了!五现在是一个数字,不再是一个物体了。我知道乘法技巧这次救不了我。5 ++实际上是5 = 5 + 1。与乘法运算不同,++运算符将值赋给5。更具体地说,它将5 + 1的结果赋给它就像在乘法的情况下返回一个新的不可变数。
我知道他已经在我手上了,为了确保他逃不掉。我还有一个测试要做。如果我是对的,5现在真的是一个数字,那么这就行不通了:
> five.wtf = 'potato?'
'potato?'
这次他不会再骗我了。我知道土豆?会被打印到控制台因为那是赋值的输出。真正的问题是,wtf还会在那里吗?
> five.wtf
undefined
正如我怀疑的那样——什么都没有——因为数字不能被赋予属性。我们在学院的第一年就知道了;)
谢谢内森。多亏你有勇气问我这个问题,我终于可以把这一切抛在脑后,开始一个新的案子。
就像这个关于toValue()函数的例子。哦,天哪。人参公鸡!