为何 0.._
等于 undefinedgit
今天看文章 为何用「void 0」代替「undefined」 的时候,github
做者提到,用 void 0
替代 undefinded
的缘由其中有一点是前者更短,更省空间。bash
固然最主要的缘由仍是 undefinded 在局部做用域中能够被重写less
下面有人回复 0.._
长度更短,结果也是 undefinded
。 后面解释说是至关于 0['_']
,不过没有更深刻的讨论了。ide
当时心中产生了几个问题:工具
0.._
是如何隐式转换成 undefined
的0.._
的写法代替 void 0
0.._
的隐式转换对于10进制数字来讲,后面接 .
操做符,js 引擎并不知道该 .
是小数点仍是访问属性的 .
,所以有以下规定:性能
前面的数字为10进制,已带小数点的,则该 .
是访问属性,不然即为小数点; 若不是10进制,则 .
是访问属性测试
0.0._ // 输出 undefined 至关于 (0.0)._
0.._ // 至关于 (0.)._
00._ // 前面为 8进制
true._ // 输出 undefined
0._ // 语法错误 .后面应该接数字
'use strict';
00._ // Uncaught SyntaxError: Octal literals are not allowed in strict mode. 严格模式下不会解析成八进制
复制代码
注:以上是测试得出的结论,规范中没找到。ui
不过按编译原理的知识,引擎会先根据 词法解析-数值字面量 找到 0.
这个数值字面量词法,接着才进行语法分析编码
同时 附加语法-数值字面量 中提到非 strict 模式下 NumericLiteral 才容许 OctalIntegerLiteral 八进制的词法
接下来就是 为什么数值字面量可以进行属性访问 的问题了。这是一个左值表达式。
左值表达式语法
MemberExpression :
PrimaryExpression
FunctionExpression
MemberExpression [ Expression ]
MemberExpression . IdentifierName
new MemberExpression Arguments
复制代码
左值表达式-属性访问 有二者方式
前者等同于 MemberExpression [ <identifier-name-string> ]
<identifier-name-string>
是一个字符串字面量,它与 Unicode 编码后的 IdentifierName 包含相同的字符序列。
对于 MemberExpression [ Expression ]
表达式,其执行顺序以下:
以 0.._
为例,其等同于 0['_']
,即 MemberExpression = 0,Expression = '_'
,按如下步骤进行
即
Number { __proto__: Number, [[PrimitiveValue]]: 0}
Number { __proto__: Number, [[PrimitiveValue]]: 0}
,引用名称为 _
。在该基值(及原型链)中进行_
属性的寻找。最后没有找到,返回 undefined
其实关键的就是执行 CheckObjectCoercible(0)
的时候调用 ToObject
返回了一个临时包装对象
这点规范说的有点模糊,只说了 CheckObjectCoercible 在其参数没法用 ToObject 转换成对象的状况下抛出一个异常,可是没有说 baseValue 会进行 ToObject 转换。 在 JS的基本数据类型的临时包装类型对象的触发条件和生命周期是多久? - 貘吃馍香的回答 - 知乎 中有人进行了回答。
0.._
代替 void 0
咱们从 可读性、性能、正确性 三个方面分析
与 void 0
相比,0.._
仅减小了一个字符,可是该写法大大减低了可读性。
对于压缩工具来讲,不在意可读性,那么咱们从性能角度分析。
var COUNT = 100000000
var tmp
console.time("test1")
for(let i=0;i<COUNT;i++){
if(tmp === void 0){
}
}
console.timeEnd("test1")
// test1: 61.760986328125ms
console.time("test2")
for(let i=0;i<COUNT;i++){
if(tmp === 0.._){
}
}
console.timeEnd("test2")
// test2: 74.657958984375ms
复制代码
void 0
更快一点,但这个影响不大,单次指令之间的执行差别在微秒以内。
最后就看二者的值是否是正确的,即结果永远为 undefined
对于 void 0
,void 是关键字,不会被外部改变,所以返回值永远返回 undefined ,见 void 运算符
对于 0.._
,咱们上面分析到,在基值
中进行引用名称
的查找时,会往原型链中查找,所以改变 Number、Object 等的原型属性,0.._
值就不同了
console.log(0.._) // undefined
Object.prototype._ = 0
console.log(0.._) // 0
Number.prototype._ = 1
console.log(0.._) // 1
复制代码
能够看到, 0.._
结果不是固定的,所以不能用于替换 void 0
ps: 中文版翻译有些地方不够准确,能够先看中文版了解大概,再到原版中详细查看