爲何 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: 中文版翻譯有些地方不夠準確,能夠先看中文版瞭解大概,再到原版中詳細查看