javascript - js类型转换问题

查看:113
本文介绍了javascript - js类型转换问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问 题

偶然看到一篇博客,觉得挺有意思。
http://justjavac.com/javascri...
有个小小的问题没明白,++[[]][0],这个表达式等于1. 按博客上说的原理是取得数组的第0个元素,也就是空字符串"",然后用进行转换并自增变成了1.
我一步一步试过,[[]][0] 等于[]空数组。但是如果我直接 ++[]就会报错,这是为什么呢?

解决方案

简单提一下,你提到的那篇博客,以及那篇博客里面的文章,都说的似是而非,主要是因为它们是在解释++[[]][0],而并不是在拿它和++[]对比。

++符号必须跟着变量而不是常量,所以++[]报错了,这是符合直觉的;然而这并不能解释++[[]][0]为什么没有报错,毕竟[[]][0]这也是个常量啊。


说这是隐式类型转换的锅,其实不太准确,虽然这里涉及到了隐式类型转换,但它并不是最主要原因。
简单的来说,这是因为++[]是个语法错误,而++[[]][0]不是。

这里的所谓语法错误,不是运行时候才会报错的那种,而是在运行前的编译阶段就会抛出的错误。如果你仔细看看它的错误提示就会知道,这个错误虽然也是引用错误ReferenceError,但是提示却是Invalid left-hand side expression无效的左值表达式

为了清晰的表现他俩的结构,这里我使用uglify-js分别给它们俩构建语法树。
首先是++[[]][0]

ast = {
    type: "program",
    body: [{
        type:"ExpressionStatement",
        expression: {
            type: "UpdateExpression",
            operator: "++",
            argument: {
                type: "MemberExpression",
                object: {
                    type: "ArrayExpression",
                    elements: [{
                        type: "ArrayExpression",
                        elements: []
                    }]
                },
                property: {
                    type: "Literal",
                    value: 0
                }
            }
        }
    }]
}

然后是++[]

ast = {
    type: "program",
    body: [{
        type:"ExpressionStatement",
        expression: {
            type: "UpdateExpression",
            operator: "++",
            argument: {
                type: "ArrayExpression",
                elements: []
            }
        }
    }]
}

很明显可以看到,++运算符所在的表达式被称作UpdateExpression,对于剩下的部分,[[]][0]被称作MemberExpression,而[]被称做ArrayExpression,这是它们俩的主要区别。

那么我们再来看看对于UpdateExpression的规定,这个表达式允许的成分如下:

  1. UpdateExpression

  2. LeftHandSideExpression

  3. UnaryExpression

  4. NewExpression

  5. CallExpression

  6. MemberExpression

  7. PrimaryExpression

  8. SuperProperty

  9. MetaProperty

  10. NewTarget

  11. SuperCall

  12. Arguments

  13. ArgumentList

第一个不是错误,表达式可以相互嵌套,甚至嵌套自己。

这里面偏偏没有ArrayExpression,所以在++后面紧跟数组字面量是个语法错误,解释器在编译代码的阶段就会直接报错;而++[[]][0]却是符合语法的。


++[[]][0]不仅是符合语法的,而且在运行过程中恰好也没有碰到能抛出错误的情况,需要类型转换的地方全都由解释器隐式转换过去了,所以它可以顺利运行。

如果你对这部分感兴趣,可以去翻翻规范,我把链接贴在最后了。


所以,从本质上来说,这个问题的原因其实是,JavaScript的静态语义动态语义的不同。
所谓静态语义,就是解释器编译代码时候使用的语义,动态语义就是代码运行时候的语义。

在最开始我提到了一句:++[]报错符合直觉,而++[[]][0]不报错违反了直觉。
其实我们再仔细想想就能明白,它们其实都是符合直觉的,不过并不是我们的直觉,而是解释器的直觉。

[]是数组字面量,它毫无疑问的是常量,但是[[]][0]呢?我们一眼就看出它也是常量,但是对于解释器却不是如此。我们所谓的一眼其实已经对它进行了运算,求出了这个表达式的结果[],所以我们下意识的觉得它就是个常量,但是对于解释器却不是如此。在运行前的编译阶段,解释器只会对代码做词法和语法分析,而不会运行代码。此时[[]][0]这个表达式对于解释器来说,其实是个黑箱,解释器并不知道它的结果是常量。因为数组内元素也可以是变量啊,那么解释器认为这里符合语法也就可以理解了。


规范:
Update Expression
++运算符:Runtime Semantics

这篇关于javascript - js类型转换问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆