JavaScript 轉義字符JSON parse錯誤研究
JSON 字符串轉換為 JavaScript 對象
JSON.parse 將一個 JSON 字符串轉換為 JavaScript 對象。
JSON.parse('{"hello":"\world"}')
以上代碼輸出:
{
hello: "world"
}
是一個 JavaScript 對象,但是仔細觀察會發(fā)現,"\world" 變成了 "world"。
那么我們繼續(xù)運行如下代碼:
JSON.parse('{"hello":"\\world"}')
出拋出異常:
VM376:1 Uncaught SyntaxError: Unexpected token w in JSON at position 11
at JSON.parse (<anonymous>)
at <anonymous>:1:6
Unexpected token w。
好奇心不死,繼續(xù)試,3 個反斜杠:
JSON.parse('{"hello":"\\\world"}')
結果是:
VM16590:1 Uncaught SyntaxError: Unexpected token w in JSON at position 11
at JSON.parse (<anonymous>)
at <anonymous>:1:6
繼續(xù),4 個反斜杠:
JSON.parse('{"hello":"\\\\world"}')
結果正常:
{
hello: "\world"
}
- 1個,"world"
- 2個,Error
- 3個,Error
- 4個,"\world"
- 5個,"\world"
- 6個,Error
- 7個,Error
- 8個,"\\world"
- 。。。
我們換個思路,把 JSON.parse 去掉,只輸出 JavaScript 字符串:
> 'hello' "hello" > '\hello' "hello" > '\\hello' "\hello" > '\\\hello' "\hello" > '\\\\hello' "\\hello"
問題大概找到了。
把上面的規(guī)則帶入到之前的 JSON.parse 代碼,問題就解決了。
我們看看 JSON 的字符串解析規(guī)則:
根據這個規(guī)則,我們解析一下 "\hello",第 1 個字符是反斜杠(\),所以在引號后面走最下面的分支(紅線標注):
第 2 個字符是 h,但是反斜杠后面只有 9 條路,這個不屬于任何一條路,所以這個是個非法字符。
不只是 JSON,在很多語言中都會拋出類似 Error:(7, 27) Illegal escape: '\h' 的錯誤。
但是不知道為什么 JavaScript 偏偏可以解析這個非法轉義字符,而解決方式也很暴力:直接忽略。
在 es 規(guī)范我沒有找到具體的章節(jié)。去看看 V8 是怎么解析的吧。
引擎讀取 JavaScript 源碼后首先進行詞法分析,文件 /src/parsing/scanner.cc 的功能是讀取源碼并解析(當前最新版 6.4.286)。
找到 Scanner::Scan() 函數關鍵代碼:
case '"': case '\'': token = ScanString(); break;
是一個很長的 switch 語句:如果遇到雙引號(")、單引號(')則調用 ScanString() 函數。
簡單解釋下:以上代碼是 C++ 代碼,在 C++ 中單引號是字符,雙引號是字符串。所以表示字符時,雙引號不需要轉義,但是單引號需要轉義;而表示字符串時,正好相反。此處的 C++ 轉義并不是我們今天要研究的轉義。
ScanString() 函數
在 ScanString() 函數中我們也只看重點代碼:
while (c0_ != quote && c0_ != kEndOfInput && !IsLineTerminator(c0_)) { uc32 c = c0_; Advance(); if (c == '\\') { if (c0_ == kEndOfInput || !ScanEscape<false, false>()) { return Token::ILLEGAL; } } else { AddLiteralChar(c); } } if (c0_ != quote) return Token::ILLEGAL; literal.Complete();
如果已經到了末尾,或者下 1 個字符是不能轉義的字符,則返回 Token::ILLEGAL。那么我們看看 ScanEscape 是不是返回了 false 呢?
template <bool capture_raw, bool in_template_literal> bool Scanner::ScanEscape() { uc32 c = c0_; Advance<capture_raw>(); // Skip escaped newlines. if (!in_template_literal && c0_ != kEndOfInput && IsLineTerminator(c)) { // Allow escaped CR+LF newlines in multiline string literals. if (IsCarriageReturn(c) && IsLineFeed(c0_)) Advance<capture_raw>(); return true; } switch (c) { case '\'': // fall through case '"' : // fall through case '\\': break; case 'b' : c = '\b'; break; case 'f' : c = '\f'; break; case 'n' : c = '\n'; break; case 'r' : c = '\r'; break; case 't' : c = '\t'; break; case 'u' : { c = ScanUnicodeEscape<capture_raw>(); if (c < 0) return false; break; } case 'v': c = '\v'; break; case 'x': { c = ScanHexNumber<capture_raw>(2); if (c < 0) return false; break; } case '0': // Fall through. case '1': // fall through case '2': // fall through case '3': // fall through case '4': // fall through case '5': // fall through case '6': // fall through case '7': c = ScanOctalEscape<capture_raw>(c, 2); break; } // Other escaped characters are interpreted as their non-escaped version. AddLiteralChar(c); return true; }
這個函數只有 2 處返回了 false。
1、如果轉義字符后面是 u,u 后面不是 Unicode 字符時,返回 false
2、如果轉義字符后面是 x,x 后面不是十六進制數字時,返回 false
也就是說:'\u'、'\uhello'、'\u1'、'\x'、'\xx' 都拋出異常。
Uncaught SyntaxError: Invalid Unicode escape sequence
或
Uncaught SyntaxError: Invalid hexadecimal escape sequence
而其它非轉義字符,都直接執(zhí)行了后面的代碼:
AddLiteralChar(c); return true;
前面的注釋也說明了這一點:
Other escaped characters are interpreted as their non-escaped version.
其他轉義字符被解釋為對應的非轉義版本。
綜上,問題的根源就是 JavaScript 和 JSON 對轉義字符的處理方式不同,導致了難以發(fā)現的 bug。JSON 遇到不能轉義的字符直接拋出異常,而 JavaScript 遇到不能轉義的字符直接解釋為對應的非轉義版本。
以上就是JavaScript 轉義字符JSON parse錯誤研究的詳細內容,更多關于JavaScript JSON parse錯誤的資料請關注腳本之家其它相關文章!
相關文章
js動態(tài)添加的DIV中的onclick事件簡單實例
下面小編就為大家?guī)硪黄猨s動態(tài)添加的DIV中的onclick事件簡單實例。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-07-07JavaScript簡單獲取系統(tǒng)當前時間完整示例
這篇文章主要介紹了JavaScript簡單獲取系統(tǒng)當前時間的方法,涉及javascript針對日期與時間的判斷以及字符串組合的相關技巧,需要的朋友可以參考下2016-08-08jquery.rotate.js實現可選抽獎次數和中獎內容的轉盤抽獎代碼
這篇文章主要介紹了jquery.rotate.js實現可選抽獎次數和中獎內容的轉盤抽獎代碼,需要的朋友可以參考下2017-08-08怎么理解wx.navigateTo的events參數使用詳情
這篇文章主要介紹了怎么理解wx.navigateTo的events參數使用詳情,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-05-05