本文首發於個人我的網站blog.skyline.ink,歡迎各位大大訪問. 做者水平有限,文章僅供參考,不對的地方但願各位及時指正,共同進步,不勝感激javascript
對於前端程序員來講,JavaScript可謂灰常強,但此強非彼強。根據維基百科的闡釋,在計算機編程中,通俗地將語言分爲強類型和弱類型,雖然沒有精確的定義,可是強類型有很是嚴格的規則,包括變量定義時必須指定類型,使用時必須是指望的類型,不然報錯或拒絕編譯。維基百科上的類型強弱html
JavaScript並不須要在定義時指定變量類型,同時使用時,不是指望的值會自動轉換,弱類型相對於強類型來講類型檢查更不嚴格,故而引出了今天的問題,自動轉換的一些機制前端
數據類型 | 轉化成true | 轉化成false |
---|---|---|
String | 非空字符 | ""(空字符) |
Number | 非零 | 0與NaN |
Object | 非Null對象 | null |
undefined | 無 | undefined |
(注:調用Boolean()方法獲得結果相同)vue
對於多數狀況來講,對象隱式轉換成字符串或數字,其實調用了一個叫作ToPrimitive(obj,preferredType)的內部方法來幹這件事情,看了網上不少資料,都是balabala一大堆,規範裏面文字也很多,其實調用這個方法轉換的時候,除了date對象走轉換數字流程(即preferredType值是number),其餘走的都是轉字符流程(即preferredType值是string),大概流程以下:java
對象 | 調用toString() |
---|---|
普通對象 | "[object Object]" |
數組arr | arr.join() |
函數類 | 定義函數的代碼 |
日期類 | 可讀日期 |
正則對象 | 正則對象字面量的字符 |
值 | 轉化成字符串 | 轉化成數字 | 轉化成布爾值 |
---|---|---|---|
undefined | "undefined" | NaN | false |
null | "null" | 0 | false |
NaN | "NaN" | NaN | false |
[](空數組) | "" | 0 | true |
""(空字符串) | "" | 0 | false |
2 + "3"; // "23"
1 + 2 + "3"; // "33"
true + 2 + "3"; // "33"
1 + "2" + 3; // "123"
"2" + true; //"2true"
"2" + undefined; //"2undefined"
"2" + NaN //"2NaN"
'23' + {'a': 1} //"23[object Object]"
'23' + [1,3,{}, null, undefined, '', '2'] // "231,3,[object Object],,,,2"
[1,3,{}, null, undefined, '', '2'].toString() //"1,3,[object Object],,,,2"
23 + "1,3,[object Object],,,,2" //"231,3,[object Object],,,,2"
複製代碼
1 + [] //"1"
1 + [1] //"11"
1 + {a:'a'} //"1[object Object]"
null + null //0
true + {a:'a'} //"true[object Object]"
複製代碼
typeof NaN //"number"
null + undefined //NaN
1 + undefined //NaN
複製代碼
1 - '5' //-4
1 - [2, 2] //NaN
1 - {a:1} //NaN
1- undefined //NaN
1 - [] //1
1 - [2, 2] //NaN
1 - null //1
複製代碼
+ '3' // 數字3
- '-3' // 數字3
複製代碼
var x = NaN;
x === NaN; // false
undefined == "undefined" // false
null == "null" // false
null == 0 // false
null == false // false
undefined == 0 // false
undefined == false // false
複製代碼
如下內容純屬拓展,不感興趣的童鞋可忽略node
[] + {} //"[object Object]"
{} + [] // 0
[] + {} === {} + [] // true
{} + [] === [] + {} // true
{a: 1} // {a: 1}
{a: 1}; // 1
{'a': 1} // {a: 1}
{'a': 1}; // SyntaxError
{} + 0 + {}; // "0[object Object]"
{} + 0 + {} // "[object Object]0[object Object]"
複製代碼
[] + {} //"[object Object]"
{} + [] // 0
[] + {} === {} + [] // true
{} + [] === [] + {} // false
{a: 1} // 1
{a: 1}; // 1
{'a': 1} // SyntaxError
{'a': 1}; // SyntaxError
{} + 0 + {}; // "0[object Object]"
{} + 0 + {} // "0[object Object]"
複製代碼
MDN:label {a: 1}相關的幾個輸出裏,先忽略分號,{}分別被當作block和object literal,當被當作代碼塊時,入下所示:webpack
//{'a': 1}
{
"a": 1; // 語法錯誤
}
// {a: 1}
{
a:
1;
} // ^-- Automatic Semicolon Insertion
複製代碼
分號是否書寫在前端領域來講,這個問題如同vi、emacs編輯器之爭,最好計算機語言之爭通常。在知乎上看了JavaScript 語句後應該加分號麼?後,大體總結出來就是:git
說回ASI,程序員
官方規範中的ASIweb
官文太抽象,網上大神的翻譯大多數更加抽象,根據網上各類資料來看,總結用口水話說就是代碼塊最後一條語句自動插入,換行時候大多數在語句末尾自動插入,除了:
wikipedia: Abstract_syntax_tree
In computer science, an abstract syntax tree (AST), or just syntax tree, is a tree representation of the abstract syntactic structure of source code written in a programming language. Each node of the tree denotes a construct occurring in the source code. The syntax is "abstract" in not representing every detail appearing in the real syntax. For instance, grouping parentheses are implicit in the tree structure, and a syntactic construct like an if-condition-then expression may be denoted by means of a single node with three branches.
其實就是將源代碼分析成所對應的樹狀結構,便於以後的語法分析,代碼檢查等。如今的不少熱門工具如webpack、vue、UglifyJS、Lint等都會用到這個技術,各個瀏覽器引擎也會使用自家定義的一套語法書生成樹規範,生成相應的語法樹。
其實因爲瀏覽器廠商衆多,每一個與解析狀況不一致,日常代碼中基本不會遇到{}+這種問題,咱們也沒有精力研究各廠商預解析源碼,從Chrome和Firefox來看,總結出來有下面幾點:
大概在chrome版本49以前,Chrome控制檯上面的輸出結果基本和Firefox一致,以後在chrome上有人提出bug,Issue 499864,大概意思就是說我在控制檯輸入{a: 4, b: 5}
你給我報個錯幹嗎,我就是想要一個對象而已。Chrome確實該近幾年大火,沒過多久就修復了,修復的方式也特別666,就是凡是語句以{開頭,以}結尾,我解析的時候就包裹一層括號在外面。git記錄,裏面的關鍵代碼以下:
+ if (/^\s*\{/.test(text) && /\}\s*$/.test(text))
+ text = '(' + text + ')';
複製代碼
也就是說{} + 0 + {}實際上是({} + 0 + {}), {a: 1}實際上是({a: 1}),也就是說在Chrome中,凡是語句以{開頭,
以{} + 0 + {}爲例來看
此時,Chrome將第一個{}解析成對象
此時,firefox將第一個{}解析成代碼塊
分析以後不可貴出如上的結果
《JavaScript高級程序設計》