今天,我们将专门发表一篇文章来介绍那些奇怪的JavaScript时刻,在这些时刻,事情表现得有点奇怪。
“在这个世界上,没有一个正常人能完成任何有意义的事情。”--乔纳森,“奇怪的事情”
我们将查看一些结果令人惊讶的代码片段,并对正在发生的事情进行解释,以便更好地理解我们喜爱的编程语言。虽然它是个怪人,但我们喜欢它!
场景#1:[‘1’,‘7’,‘11’].map(ParseInt)。
乍一看,这可能看起来很奇怪,但它实际上有一个优雅的解释。要了解发生了什么,我们需要了解涉及到的两个函数:map和parseInt。
map()按顺序为数组中的每个元素调用一次提供的回调函数,并根据结果构造一个新的数组。回调函数仅对已赋值(包括未定义)的数组的索引调用。
现在,上面引用的回调函数将接收一些特定的参数,让我们以它的输出为例:
[1,2,3]。地图(控制台。日志)1 1 0&>;(3)[1,2,3]1 2 1&>;(3)[1,2,3]1 3 2&>;(3)[1,2,3]
可以看到,map函数在每次迭代中不仅传递了项的值,还传递了索引和完整数组的副本。这很重要,也是影响我们之前结果的部分原因。
函数的作用是:解析字符串参数,并返回指定基数的整数(数学数制中的基数)。
因此,根据定义,parseInt(string[,radix])需要两个参数,我们要解析的字符串和基数。
现在我们已经对这两个函数有了足够的了解,让我们试着了解一下我们的例子中发生了什么,我们将从我们的原始脚本开始,我们将逐步解释它:
正如我们所知,map函数的回调将接收3个参数,因此让我们这样做:
开始了解发生了什么事了吗?当我们添加参数时,可以清楚地看到parseInt函数正在接收额外的参数,而不仅仅是数组中项的实际值,所以现在我们可以测试函数将对这些值组合执行什么操作,但我们也可以忽略数组参数,因为它将被parseInt函数丢弃:
现在,这解释了我们最初看到的值,parseInt函数结果被redex参数更改,该参数确定转换的基数。
现在了解了它的工作原理,我们可以轻松地修复我们的脚本并获得所需的结果:
场景#2:(‘B';+';a';++’a‘+’a‘).toLowerCase()=’香蕉‘。
您可能认为上面的表达式是假的,毕竟,我们在表达式左侧构建的字符串中没有字母‘n’,不是吗?让我们来了解一下:
好的,您可能已经意识到发生了什么,但如果没有,让我在这里快速解释一下。让我们把注意力集中在表达式的左边,右边没有什么奇怪的,相信我。
有趣的是,我们正在形成单词“banana”,所以问题似乎在这里,让我们去掉小写转换,看看会发生什么:
对啰!。现在我们找到了一些‘N’,看起来我们实际上在字符串中找到了一个NaN,也许它来自++表达式,让我们假装一下,看看我们会得到什么:
不太好,我们有多余的a,所以我们试试别的吧:
啊,我们走吧,…。++操作本身并不求值,但是当我们在末尾添加字符‘a’时,它全部进入NaN,现在适合我们的代码。然后,NaN表达式作为字符串与文本的其余部分连接起来,我们最终得到了香蕉。很奇怪!
(![]+[])[+[]]+(![]+[])[+!+[]]+([![]]+[][]])[++[]+[+[]+(![]+[])[!+[]+!+[]]=';失败';
这个世界是怎么回事?一串括号是如何组成“失败”这个词的呢?相信我,JS没有失败,我们实际上是让字符串FAIL作为输出。
让我们试着解释一下,在这一串中有几个东西形成了一个模式:
该模式的计算结果为字符串false,这很奇怪,但它是该语言的属性,结果是false+[]=';false';,此转换与JS如何在内部映射内部调用有关,我们不会详细说明为什么会发生这种情况。
一旦您形成了字符串false,其余的就很容易了,只需查找您需要的字母的位置,除了字母i,它不是单词false的一部分。
由于原始表达式稍有更改,让我们看一下它([![]]+[][[]]),它的计算结果是字符串false_unfined。所以基本上我们强制一个未定义的值,并将其连接到我们知道如何获得的假字符串,剩下的就是历史了。
是真是真,这是问题;是假是假,这是问题。
JavaScript中的每个值都作为自己的布尔值(TRUTY/FALSE),这些值用于需要布尔值但没有给出布尔值的操作中。很可能你至少做过一次这样的事情:
在上面的代码中,即使值为“true”,array也不是布尔值,并且表达式将导致执行下面的console.log。
凡是不假的都是真的。糟糕的解释?很公平,让我们进一步研究一下。
JS中的一些东西很奇怪,这是语言设计的方式,我们接受它的方式。让我们看看一些奇怪的数组等式:
[]==';';//->;true[]==0//-&>;true[';';]=';';//->;true[0]==0//->;true[0]==';';//->;false[';';]==0//->;TRUE[NULL]==';';//TRUE[NULL]==0//TRUE[UNDEFINED]==';';//TRUE[UNDEFINED]==0//TRUE[[]]==0//TRUE[[]]==';';//TRUE[]==';';//TRUE[]==0//TRUE[NULL]==0//TRUE[NULL]==';';//TRUE[UNDEFINTED]==0//TRUE[UNDEFINTED]=';';//TRUE。
如果你对为什么感兴趣的话?您可以在规范的第7.2.13节“抽象平等比较”中阅读。虽然我要警告你,这不是给普通人看的。
在我们的现实世界中,我们知道数学就是数学,我们知道它是如何工作的,我们从小就被教导如何加法,而且总是这样,如果你把相同的数字相加,你就会得到结果,对吗?井…。对于javascript,这并不总是正确的,…。或者是一种…。让我们看看:
3-1//->;2 3+1//->;4';3';-1//->;2';3';+1//->;';31';';';';';';';';[]+[]//->;';';{}+[]//->;0[]+{}//->;';[Object Object]';{}+{}//-&>;';[Object Object][Object Object]';';222';--';111';//->;333[4]*[4]//->;16[]*[]//->;0[4,4]*[4,4]//NaN。
当我们减法时,字符串和数字相互作用,但在加法过程中,两者都充当字符串,这是为什么呢?井…。它就是这样设计的,但是有一个简单的表格可以帮助您理解JavaScript在每种情况下会做什么:
数字+数字->;添加布尔+数字->;添加布尔+布尔->;添加编号+字符串->;连接字符串+布尔->;连接字符串+字符串->;连接
其他的例子呢?在加法之前,正在隐式调用A、ToPrimitive和ToString两个方法,分别用于[]和{}。阅读规范中有关评估流程的更多信息:
值得注意的是,这里的{}+[]是个例外。它与[]+{}不同的原因是,没有括号,它被解释为代码块,然后是一元+,将[]转换为数字。它看到以下内容:
要获得与[]+{}相同的输出,我们可以将其括在括号中。
我希望你喜欢这篇文章,就像我喜欢写它一样。JavaScript是一种令人惊叹的语言,充满了技巧和怪诞,我希望本文能让您清楚地了解其中一些有趣的主题,下次您遇到类似的事情时,您就会知道到底发生了什么。
在更多的情况下,JS可能会变得非常奇怪,如果你们都喜欢的话,我将来可能会做更多这样的帖子。
如果你喜欢这篇文章,并且想支持我的工作:那就给我买瓶啤酒吧