首页 文章

为什么[[]] [[]] [[]]返回字符串“10”?

提问于
浏览
1497

这是有效的,并返回JavaScript中的字符串 "10"more examples here):

console.log(++[[]][+[]]+[+[]])

为什么?这里发生了什么?

8 回答

  • 4

    这个评估相同但略小

    +!![]+''+(+[])
    
    • [] - 是一个转换后的数组,当你加或减时它转换为0,因此[] = 0

    • ![] - 计算结果为false,因此!! []计算结果为true

    • !! [] - 将true转换为计算结果为true的数值,因此在本例中为1

    • '' - 在表达式中附加一个空字符串,使数字转换为字符串

    • [] - 计算结果为0

    所以评估为

    +(true) + '' + (0)
    1 + '' + 0
    "10"
    

    所以现在你知道了,试试这个:

    _=$=+[],++_+''+$
    
  • 21

    也许用最简单的方法将表达式评估为“10”而不是数字是:

    +!+[] + [+[]] // "10"

    -~[] + [+[]] // "10"

    // ==========说明========== \

    +!+[]+[] 转换为0. !0 转换为 true . +true 转换为1. -~[] = -(-1) ,即1

    [+[]]+[] 转换为0. [0] 是一个包含单个元素0的数组 .

    然后JS评估 1 + [0] ,因此 Number + Array 表达式 . 然后ECMA规范工作: + 运算符通过调用基础 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 加法结果:

    [] + []   //    ""
    [1] + [2] //    "12"
    {} + {}   //    NaN
    {a:1} + {b:2}     //    NaN
    [1, {}] + [2, {}] //    "1,[object Object]2,[object Object]"
    
  • 1

    让我们简单一点:

    ++[[]][+[]]+[+[]] = "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"
    
  • 13

    []求值为0 [...]然后对任何事物求和(操作)将数组内容转换为由逗号连接的元素组成的字符串表示 .

    任何其他类似于获取数组的索引(具有比操作更重要的优先级)是有序的并且没有什么有趣的 .

  • 1927

    如果我们把它分开,这个混乱就等于:

    ++[[]][+[]]
    +
    [+[]]
    

    在JavaScript中, +[] === 0 确实如此 . + 将某些内容转换为数字,在这种情况下,它将归结为 +""0 (请参阅下面的规范详情) .

    因此,我们可以简化它( ++ 优先于 + ):

    ++[[]][0]
    +
    [0]
    

    因为 [[]][0] 表示:从 [[]] 获取第一个元素,所以:

    • [[]][0] 返回内部数组( [] ) . 由于引用说 [[]][0] === [] 是错误的,但让我们调用内部数组 A 来避免错误的表示法 .

    • ++[[]][0] == A + 1 ,因为 ++ 表示'increment by one' .

    • ++[[]][0] === +(A + 1) ;换句话说,它总是一个数字( +1 不一定返回一个数字,而 ++ 总是这样 - 感谢Tim Down指出这一点) .

    同样,我们可以将混乱简化为更清晰的内容 . 让 [] 代替 A

    +([] + 1)
    +
    [0]
    

    在JavaScript中,这也是如此: [] + 1 === "1" ,因为 [] == "" (加入一个空数组),所以:

    • +([] + 1) === +("" + 1) ,和

    • +("" + 1) === +("1") ,和

    • +("1") === 1

    让我们进一步简化它:

    1
    +
    [0]
    

    此外,在JavaScript中也是如此: [0] == "0" ,因为它正在使用一个元素连接数组 . 连接将连接由 , 分隔的元素 . 使用一个元素,您可以推断出该逻辑将导致第一个元素本身 .

    所以,最后我们得到(number string = string):

    1
    +
    "0"
    
    === "10" // Yay!
    

    +[] 的规格详情:

    这是一个非常迷宫,但要做 +[] ,首先它被转换为字符串,因为这是 + 所说的:

    11.4.6一元运算符一元运算符将其操作数转换为数字类型 . 生产环境 UnaryExpression:UnaryExpression的计算方法如下:设expr是评估UnaryExpression的结果 . 返回ToNumber(GetValue(expr)) .

    ToNumber() 说:

    对象应用以下步骤:让primValue为ToPrimitive(输入参数,提示字符串) . 返回ToString(primValue) .

    ToPrimitive() 说:

    Object返回Object的默认值 . 通过调用对象的[[DefaultValue]]内部方法,传递可选提示PreferredType来检索对象的默认值 . 对于8.12.8中的所有本机ECMAScript对象,此规范定义了[[DefaultValue]]内部方法的行为 .

    [[DefaultValue]] 说:

    8.12.8 [[DefaultValue]](提示)当使用提示字符串调用O的[[DefaultValue]]内部方法时,执行以下步骤:设toString为调用[[Get]]内部方法的结果对象O的参数为“toString” . 如果IsCallable(toString)为真,则a . 令str为调用toString的[[Call]]内部方法的结果,其中O为此值,且为空参数列表 . 湾如果str是原始值,则返回str .

    数组的 .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() === "" .

    同样, + 定义为:

    11.4.6一元运算符一元运算符将其操作数转换为数字类型 . 生产环境 UnaryExpression:UnaryExpression的计算方法如下:设expr是评估UnaryExpression的结果 . 返回ToNumber(GetValue(expr)) .

    ToNumber 的定义为 ""

    StringNumericLiteral ::: [empty]的MV为0 .

    所以 +"" === 0 ,因而 +[] === 0 .

  • 109
    ++[[]][+[]] => 1 // [+[]] = [0], ++0 = 1
    [+[]] => [0]
    

    然后我们有一个字符串连接

    1+[0].toString() = 10
    
  • 7

    以下内容改编自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] 简化为 [] ,我们有 ++[] ,对吗?事实上,情况并非如此,因为评估 ++[] 会抛出错误,这可能最初看起来令人困惑 . 但是,对 ++ 的性质进行一点考虑就会明确:它用于增加变量(例如 ++i )或对象属性(例如 ++obj.count ) . 它不仅评估值,还将值存储在某个地方 . 在 ++[] 的情况下,它无处放置新值(无论它是什么),因为没有对要更新的对象属性或变量的引用 . 在规范方面,这由内部PutValue操作覆盖,该操作由前缀增量运算符调用 .

    那么, ++[[]][0] 做了什么?那么,通过与 +[] 类似的逻辑,内部数组被转换为 0 ,并且该值递增 1 以给出最终值 1 . 外部数组中的属性 0 的值更新为 1 ,整个表达式的计算结果为 1 .

    这让我们失望了

    1 + [0]
    

    ...这是addition operator的简单使用 . 两个操作数首先是converted to primitives,如果任一原始值是字符串,则执行字符串连接,否则执行数字加法 . [0] 转换为 "0" ,因此使用字符串连接,生成 "10" .

    作为最后的一点,可能不会立即显而易见的是,覆盖 toString()toString()valueOf() 方法中的任何一个都将改变表达式的结果,因为在将对象转换为原始值时,如果存在则两者都被检查和使用 . 例如,以下内容

    Array.prototype.toString = function() {
      return "foo";
    };
    ++[[]][+[]]+[+[]]
    

    ...产生 "NaNfoo" . 为什么会发生这种情况留给读者练习...

  • 58
    • 一元加上给定的字符串转换为数字

    • 递增运算符给定字符串转换并递增1

    • [] =='' . 空字符串

    • ''或[]评估0 .

    ++[[]][+[]]+[+[]] = 10 
    ++[''][0] + [0] : First part is gives zeroth element of the array which is empty string 
    1+0 
    10
    

相关问题