这是有效的,并返回JavaScript中的字符串 "10" (more examples here):
"10"
console.log(++[[]][+[]]+[+[]])
为什么?这里发生了什么?
这个评估相同但略小
+!![]+''+(+[])
[] - 是一个转换后的数组,当你加或减时它转换为0,因此[] = 0
![] - 计算结果为false,因此!! []计算结果为true
!! [] - 将true转换为计算结果为true的数值,因此在本例中为1
'' - 在表达式中附加一个空字符串,使数字转换为字符串
[] - 计算结果为0
所以评估为
+(true) + '' + (0) 1 + '' + 0 "10"
所以现在你知道了,试试这个:
_=$=+[],++_+''+$
也许用最简单的方法将表达式评估为“10”而不是数字是:
+!+[] + [+[]] // "10"
+!+[] + [+[]]
-~[] + [+[]] // "10"
-~[] + [+[]]
// ==========说明========== \
+!+[] : +[] 转换为0. !0 转换为 true . +true 转换为1. -~[] = -(-1) ,即1
+!+[]
+[]
!0
true
+true
-~[]
-(-1)
[+[]] : +[] 转换为0. [0] 是一个包含单个元素0的数组 .
[+[]]
[0]
然后JS评估 1 + [0] ,因此 Number + Array 表达式 . 然后ECMA规范工作: + 运算符通过调用基础 Object 原型中的 toString()/valueOf() 函数将两个操作数转换为字符串 . 如果表达式的两个操作数仅为数字,则它作为附加函数运行 . 诀窍是数组很容易将它们的元素转换为连接的字符串表示 .
1 + [0]
Number + Array
+
Object
toString()/valueOf()
一些例子:
1 + {} // "1[object Object]" 1 + [] // "1" 1 + new Date() // "1Wed Jun 19 2013 12:13:25 GMT+0400 (Caucasus Standard Time)"
有一个很好的例外,在 NaN 中有两个 Objects 加法结果:
NaN
Objects
[] + [] // "" [1] + [2] // "12" {} + {} // NaN {a:1} + {b:2} // NaN [1, {}] + [2, {}] // "1,[object Object]2,[object Object]"
让我们简单一点:
++[[]][+[]]+[+[]] = "10" var a = [[]][+[]]; var b = [+[]]; // so a == [] and b == [0] ++a; // then a == 1 and b is still that array [0] // when you sum the var a and an array, it will sum b as a string just like that: 1 + "0" = "10"
[]求值为0 [...]然后对任何事物求和(操作)将数组内容转换为由逗号连接的元素组成的字符串表示 .
任何其他类似于获取数组的索引(具有比操作更重要的优先级)是有序的并且没有什么有趣的 .
如果我们把它分开,这个混乱就等于:
++[[]][+[]] + [+[]]
在JavaScript中, +[] === 0 确实如此 . + 将某些内容转换为数字,在这种情况下,它将归结为 +"" 或 0 (请参阅下面的规范详情) .
+[] === 0
+""
0
因此,我们可以简化它( ++ 优先于 + ):
++
++[[]][0] + [0]
因为 [[]][0] 表示:从 [[]] 获取第一个元素,所以:
[[]][0]
[[]]
[[]][0] 返回内部数组( [] ) . 由于引用说 [[]][0] === [] 是错误的,但让我们调用内部数组 A 来避免错误的表示法 .
[]
[[]][0] === []
A
++[[]][0] == A + 1 ,因为 ++ 表示'increment by one' .
++[[]][0] == A + 1
++[[]][0] === +(A + 1) ;换句话说,它总是一个数字( +1 不一定返回一个数字,而 ++ 总是这样 - 感谢Tim Down指出这一点) .
++[[]][0] === +(A + 1)
+1
同样,我们可以将混乱简化为更清晰的内容 . 让 [] 代替 A :
+([] + 1) + [0]
在JavaScript中,这也是如此: [] + 1 === "1" ,因为 [] == "" (加入一个空数组),所以:
[] + 1 === "1"
[] == ""
+([] + 1) === +("" + 1) ,和
+([] + 1) === +("" + 1)
+("" + 1) === +("1") ,和
+("" + 1) === +("1")
+("1") === 1
让我们进一步简化它:
此外,在JavaScript中也是如此: [0] == "0" ,因为它正在使用一个元素连接数组 . 连接将连接由 , 分隔的元素 . 使用一个元素,您可以推断出该逻辑将导致第一个元素本身 .
[0] == "0"
,
所以,最后我们得到(number string = string):
1 + "0" === "10" // Yay!
+[] 的规格详情:
这是一个非常迷宫,但要做 +[] ,首先它被转换为字符串,因为这是 + 所说的:
11.4.6一元运算符一元运算符将其操作数转换为数字类型 . 生产环境 UnaryExpression:UnaryExpression的计算方法如下:设expr是评估UnaryExpression的结果 . 返回ToNumber(GetValue(expr)) .
ToNumber() 说:
ToNumber()
对象应用以下步骤:让primValue为ToPrimitive(输入参数,提示字符串) . 返回ToString(primValue) .
ToPrimitive() 说:
ToPrimitive()
Object返回Object的默认值 . 通过调用对象的[[DefaultValue]]内部方法,传递可选提示PreferredType来检索对象的默认值 . 对于8.12.8中的所有本机ECMAScript对象,此规范定义了[[DefaultValue]]内部方法的行为 .
[[DefaultValue]] 说:
[[DefaultValue]]
8.12.8 [[DefaultValue]](提示)当使用提示字符串调用O的[[DefaultValue]]内部方法时,执行以下步骤:设toString为调用[[Get]]内部方法的结果对象O的参数为“toString” . 如果IsCallable(toString)为真,则a . 令str为调用toString的[[Call]]内部方法的结果,其中O为此值,且为空参数列表 . 湾如果str是原始值,则返回str .
数组的 .toString 说:
.toString
15.4.4.2 Array.prototype.toString()调用toString方法时,执行以下步骤:array是在此值上调用ToObject的结果 . 让func成为使用参数“join”调用数组的[[Get]]内部方法的结果 . 如果IsCallable(func)为false,则让func成为标准的内置方法Object.prototype.toString(15.2.4.2) . 返回调用func提供数组的[[Call]]内部方法的结果作为此值和空参数列表 .
所以 +[] 归结为 +"" ,因为 [].join() === "" .
[].join() === ""
同样, + 定义为:
ToNumber 的定义为 "" :
ToNumber
""
StringNumericLiteral ::: [empty]的MV为0 .
所以 +"" === 0 ,因而 +[] === 0 .
+"" === 0
++[[]][+[]] => 1 // [+[]] = [0], ++0 = 1 [+[]] => [0]
然后我们有一个字符串连接
1+[0].toString() = 10
以下内容改编自blog post,回答了我在此问题仍未结束时发布的问题 . 链接是ECMAScript 3规范的(HTML副本),仍然是当今常用Web浏览器中JavaScript的基线 .
首先,评论:这种表达式永远不会出现在任何(理智的) 生产环境 环境中,并且只是作为一种练习用于读者如何知道JavaScript的脏边缘 . JavaScript运算符在类型之间隐式转换的一般原则是有用的,一些常见的转换也是如此,但在这种情况下的大部分细节都不是 .
表达式 ++[[]][+[]]+[+[]] 最初可能看起来相当气势和模糊,但实际上相对容易分解为单独的表达式 . 下面我简单地添加了括号以便清楚;我可以向你保证他们什么都不会改变,但是如果你想验证那么你可以随意阅读grouping operator . 因此,表达式可以更清楚地写成
++[[]][+[]]+[+[]]
( ++[[]][+[]] ) + ( [+[]] )
打破这一点,我们可以通过观察 +[] 评估为 0 来简化 . 为了满足自己为什么这是真的,请查看unary + operator并按照稍微曲折的路径,最后将ToPrimitive空数组转换为空字符串,最后由ToNumber转换为 0 . 我们现在可以为 +[] 的每个实例替换 0 :
( ++[[]][0] ) + [0]
已经更简单了 . 至于 ++[[]][0] ,这是prefix increment operator( ++ )的组合,array literal定义了一个单元素的数组,该数组本身是一个空数组( [[]] )和一个property accessor( [0] ),在数组文字定义的数组上调用 .
++[[]][0]
那么,我们可以将 [[]][0] 简化为 [] ,我们有 ++[] ,对吗?事实上,情况并非如此,因为评估 ++[] 会抛出错误,这可能最初看起来令人困惑 . 但是,对 ++ 的性质进行一点考虑就会明确:它用于增加变量(例如 ++i )或对象属性(例如 ++obj.count ) . 它不仅评估值,还将值存储在某个地方 . 在 ++[] 的情况下,它无处放置新值(无论它是什么),因为没有对要更新的对象属性或变量的引用 . 在规范方面,这由内部PutValue操作覆盖,该操作由前缀增量运算符调用 .
++[]
++i
++obj.count
那么, ++[[]][0] 做了什么?那么,通过与 +[] 类似的逻辑,内部数组被转换为 0 ,并且该值递增 1 以给出最终值 1 . 外部数组中的属性 0 的值更新为 1 ,整个表达式的计算结果为 1 .
1
这让我们失望了
...这是addition operator的简单使用 . 两个操作数首先是converted to primitives,如果任一原始值是字符串,则执行字符串连接,否则执行数字加法 . [0] 转换为 "0" ,因此使用字符串连接,生成 "10" .
"0"
作为最后的一点,可能不会立即显而易见的是,覆盖 toString() 的 toString() 或 valueOf() 方法中的任何一个都将改变表达式的结果,因为在将对象转换为原始值时,如果存在则两者都被检查和使用 . 例如,以下内容
toString()
valueOf()
Array.prototype.toString = function() { return "foo"; }; ++[[]][+[]]+[+[]]
...产生 "NaNfoo" . 为什么会发生这种情况留给读者练习...
"NaNfoo"
一元加上给定的字符串转换为数字
递增运算符给定字符串转换并递增1
[] =='' . 空字符串
''或[]评估0 .
++[[]][+[]]+[+[]] = 10 ++[''][0] + [0] : First part is gives zeroth element of the array which is empty string 1+0 10
8 回答
这个评估相同但略小
[] - 是一个转换后的数组,当你加或减时它转换为0,因此[] = 0
![] - 计算结果为false,因此!! []计算结果为true
!! [] - 将true转换为计算结果为true的数值,因此在本例中为1
'' - 在表达式中附加一个空字符串,使数字转换为字符串
[] - 计算结果为0
所以评估为
所以现在你知道了,试试这个:
也许用最简单的方法将表达式评估为“10”而不是数字是:
+!+[] + [+[]]
// "10"-~[] + [+[]]
// "10"// ==========说明========== \
+!+[]
:+[]
转换为0.!0
转换为true
.+true
转换为1.-~[]
=-(-1)
,即1[+[]]
:+[]
转换为0.[0]
是一个包含单个元素0的数组 .然后JS评估
1 + [0]
,因此Number + Array
表达式 . 然后ECMA规范工作:+
运算符通过调用基础Object
原型中的toString()/valueOf()
函数将两个操作数转换为字符串 . 如果表达式的两个操作数仅为数字,则它作为附加函数运行 . 诀窍是数组很容易将它们的元素转换为连接的字符串表示 .一些例子:
有一个很好的例外,在
NaN
中有两个Objects
加法结果:让我们简单一点:
[]求值为0 [...]然后对任何事物求和(操作)将数组内容转换为由逗号连接的元素组成的字符串表示 .
任何其他类似于获取数组的索引(具有比操作更重要的优先级)是有序的并且没有什么有趣的 .
如果我们把它分开,这个混乱就等于:
在JavaScript中,
+[] === 0
确实如此 .+
将某些内容转换为数字,在这种情况下,它将归结为+""
或0
(请参阅下面的规范详情) .因此,我们可以简化它(
++
优先于+
):因为
[[]][0]
表示:从[[]]
获取第一个元素,所以:[[]][0]
返回内部数组([]
) . 由于引用说[[]][0] === []
是错误的,但让我们调用内部数组A
来避免错误的表示法 .++[[]][0] == A + 1
,因为++
表示'increment by one' .++[[]][0] === +(A + 1)
;换句话说,它总是一个数字(+1
不一定返回一个数字,而++
总是这样 - 感谢Tim Down指出这一点) .同样,我们可以将混乱简化为更清晰的内容 . 让
[]
代替A
:在JavaScript中,这也是如此:
[] + 1 === "1"
,因为[] == ""
(加入一个空数组),所以:+([] + 1) === +("" + 1)
,和+("" + 1) === +("1")
,和+("1") === 1
让我们进一步简化它:
此外,在JavaScript中也是如此:
[0] == "0"
,因为它正在使用一个元素连接数组 . 连接将连接由,
分隔的元素 . 使用一个元素,您可以推断出该逻辑将导致第一个元素本身 .所以,最后我们得到(number string = string):
+[]
的规格详情:这是一个非常迷宫,但要做
+[]
,首先它被转换为字符串,因为这是+
所说的:ToNumber()
说:ToPrimitive()
说:[[DefaultValue]]
说:数组的
.toString
说:所以
+[]
归结为+""
,因为[].join() === ""
.同样,
+
定义为:ToNumber
的定义为""
:所以
+"" === 0
,因而+[] === 0
.然后我们有一个字符串连接
以下内容改编自blog post,回答了我在此问题仍未结束时发布的问题 . 链接是ECMAScript 3规范的(HTML副本),仍然是当今常用Web浏览器中JavaScript的基线 .
首先,评论:这种表达式永远不会出现在任何(理智的) 生产环境 环境中,并且只是作为一种练习用于读者如何知道JavaScript的脏边缘 . JavaScript运算符在类型之间隐式转换的一般原则是有用的,一些常见的转换也是如此,但在这种情况下的大部分细节都不是 .
表达式
++[[]][+[]]+[+[]]
最初可能看起来相当气势和模糊,但实际上相对容易分解为单独的表达式 . 下面我简单地添加了括号以便清楚;我可以向你保证他们什么都不会改变,但是如果你想验证那么你可以随意阅读grouping operator . 因此,表达式可以更清楚地写成打破这一点,我们可以通过观察
+[]
评估为0
来简化 . 为了满足自己为什么这是真的,请查看unary + operator并按照稍微曲折的路径,最后将ToPrimitive空数组转换为空字符串,最后由ToNumber转换为0
. 我们现在可以为+[]
的每个实例替换0
:已经更简单了 . 至于
++[[]][0]
,这是prefix increment operator(++
)的组合,array literal定义了一个单元素的数组,该数组本身是一个空数组([[]]
)和一个property accessor([0]
),在数组文字定义的数组上调用 .那么,我们可以将
[[]][0]
简化为[]
,我们有++[]
,对吗?事实上,情况并非如此,因为评估++[]
会抛出错误,这可能最初看起来令人困惑 . 但是,对++
的性质进行一点考虑就会明确:它用于增加变量(例如++i
)或对象属性(例如++obj.count
) . 它不仅评估值,还将值存储在某个地方 . 在++[]
的情况下,它无处放置新值(无论它是什么),因为没有对要更新的对象属性或变量的引用 . 在规范方面,这由内部PutValue操作覆盖,该操作由前缀增量运算符调用 .那么,
++[[]][0]
做了什么?那么,通过与+[]
类似的逻辑,内部数组被转换为0
,并且该值递增1
以给出最终值1
. 外部数组中的属性0
的值更新为1
,整个表达式的计算结果为1
.这让我们失望了
...这是addition operator的简单使用 . 两个操作数首先是converted to primitives,如果任一原始值是字符串,则执行字符串连接,否则执行数字加法 .
[0]
转换为"0"
,因此使用字符串连接,生成"10"
.作为最后的一点,可能不会立即显而易见的是,覆盖
toString()
的toString()
或valueOf()
方法中的任何一个都将改变表达式的结果,因为在将对象转换为原始值时,如果存在则两者都被检查和使用 . 例如,以下内容...产生
"NaNfoo"
. 为什么会发生这种情况留给读者练习...一元加上给定的字符串转换为数字
递增运算符给定字符串转换并递增1
[] =='' . 空字符串
''或[]评估0 .