JavaScript如何正確處理Unicode編碼問題!
Fundebug經授權轉載,版權歸原作者所有。
JavaScript 處理 Unicode 的方式至少可以說是令人驚訝的。本文解釋了 JavaScript 中的 處理 Unicode 相關的痛點,提供了常見問題的解決方案,並解釋了ECMAScript 6 標準如何改進這種情況。
Unicode 基礎知識
在深入研究 JavaScript 之前,先解釋一下 Unicode 一些基礎知識,這樣在 Unicode 方面,我們至少都瞭解一些。
Unicode 是目前絕大多數程式使用的字元編碼,定義也很簡單,用一個 碼位(code point) 對映一個字元。碼位值的範圍是從 U+0000
到 U+10FFFF
,可以表示超過 110 萬個字元。下面是一些字元與它們的碼位。
- A 的碼位 U+0041
- a 的碼位 U+0061
- © 的碼位 U+00A9
- 的碼位 U+2603
- 的碼位 U+1F4A9
碼位 通常被格式化為十六進位制數字,零填充至少四位數,格式為 U +字首
。
Unicode 最前面的 65536 個字元位,稱為 基本多文種平面(BMP-—Basic Multilingual Plane),又簡稱為“零號平面”, plane 0),它的 碼位 範圍是從 U+0000
到 U+FFFF
。最常見的字元都放在這個平面上,這是 Unicode 最先定義和公佈的一個平面。
剩下的字元都放在 輔助平面(Supplementary Plane)或者 星形平面(astral planes) ,碼位範圍從 U+010000
一直到 U+10FFFF
,共 16 個輔助平面。
輔助平面內的碼位很容易識別:如果需要超過 4 個十六進位制數字來表示碼位,那麼它就是一個輔助平面內的碼。
現在對 Unicode 有了基本的瞭解,接下來看看它如何應用於 JavaScript 字串。
轉義序列
在谷歌控制檯輸入如下:
>> `x41x42x43`
`ABC`
>> `x61x62x63`
`abc`
以下稱為十六進位制轉義序列。它們由引用匹配碼位的兩個十六進位制數字組成。例如,x41
碼位為 U+0041
表示大寫字母 A。這些轉義序列可用於 U+0000
到 U+00FF
範圍內的碼位。
同樣常見的還有以下型別的轉義:
>> `u0041u0042u0043`
`ABC`
>> `I u2661 JavaScript!`
`I JavaScript!
這些被稱為 Unicode轉義序列。它們由表示碼位的 4 個十六進位制數字組成。例如,u2661
表示碼位為 U+2661
表示一個心。這些轉義序列可以用於 U+0000
到 U+FFFF
範圍內的碼位,即整個基本平面。
但是其他的所有輔助平面呢? 我們需要 4 個以上的十六進位制數字來表示它們的碼位,那麼如何轉義它們呢?
在 ECMAScript 6中,這很簡單,因為它引入了一種新的轉義序列: Unicode 碼位轉義。例如:
>> `u{41}u{42}u{43}`
`ABC`
>> `u{1F4A9}`
`` // U+1F4A9 PILE OF POO
在大括號之間可以使用最多 6 個十六進位制數字,這足以表示所有 Unicode 碼位。因此,通過使用這種型別的轉義序列,可以基於其程式碼位輕鬆轉義任何 Unicode 碼位。
為了向後相容 ECMAScript 5 和更舊的環境,不幸的解決方案是使用代理對:
>> `uD83DuDCA9`
`` // U+1F4A9 PILE OF POO
在這種情況下,每個轉義表示代理項一半的碼位。兩個代理項就組成一個輔助碼位。
注意,代理項對碼位與原始碼位全不同。有公式可以根據給定的輔助碼位來計算代理項對碼位,反之亦然——根據代理對計算原始輔助程式碼位。
輔助平面(Supplementary Planes)中的碼位,在 UTF-16 中被編碼為一對16 位元長的碼元(即32bit,4Bytes),稱作代理對(surrogate pair),具體方法是:
- 碼位減去
0x10000
,得到的值的範圍為 20 位元長的0..0xFFFFF
. - 高位的 10 位元的值(值的範圍為
0..0x3FF
)被加上0xD800
得到第一個碼元或稱作高位代理。 - 低位的 10 位元的值(值的範圍也是
0..0x3FF
)被加上0xDC00
得到第二個碼元或稱作低位代理(low surrogate),現在值的範圍是0xDC00..0xDFFF
.
使用代理對,所有輔助平面中的碼位(即從 U+010000
到 U+10FFFF
)都可以表示,但是使用一個轉義來表示基本平面的碼位,以及使用兩個轉義來表示輔助平面中的碼位,整個概念是令人困惑的,並且會產生許多惱人的後果。
使用 JavaScript 字串方法來計算字元長度
例如,假設你想要計算給定字串中的字元個數。你會怎麼做呢?
首先想到可能是使用 length
屬性。
>> `A`.length // 碼位: U+0041 表示 A
1
>> `A` == `u0041`
true
>> `B`.length // 碼位: U+0042 表示 B
1
>> `B` == `u0042`
true
在這些例子中,字串的 length
屬性恰好反映了字元的個數。這是有道理的:如果我們使用轉義序列來表示字元,很明顯,我們只需要對每個字元進行一次轉義。但情況並非總是如此!這裡有一個稍微不同的例子:
>> `?`.length // 碼位: U+1D400 表示 Math Bold 字型大寫 A
2
>> `?` == `uD835uDC00`
true
>> `?`.length // 碼位: U+1D401 表示 Math Bold 字型大寫 B
2
>> `?` == `uD835uDC01`
true
>> ``.length // U+1F4A9 PILE OF POO
2
>> `` == `uD83DuDCA9`
true
在內部,JavaScript 將輔助平面內的字元表示為代理對,並將單獨的代理對部分開為單獨的 “字元”。如果僅使用 ECMAScript 5 相容轉義序列來表示字元,將看到每個輔助平面內的字元都需要兩個轉義。這是令人困惑的,因為人們通常用 Unicode 字元或圖形來代替。
計算輔助平面內的字元個數
回到這個問題:如何準確地計算 JavaScript 字串中的字元個數 ? 訣竅就是如何正確地解析代理對,並且只將每對代理對作為一個字元計數。你可以這樣使用:
var regexAstralSymbols = /[uD800-uDBFF][uDC00-uDFFF]/g;
function countSymbols(string) {
return string
// Replace every surrogate pair with a BMP symbol.
.replace(regexAstralSymbols, `_`)
// …and *then* get the length.
.length;
}
或者,如果你使用 Punycode.js,利用它的實用方法在 JavaScript 字串和 Unicode 碼位之間進行轉換。decode
方法接受一個字串並返回一個 Unicode 編碼位陣列;每個字元對應一項。
function countSymbols(string) {
return punycode.ucs2.decode(string).length;
}
在 ES6 中,可以使用 Array.from 來做類似的事情,它使用字串的迭代器將其拆分為一個字串陣列,每個字串陣列包含一個字元:
function countSymbols(string) {
return Array.from(string).length;
}
或者,使用解構運算子 ...
:
function countSymbols(string) {
return [...string].length;
}
使用這些實現,我們現在可以正確地計算碼位,這將導致更準確的結果:
>> countSymbols(`A`) // 碼位:U+0041 表示 A
1
>> countSymbols(`?`) // 碼位: U+1D400 表示 Math Bold 字型大寫 A
1
>> countSymbols(``) // U+1F4A9 PILE OF POO
1
找撞臉
考慮一下這個例子:
>> `mañana` == `mañana`
false
JavaScript告訴我們,這些字串是不同的,但視覺上,沒有辦法告訴我們!這是怎麼回事?
JavaScript轉義工具會告訴你,原因如下:
>> `maxF1ana` == `manu0303ana`
false
>> `maxF1ana`.length
6
>> `manu0303ana`.length
7
第一個字串包含碼位 U+00F1
表示字母 n 和 n 頭上波浪號,而第二個字串使用兩個單獨的碼位(U+006E
表示字母 n 和 U+0303
表示波浪號)來建立相同的字元。這就解釋了為什麼它們的長度不同。
然而,如果我們想用我們習慣的方式來計算這些字串中的字元個數,我們希望這兩個字串的長度都為 6,因為這是每個字串中可視可區分的字元的個數。要怎樣才能做到這一點呢?
在ECMAScript 6 中,解決方案相當簡單:
function countSymbolsPedantically(string) {
// Unicode Normalization, NFC form, to account for lookalikes:
var normalized = string.normalize(`NFC`);
// Account for astral symbols / surrogates, just like we did before:
return punycode.ucs2.decode(normalized).length;
}
String.prototype
上的 normalize
方法執行 Unicode規範化,這解釋了這些差異。 如果有一個碼位表示與另一個碼位後跟組合標記相同的字元,則會將其標準化為單個碼位形式。
>> countSymbolsPedantically(`mañana`) // U+00F1
6
>> countSymbolsPedantically(`mañana`) // U+006E + U+0303
6
為了向後相容 ECMAScript5 和舊環境,可以使用 String.prototype.normalize polyfill。
計算其他組合標記
然而,上述方案仍然不是完美的——應用多個組合標記的碼位總是導致單個可視字元,但可能沒有 normalize 的形式,在這種情況下,normalize 是沒有幫助。例如:
>> `qu0307u0323`.normalize(`NFC`) // `q̣̇`
`qu0307u0323`
>> countSymbolsPedantically(`qu0307u0323`)
3 // not 1
>> countSymbolsPedantically(`Z͑ͫ̓ͪ̂ͫ̽͏̴̙̤̞͉͚̯̞̠͍A̴̵̜̰͔ͫ͗͢L̠ͨͧͩ͘G̴̻͈͍͔̹̑͗̎̅͛́Ǫ̵̹̻̝̳͂̌̌͘!͖̬̰̙̗̿̋ͥͥ̂ͣ̐́́͜͞`)
74 // not 6
如果需要更精確的解決方案,可以使用正規表示式從輸入字串中刪除任何組合標記。
// 將下面的正規表示式替換為經過轉換的等效表示式,以使其在舊環境中工作
var regexSymbolWithCombiningMarks = /(P{Mark})(p{Mark}+)/gu;
function countSymbolsIgnoringCombiningMarks(string) {
// 刪除任何組合字元,只留下它們所屬的字元:
var stripped = string.replace(regexSymbolWithCombiningMarks, function($0, symbol, combiningMarks) {
return symbol;
});
return punycode.ucs2.decode(stripped).length;
}
此函式刪除任何組合標記,只留下它們所屬的字元。任何不匹配的組合標記(在字串開頭)都保持不變。這個解決方案甚至可以在 ECMAScript3 環境中工作,並且它提供了迄今為止最準確的結果:
>> countSymbolsIgnoringCombiningMarks(`qu0307u0323`)
1
>> countSymbolsIgnoringCombiningMarks(`Z͑ͫ̓ͪ̂ͫ̽͏̴̙̤̞͉͚̯̞̠͍A̴̵̜̰͔ͫ͗͢L̠ͨͧͩ͘G̴̻͈͍͔̹̑͗̎̅͛́Ǫ̵̹̻̝̳͂̌̌͘!͖̬̰̙̗̿̋ͥͥ̂ͣ̐́́͜͞`)
6
計算其他型別的圖形叢集
上面的演算法仍然是一個簡化—它還是無法正確計算像這樣的字元:நி,漢語言由連體的 Jamo 組成,如 깍, 表情字元序列,如 (( U+200D
+ U+200D
+ + U+200D
+ )或其他類似字元。
Unicode 文字分段上的 Unicode 標準附件#29 描述了用於確定字形簇邊界的演算法。 對於適用於所有 Unicode指令碼的完全準確的解決方案,請在 JavaScript 中實現此演算法,然後將每個字形叢集計為單個字元。 有人建議將Intl.Segmenter(一種文字分段API)新增到ECMAScript中。
JavaScript 中字串反轉
下面是一個類似問題的示例:在JavaScript中反轉字串。這能有多難,對吧? 解決這個問題的一個常見的、非常簡單的方法是:
function reverse(string) {
return string.split(``).reverse().join(``);
}
它似乎在很多情況下都很有效:
>> reverse(`abc`)
`cba`
>> reverse(`mañana`) // U+00F1
`anañam`
然而,它完全打亂了包含組合標記或位於輔助平面字元的字串。
>> reverse(`mañana`) // U+006E + U+0303
`anãnam` // note: the `~` is now applied to the `a` instead of the `n`
>> reverse(``) // U+1F4A9
`��` // ``uDCA9uD83D``, the surrogate pair for `` in the wrong order
要在 ES6 中正確反轉位於輔助平面字元,字串迭代器可以與 Array.from
結合使用:
function reverse(string) {
return Array.from(string).reverse().join(``);
}
但是,這仍然不能解決組合標記的問題。
幸運的是,一位名叫 Missy Elliot 的聰明的電腦科學家提出了一個防彈演算法來解釋這些問題。它看上去像這樣:
我把丁字褲放下,翻轉,然後倒過來。我把丁字褲放下,翻轉,然後倒過來。
事實上:通過將任何組合標記的位置與它們所屬的字元交換,以及在進一步處理字串之前反轉任何代理對,可以成功避免問題。
// 使用庫 Esrever (https://mths.be/esrever)
>> esrever.reverse(`mañana`) // U+006E + U+0303
`anañam`
>> esrever.reverse(``) // U+1F4A9
`` // U+1F4A9
字串方法中的 Unicode 的問題
這種行為也會影響其他字串方法。
將碼位轉轉換為字元
String.fromCharCode
可以將一個碼位轉換為字元。 但它只適用於 BMP 範圍內的碼位 ( 即從 U+0000
到U+FFFF
)。如果將它用於轉換超過 BMP 平面外的碼位 ,將獲得意想不到的結果。
>> String.fromCharCode(0x0041) // U+0041
`A` // U+0041
>> String.fromCharCode(0x1F4A9) // U+1F4A9
`` // U+F4A9, not U+1F4A9
唯一的解決方法是自己計算代理項一半的碼位,並將它們作為單獨的引數傳遞。
>> String.fromCharCode(0xD83D, 0xDCA9)
`` // U+1F4A9
如果不想計算代理項的一半,可以使用 Punycode.js 的實用方法:
>> punycode.ucs2.encode([ 0x1F4A9 ])
`` // U+1F4A9
幸運的是,ECMAScript 6 引入了 String.fromCodePoint(codePoint)
,它可以位於基本平面外的碼位的字元。它可以用於任何 Unicode 編碼點,即從 U+000000
到 U+10FFFF
。
>> String.fromCodePoint(0x1F4A9)
`` // U+1F4A9
為了向後相容ECMAScript 5 和更舊的環境,使用 String.fromCodePoint() polyfill
。
從字串中獲取字元
如果使用 String.prototype.charAt(position)
來檢索包含字串中的第一個字元,則只能獲得第一個代理項而不是整個字元。
>> ``.charAt(0) // U+1F4A9
`uD83D` // U+D83D, i.e. the first surrogate half for U+1F4A9
有人提議在 ECMAScript 7 中引入 String.prototype.at(position)
。它類似於charAt
,只不過它儘可能地處理完整的字元而不是代理項的一半。
>> ``.at(0) // U+1F4A9
`` // U+1F4A9
為了向後相容 ECMAScript 5 和更舊的環境,可以使用 String.prototype.at() polyfill/prollyfill。
從字串中獲取碼位
類似地,如果使用 String.prototype.charCodeAt(position)
檢索字串中第一個字元的碼位,將獲得第一個代理項的碼位,而不是 poo 字元堆的碼位。
>> ``.charCodeAt(0)
0xD83D
幸運的是,ECMAScript 6 引入了 String.prototype.codePointAt(position)
,它類似於 charCodeAt
,只不過它儘可能處理完整的字元而不是代理項的一半。
>> ``.codePointAt(0)
0x1F4A9
為了向後相容 ECMAScript 5 和更舊的環境,使用 String.prototype.codePointAt()_polyfill。
遍歷字串中的所有字元
假設想要迴圈字串中的每個字元,並對每個單獨的字元執行一些操作。
在 ECMAScript 5 中,你必須編寫大量的樣板程式碼來判斷代理對:
function getSymbols(string) {
var index = 0;
var length = string.length;
var output = [];
for (; index < length - 1; ++index) {
var charCode = string.charCodeAt(index);
if (charCode >= 0xD800 && charCode <= 0xDBFF) {
charCode = string.charCodeAt(index + 1);
if (charCode >= 0xDC00 && charCode <= 0xDFFF) {
output.push(string.slice(index, index + 2));
++index;
continue;
}
}
output.push(string.charAt(index));
}
output.push(string.charAt(index));
return output;
}
var symbols = getSymbols(``);
symbols.forEach(function(symbol) {
console.log(symbol == ``);
});
或者可以使用正規表示式,如 var regexCodePoint = /[^uD800-uDFFF]|[uD800-uDBFF][uDC00-uDFFF]|[uD800-uDFFF]/g;
並迭代匹配
在 ECMAScript 6中,你可以簡單地使用 for…of
。字串迭代器處理整個字元,而不是代理對。
for (const symbol of ``) {
console.log(symbol == ``);
}
不幸的是,沒有辦法對它進行填充,因為 for…of
是一個語法級結構。
其他問題
此行為會影響幾乎所有字串方法,包括此處未明確提及的方法(如 String.prototype.substring
,String.prototype.slice
等),因此在使用它們時要小心。
正規表示式中的 Unicode 問題
匹配碼位和 Unicode 標量值
正規表示式中的點運算子(.
)只匹配一個“字元”, 但是由於JavaScript將代理半部分公開為單獨的 “字元”,所以它永遠不會匹配位於輔助平面上的字元。
>> /foo.bar/.test(`foobar`)
false
讓我們思考一下,我們可以使用什麼正規表示式來匹配任何 Unicode字元? 什麼好主意嗎? 如下所示的,.
這w個是不夠的,因為它不匹配換行符或整個位於輔助平面上的字元。
>> /^.$/.test(``)
false
為了正確匹配換行符,我們可以使用 [sS]
來代替,但這仍然不能匹配整個位於輔助平面上的字元。
>> /^[sS]$/.test(``)
false
事實證明,匹配任何 Unicode 編碼點的正規表示式一點也不簡單:
>> /[ -uD7FFuE000-uFFFF]|[uD800-uDBFF][uDC00-uDFFF]|[uD800-uDBFF](?![uDC00-uDFFF])|(?:[^uD800-uDBFF]|^)[uDC00-uDFFF]/.test(``) // wtf
true
當然,你不希望手工編寫這些正規表示式,更不用說除錯它們了。為了生成像上面的一個正規表示式,可以使用了一個名為 Regenerate的庫,它可以根據碼位或字元列表輕鬆地建立正規表示式:
>> regenerate().addRange(0x0, 0x10FFFF).toString()
`[ -uD7FFuE000-uFFFF]|[uD800-uDBFF][uDC00-uDFFF]|[uD800-uDBFF](?![uDC00-uDFFF])|(?:[^uD800-uDBFF]|^)[uDC00-uDFFF]`
從左到右,這個正規表示式匹配BMP字元、代理項對或單個代理項。
雖然在 JavaScript 字串中技術上允許使用單獨的代理,但是它們本身並不對映到任何字元,因此應該避免使用。術語 Unicode標量值 指除代理碼位之外的所有碼位。下面是一個正規表示式,它匹配任何 Unicode 標量值:
>> regenerate()
.addRange(0x0, 0x10FFFF) // all Unicode code points
.removeRange(0xD800, 0xDBFF) // minus high surrogates
.removeRange(0xDC00, 0xDFFF) // minus low surrogates
.toRegExp()
/[ -uD7FFuE000-uFFFF]|[uD800-uDBFF][uDC00-uDFFF]/
Regenerate作為構建指令碼的一部分使用的,用於建立複雜的正規表示式,同時仍然保持生成這些表示式的指令碼的可讀性和易於維護。
ECMAScript 6 為正規表示式引入一個 u
標誌,它會使用 .
操作符匹配整個碼位,而不是代理項的一半。
>> /foo.bar/.test(`foobar`)
false
>> /foo.bar/u.test(`foobar`)
true
注意 .
操作符仍然不會匹配換行符,設定 u
標誌時,.
操作符等效於以下向後相容的正規表示式模式:
>> regenerate()
.addRange(0x0, 0x10FFFF) // all Unicode code points
.remove( // minus `LineTerminator`s (https://ecma-international.org/ecma-262/5.1/#sec-7.3):
0x000A, // Line Feed <LF>
0x000D, // Carriage Return <CR>
0x2028, // Line Separator <LS>
0x2029 // Paragraph Separator <PS>
)
.toString();
`[ - x0Bfx0E-u2027u202A-uD7FFuE000-uFFFF]|[uD800-uDBFF][uDC00-uDFFF]|[uD800-uDBFF](?![uDC00-uDFFF])|(?:[^uD800-uDBFF]|^)[uDC00-uDFFF]`
>> /foo(?:[ - x0Bfx0E-u2027u202A-uD7FFuE000-uFFFF]|[uD800-uDBFF][uDC00-uDFFF]|[uD800-uDBFF](?![uDC00-uDFFF])|(?:[^uD800-uDBFF]|^)[uDC00-uDFFF])bar/u.test(`foobar`)
true
位於輔助平面碼位上的字元
考慮到 /[a-c]/
匹配任何字元從 碼位為 U+0061
的字母 a 到 碼位為 U+0063
的字母 c,似乎/[-]/ 會匹配碼位 U+1F4A9
到碼位 U+1F4AB
,然而事實並非如此:
>> /[-]/
SyntaxError: Invalid regular expression: Range out of order in character class
發生這種情況的原因是,正規表示式等價於:
>> /[uD83DuDCA9-uD83DuDCAB]/
SyntaxError: Invalid regular expression: Range out of order in character class
事實證明,不像我們想的那樣匹配碼位 U+1F4A9
到碼位 U+1F4AB
,而是匹配正規表示式:
- U+D83D(高代理位)
- 從
U+DCA9
到U+D83D
的範圍(無效,因為起始碼位大於標記範圍結束的碼位) - U+DCAB(低代理位)
>> /[uD83DuDCA9-uD83DuDCAB]/u.test(`uD83DuDCA9`) // match U+1F4A9
true
>> /[u{1F4A9}-u{1F4AB}]/u.test(`u{1F4A9}`) // match U+1F4A9
true
>> /[-]/u.test(``) // match U+1F4A9
true
>> /[uD83DuDCA9-uD83DuDCAB]/u.test(`uD83DuDCAA`) // match U+1F4AA
true
>> /[u{1F4A9}-u{1F4AB}]/u.test(`u{1F4AA}`) // match U+1F4AA
true
>> /[-]/u.test(``) // match U+1F4AA
true
>> /[uD83DuDCA9-uD83DuDCAB]/u.test(`uD83DuDCAB`) // match U+1F4AB
true
>> /[u{1F4A9}-u{1F4AB}]/u.test(`u{1F4AB}`) // match U+1F4AB
true
>> /[-]/u.test(``) // match U+1F4AB
true
遺憾的是,這個解決方案不能向後相容 ECMAScript 5 和更舊的環境。如果這是一個問題,應該使用 Regenerate 生成 es5相容的正規表示式,處理輔助平面範圍內的字元:
>> regenerate().addRange(``, ``)
`uD83D[uDCA9-uDCAB]`
>> /^uD83D[uDCA9-uDCAB]$/.test(``) // match U+1F4A9
true
>> /^uD83D[uDCA9-uDCAB]$/.test(``) // match U+1F4AA
true
>> /^uD83D[uDCA9-uDCAB]$/.test(``) // match U+1F4AB
true
實戰中的 bug 以及如何避免它們
這種行為會導致許多問題。例如,Twitter 每條 tweet 允許 140 個字元,而它們的後端並不介意它是什麼型別的字元——是否為輔助平面內的字元。但由於JavaScript 計數在其網站上的某個時間點只是讀出字串的長度,而不考慮代理項對,因此不可能輸入超過 70 個輔助平面內的字元。(這個bug已經修復。)
許多處理字串的JavaScript庫不能正確地解析輔助平面內的字元。
例如,Countable.js 它沒有正確計算輔助平面內的字元。
Underscore.string
有一個 reverse 方法,它不處理組合標記或輔助平面內的字元。(改用 Missy Elliot 的演算法)
它還錯誤地解碼輔助平面內的字元的 HTML 數字實體,例如 💩
。 許多其他 HTML 實體轉換庫也存在類似的問題。(在修復這些錯誤之前,請考慮使用 he 代替所有 HTML 編碼/解碼需求。)
原文:
https://firebase.google.com/docs/cloud-messaging/
程式碼部署後可能存在的BUG沒法實時知道,事後為了解決這些BUG,花了大量的時間進行log 除錯,這邊順便給大家推薦一個好用的BUG監控工具 Fundebug。
你的點贊是我持續分享好東西的動力,歡迎點贊!
一個笨笨的碼農,我的世界只能終身學習!
更多內容請關注公眾號《大遷世界》!
關於Fundebug
Fundebug專注於JavaScript、微信小程式、微信小遊戲、支付寶小程式、React Native、Node.js和Java線上應用實時BUG監控。 自從2016年雙十一正式上線,Fundebug累計處理了9億+錯誤事件,付費客戶有Google、360、金山軟體、百姓網等眾多品牌企業。歡迎大家免費試用!
相關文章
- JavaScript 如何正確處理 Unicode 編碼問題!JavaScriptUnicode
- 如何正確處理nonce
- 處理JavaScript異常的正確姿勢JavaScript
- ptyon 特殊處理 url 編碼與解碼,字元編碼轉化 unicode字元Unicode
- 以太坊實戰之《如何正確處理nonce》
- 正確處理listview的positionView
- 如何正確學習JavaScript?JavaScript
- lxml處理xml時的字元編碼問題XML字元
- JavaScript 中錯誤正確處理方式,你用對了嗎?JavaScript
- Java處理正則匹配卡死(正則回溯問題)Java
- 正確的軟體開發程式有助於處理不可預見的問題
- 遇到股票套牢該如何正確處理?股票套牢解套技
- Python編解碼問題與文字檔案處理Python
- Python 中 Unicode 的正確用法PythonUnicode
- 使用 pymysql 的時候如何正確的處理轉義字元MySql字元
- 如何在Kubernetes部署期間正確處理DB模式模式
- Python 編碼處理之 str與Unicode的區別與使用PythonUnicode
- 軟體編碼階段遇到問題的處理辦法
- 材質優化:如何正確處理紋理和材質的關係優化
- 有關java的unicode編碼的問題,大家幫忙JavaUnicode
- Recoil 中預設值的正確處理
- iOS --NSDecimalNumber 處理計算精度不準確問題iOSDecimal
- 如何處理 No DMARC Record Found 問題
- 如何處理HTTP 503故障問題?HTTP
- 【廖雪峰python入門筆記】Unicode編碼_UnicodeDecodeError處理Python筆記UnicodeError
- 前端工作流編譯正確操作流程和錯誤處理記錄前端編譯
- MySQL:亂碼問題處理流程MySql
- java處理中文亂碼問題Java
- 非正規方法處理AngulurJS模組管理問題JS
- 提出問題,解答問題!這才是理解程式碼設計的正確方法
- Unicode編碼解碼Unicode
- Node中POST請求的正確處理方式
- 正確處理被病毒侵入電腦的方法
- Javascript如何訪問和處理系統檔案JavaScript
- [譯]JavaScript async / await:好處、坑和正確用法JavaScriptAI
- ORACLE問題處理十個指令碼Oracle指令碼
- Java 正確的做字串編碼轉換Java字串編碼
- Linux 和 Windows 下編碼問題處理 codestyle 解決方法LinuxWindows