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
的规定,这个表达式允许的成分如下:
UpdateExpression
LeftHandSideExpression
UnaryExpression
NewExpression
CallExpression
MemberExpression
PrimaryExpression
SuperProperty
MetaProperty
NewTarget
SuperCall
Arguments
ArgumentList
第一个不是错误,表达式可以相互嵌套,甚至嵌套自己。
这里面偏偏没有ArrayExpression
,所以在++
后面紧跟数组字面量是个语法错误,解释器在编译代码的阶段就会直接报错;而++[[]][0]
却是符合语法的。
++[[]][0]
不仅是符合语法的,而且在运行过程中恰好也没有碰到能抛出错误的情况,需要类型转换的地方全都由解释器隐式转换过去了,所以它可以顺利运行。
如果你对这部分感兴趣,可以去翻翻规范,我把链接贴在最后了。
所以,从本质上来说,这个问题的原因其实是,JavaScript的静态语义
和动态语义
的不同。
所谓静态语义,就是解释器编译代码时候使用的语义,动态语义就是代码运行时候的语义。
在最开始我提到了一句:++[]
报错符合直觉,而++[[]][0]
不报错违反了直觉。
其实我们再仔细想想就能明白,它们其实都是符合直觉的,不过并不是我们的直觉,而是解释器的直觉。
[]
是数组字面量,它毫无疑问的是常量,但是[[]][0]
呢?我们一眼就看出它也是常量,但是对于解释器却不是如此。我们所谓的一眼
其实已经对它进行了运算,求出了这个表达式的结果[]
,所以我们下意识的觉得它就是个常量,但是对于解释器却不是如此。在运行前的编译阶段,解释器只会对代码做词法和语法分析,而不会运行代码。此时[[]][0]
这个表达式对于解释器来说,其实是个黑箱,解释器并不知道它的结果是常量。因为数组内元素也可以是变量啊,那么解释器认为这里符合语法也就可以理解了。
规范:
Update Expression;
++运算符:Runtime Semantics。
这篇关于javascript - js类型转换问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!