前端Base64編碼知識(shí),一文打盡
原文: https://juejin.cn/post/6989391487200919566 作者: 云的世界 掘金專欄: 前端基礎(chǔ)進(jìn)階
大廠技術(shù) 高級(jí)前端 Node進(jìn)階
點(diǎn)擊上方 程序員成長(zhǎng)指北,關(guān)注公眾號(hào)
回復(fù)1,加入高級(jí)Node交流群
前言
本文將詳細(xì)的介紹前端 Base64 編碼知識(shí),探索起源,讓大家對(duì)開(kāi)發(fā)經(jīng)常用到的 Base64 有個(gè)更全面深入的認(rèn)知。
大綱
Base64在前端的應(yīng)用 Base64數(shù)據(jù)編碼起源 Base64編碼64的含義 Base64編碼優(yōu)缺點(diǎn) 一些計(jì)算機(jī)和前端基礎(chǔ)知識(shí) ASCII碼, Unicode , UTF-8 Base64編碼和解碼 其他的成熟方案
Base64在前端的應(yīng)用
Base64編碼,你一定知道的,先來(lái)看看它在前端的一些常見(jiàn)應(yīng)用:
當(dāng)然絕部分場(chǎng)景都是基于Data URLs[1]
Canvas圖片生成
canvas的 toDataURL[2]可以把canvas的畫(huà)布內(nèi)容轉(zhuǎn)base64編碼格式包含圖片展示的 data URI[3]。
const ctx = canvasEl.getContext("2d");
// ...... other code
const dataUrl = canvasEl.toDataURL();
// data:image/png;base64,iVBORw0KGgoAAAANSUhE.........
你畫(huà)我猜,新用戶加入,要獲取當(dāng)前的最新的繪畫(huà)界面,也可以通過(guò)Base64格式的消息傳遞。
文件讀取
FileReader的 readAsDataURL[4]可以把上傳的文件轉(zhuǎn)為base64格式的data URI,比較常見(jiàn)的場(chǎng)景是用戶頭像的剪裁和上傳。
function readAsDataURL() {
const fileEl = document.getElementById("inputFile");
return new Promise((resolve, reject) => {
const fd = new FileReader();
fd.readAsDataURL(fileEl.files[0]);
fd.onload = function () {
resolve(fd.result);
// data:image/png;base64,iVBORw0KGgoAAAA.......
}
fd.onerror = reject;
});
}
jwt
jwt由header, payload,signature三部分組成,前兩個(gè)解碼后,都是可以明文看見(jiàn)的。拿 國(guó)服最強(qiáng)JWT生成Token做登錄校驗(yàn)講解,看完保證你學(xué)會(huì)![5] 里面的token做測(cè)試。

網(wǎng)站圖片和小圖片
移動(dòng)端網(wǎng)站圖標(biāo)優(yōu)化
<link rel="icon" href="data:," />
<link rel="icon" href="data:;base64,=" />
至于怎么獲得這個(gè)值data:,的:
<canvas height="0" width="0" id="canvas"></canvas>
<script>
const canvasEl = document.getElementById("canvas");
const ctx = canvasEl.getContext("2d");
dataUrl = canvasEl.toDataURL();
console.log(dataUrl); // data:,
</script>
小圖片
這個(gè)就有很多場(chǎng)景了,比如img標(biāo)簽,背景圖等
img標(biāo)簽:
<img src="data:image/png;base64,iVBORw0KGgoAAAA......." />
css背景圖:
.bg{
background: url(data:image/png;base64,iVBORw0KGgoAAAA.......)
}
簡(jiǎn)單的數(shù)據(jù)加密
當(dāng)然這不是好方法,但是至少讓你不好解讀。
const username = document.getElementById("username").vlaue;
const password = document.getElementById("password").vlaue;
const secureKey = "%%S%$%DS)_sdsdj_66";
const sPass = utf8_to_base64(password + secureKey);
doLogin({
username,
password: sPass
})
SourceMap
借用阮大神的一段代碼, 注意mappings字段,這實(shí)際上就是bas64編碼格式的內(nèi)容,當(dāng)然你直接去解,是會(huì)失敗的。
{
version : 3,
file: "out.js",
sourceRoot : "",
sources: ["foo.js", "bar.js"],
names: ["src", "maps", "are", "fun"],
mappings: "AAgBC,SAAQ,CAAEA"
}
具體的實(shí)現(xiàn)請(qǐng)看官方的base64-vlq.js[6]文件。
混淆加密代碼
著名的代碼混淆庫(kù), javascript-obfuscator[7],其也是有應(yīng)用base64幾碼的,一起看看選項(xiàng):
webpack-obfuscator[8]也是基于其封裝的。
--string-array-indexes-type '<list>' (comma separated) [hexadecimal-number, hexadecimal-numeric-string]
--string-array-encoding '<list>' (comma separated) [none, base64, rc4]
--string-array-index-shift <boolean>
--string-array-wrappers-count <number>
--string-array-wrappers-chained-calls <boolean>

其他
X.509公鑰證書(shū), github SSH key, mht文件,郵件附件等等,都有Base64的影子。
Base64數(shù)據(jù)編碼起源
早期郵件傳輸協(xié)議基于 ASCII 文本,對(duì)于諸如圖片、視頻等二進(jìn)制文件處理并不好。ASCII 主要用于顯示現(xiàn)代英文,到目前為止只定義了 128 個(gè)字符,包含控制字符和可顯示字符。為了解決上述問(wèn)題,Base64 編碼順勢(shì)而生。
Base64是編解碼,主要的作用不在于安全性,而在于讓內(nèi)容能在各個(gè)網(wǎng)關(guān)間無(wú)錯(cuò)的傳輸,這才是Base64編碼的核心作用。
除了Base64數(shù)據(jù)編碼,其實(shí)還有Base32數(shù)據(jù)編碼, Base16數(shù)據(jù)編碼,可以參見(jiàn) RFC 4648[9]。
Base64編碼64的含義
64就是64個(gè)字符的意思。
base64對(duì)照表, 借用 Base64原理[10]的一張圖:
A-Z26a-z260-910+ /2
26 + 26 + 10 + 2 = 64
當(dāng)然還有一個(gè)字符=,這是填充字符,后面會(huì)提到,不屬于64里面的范疇。
對(duì)照表的索引值,注意一下,后面的base64編碼和解碼會(huì)用到。
Base64編碼優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
可以將二進(jìn)制數(shù)據(jù)(比如圖片)轉(zhuǎn)化為可打印字符,方便傳輸數(shù)據(jù) 對(duì)數(shù)據(jù)進(jìn)行簡(jiǎn)單的加密,肉眼是安全的 如果是在html或者css處理圖片,可以減少http請(qǐng)求
缺點(diǎn)
內(nèi)容編碼后體積變大, 至少1/3
因?yàn)槭侨止?jié)變成四個(gè)字節(jié),當(dāng)只有一個(gè)字節(jié)的時(shí)候,也至少會(huì)變成三個(gè)字節(jié)。編碼和解碼需要額外工作量
說(shuō)完優(yōu)缺點(diǎn),回到正題:
我們今天的重點(diǎn)是 uf8編碼轉(zhuǎn)Base64編碼:
基本流程
char => 碼點(diǎn) => utf-8編碼 => base64編碼
在之前要解一下編碼的知識(shí), 了解編碼知識(shí),又要先了解一些計(jì)算機(jī)的基礎(chǔ)知識(shí)。
一些計(jì)算機(jī)和前端基礎(chǔ)知識(shí)
比特和字節(jié)
比特又叫位。在計(jì)算機(jī)的世界里,信息的表示方式只有 0 和 1, 其可以表示兩種狀態(tài)。
一位二進(jìn)制可以表示兩狀態(tài), N位可以表示2^N種狀態(tài)。
一個(gè)字節(jié)(Byte)有8位(Bit)
所以一個(gè)字節(jié)可以表示 2^8 = 256種狀態(tài);
獲得字符的 Unicode碼點(diǎn)
String.prototype.charCodeAt[11] 可以獲取字符的碼點(diǎn),獲取范圍為0 ~ 65535。這個(gè)地方注意一下,關(guān)系到后面的utf-8字節(jié)數(shù)。
"a".charCodeAt(0) // 97
"中".charCodeAt(0) // 20013
進(jìn)制表示
0b開(kāi)頭,可以表示二進(jìn)制
注意0b10000000= 128 ,0b11000000=92,之后會(huì)用到.
0b11111111 // 255
0b10000000 // 128 后面會(huì)用到
0b11000000 // 192 后面會(huì)用到

0x開(kāi)頭,可以表示16進(jìn)制
0x11111111 // 286331153

0o開(kāi)頭可以表示8進(jìn)制,就不多說(shuō)了,本來(lái)不會(huì)涉及。
進(jìn)制轉(zhuǎn)換
10進(jìn)制轉(zhuǎn)其他進(jìn)制
Number.prototype.toString(radix)[12]可以把十進(jìn)制轉(zhuǎn)為其他進(jìn)制。
100..toString(2) // 1100100
100..toString(16) // 64, 也等于 ox64
其他進(jìn)制轉(zhuǎn)為10進(jìn)制
parseInt(string, radix)[13]可以把其他進(jìn)制,轉(zhuǎn)為10進(jìn)制。
parseInt("10000000", 2) // 128
parseInt("10",16) // 16
這里額外提一下一元操作符號(hào)+可以把字符串轉(zhuǎn)為數(shù)字,后面也會(huì)用到,之前提到的0b,0o,0x這里都會(huì)生效。
+"1000" // 1000
+"0b10000000" // 128
+"0o10" // 8
+"0x10" // 16
位移操作
本文只涉及右移操作,就只講右移,右移相當(dāng)于除以2,如果是整數(shù),簡(jiǎn)單說(shuō)是去低位,移動(dòng)幾位去掉幾位,其實(shí)和10進(jìn)制除以10是一樣的。
64 >> 2 = 16 我們一起看一下過(guò)程
0 1 0 0 0 0 0 0 64
-------------------
0 1 0 0 0 0 | 0 0 16
一元 & 操作和 一元|操作
一元&
當(dāng)兩者皆為1的時(shí)候,值為1。 本文的作用可用來(lái)去高位, 具體看代碼。3553 & 36 = 0b110111100001 & 0b111111 = 100001
因?yàn)楦呶蝗笔В豢赡芏紴?,故均為0, 而低位相當(dāng)于復(fù)制一遍而已。
110111 100001
111111
------------
000000 100001
一元|
當(dāng)任意一個(gè)為1,就輸出為1. 本文用來(lái)填補(bǔ)0。比如,把3補(bǔ)成8位二進(jìn)制3 | 256 = 11 | 100000000 = 100000011
100000011.substring(1)是不是就等于8位二進(jìn)制呢00000011
具備了這些基本知識(shí),我們就開(kāi)始先了解編碼相關(guān)的知識(shí)。
ASCII碼, Unicode , UTF-8
ASCII碼
ASCII碼第一位始終是0, 那么實(shí)際可以表示的狀態(tài)是 2^7 = 128種狀態(tài)。
ASCII 主要用于顯示現(xiàn)代英文,到目前為止只定義了 128 個(gè)字符,包含控制字符和可顯示字符。
0~31 之間的ASCII碼常用于控制像打印機(jī)一樣的外圍設(shè)備 32~127 之間的ASCII碼表示的符號(hào),在我們的鍵盤(pán)上都可以被找到
完整的 ASCII碼對(duì)應(yīng)表,可以參見(jiàn) 基本ASCII碼和擴(kuò)展ASCII碼[14]
接下來(lái)是Unicode和UTF-8編碼,請(qǐng)先記住這個(gè)重要的知識(shí):
Unicode: 字符集 UTF-8: 編碼規(guī)則
Unicode
Unicode 為世界上所有字符都分配了一個(gè)唯一的編號(hào)(碼點(diǎn)),這個(gè)編號(hào)范圍從 0x000000 到 0x10FFFF (十六進(jìn)制),有 100 多萬(wàn),每個(gè)字符都有一個(gè)唯一的 Unicode 編號(hào),這個(gè)編號(hào)一般寫(xiě)成 16 進(jìn)制,在前面加上 U+。例如:掘的 Unicode 是U+6398。
U+0000到U+FFFF
最前面的65536個(gè)字符位,它的碼點(diǎn)范圍是從0一直到216-1。所有最常見(jiàn)的字符都放在這里。
U+010000一直到U+10FFFF
剩下的字符都放著這里,碼點(diǎn)范圍從U+010000一直到U+10FFFF。
Unicode有平面的概念,這里就不拓展了。
Unicode只規(guī)定了每個(gè)字符的碼點(diǎn),到底用什么樣的字節(jié)序表示這個(gè)碼點(diǎn),就涉及到編碼方法。
UTF-8
UTF-8 是互聯(lián)網(wǎng)使用最多的一種 Unicode 的實(shí)現(xiàn)方式。還有 UTF-16(字符用兩個(gè)字節(jié)或四個(gè)字節(jié)表示)和 UTF-32(字符用四個(gè)字節(jié)表示)等實(shí)現(xiàn)方式。
UTF-8 是它是一種變長(zhǎng)的編碼方式, 使用的字節(jié)個(gè)數(shù)從 1 到 4 個(gè)不等,最新的應(yīng)該不止4個(gè), 這個(gè)1-4不等,是后面編碼和解碼的關(guān)鍵。
UTF-8的編碼規(guī)則:
對(duì)于只有一個(gè)字節(jié)的符號(hào),字節(jié)的第一位設(shè)為 0,后面 7 位為這個(gè)符號(hào)的 Unicode 碼。此時(shí),對(duì)于英語(yǔ)字母UTF-8 編碼和 ASCII 碼是相同的。對(duì)于 n字節(jié)的符號(hào)(n > 1),第一個(gè)字節(jié)的前n位都設(shè)為1,第n + 1位設(shè)為0,后面字節(jié)的前兩位一律設(shè)為10。剩下的沒(méi)有提及的二進(jìn)制位,全部為這個(gè)符號(hào)的 Unicode 碼,如下表所示:
| Unicode 碼點(diǎn)范圍(十六進(jìn)制) | 十進(jìn)制范圍 | UTF-8 編碼方式(二進(jìn)制) | 字節(jié)數(shù) |
|---|---|---|---|
0000 0000 ~ 0000 007F | 0 ~ 127 | 0xxxxxxx | 1 |
0000 0080 ~ 0000 07FF | 128 ~ 2047 | 110xxxxx 10xxxxxx | 2 |
0000 0800 ~ 0000 FFFF | 2048 ~ 65535 | 1110xxxx 10xxxxxx 10xxxxxx | 3 |
0001 0000 ~ 0010 FFFF | 65536 ~ 1114111 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx | 4 |
我們可能沒(méi)見(jiàn)過(guò)字節(jié)數(shù)為2或者為4的字符, 字節(jié)數(shù)為2的可以去Unicode對(duì)應(yīng)表[15]這里找,而等于4的可以去這看看Unicode? 13.0 Versioned Charts Index[16]
下面這些碼點(diǎn)都處于0000 0080 ~ 0000 07FF, utf-8編碼需要2個(gè)字節(jié)
下面這些碼點(diǎn)都處于0001 0000 ~ 0010 FFFF, utf-8編碼需要4個(gè)字節(jié)
可能這里光說(shuō)不好理解,我們分別以英文字符a和中文字符掘來(lái)講解一下:
為了驗(yàn)證結(jié)果,可以去 Convert UTF8 to Binary Bits - Online UTF8 Tools[17]
英文字符a
先獲得其碼點(diǎn), "a".charCodeAt(0)等于97對(duì)照表格, 0~127, 需 1個(gè)字節(jié)97..toString(2)得到編碼1100001根據(jù)格式 0xxxxxxx進(jìn)行填充, 最終結(jié)果
01100001
中文字符掘
先獲得其碼點(diǎn), "掘".charCodeAt(0)等于25496對(duì)照表格,2048 ~ 65535 需 3個(gè)字節(jié)25496..toString(2)得到編碼110 001110 011000根絕格式 1110xxxx 10xxxxxx 10xxxxxx進(jìn)行填充, 最終結(jié)果如下
11100110 10001110 10011000
Convert UTF8 to Binary Bits - Online UTF8 Tools[18]執(zhí)行結(jié)果:完全匹配

抽象把字符轉(zhuǎn)為utf8格式二進(jìn)制的方法
基于上面的表格和轉(zhuǎn)換過(guò)程,我們抽象一個(gè)方法,這個(gè)方法在之后的Base64編碼和解碼至關(guān)重要:
先看看功能,覆蓋utf8編碼1-3字節(jié)范圍
console.log(to_binary("A")) // 11100001
console.log(to_binary("?")) // 1101100010110011
console.log(to_binary("掘")) // 111001101000111010011000
方法如下
function to_binary(str) {
const string = str.replace(/\r\n/g, "\n");
let result = "";
let code;
for (var n = 0; n < string.length; n++) {
//獲取麻點(diǎn)
code = str.charCodeAt(n);
if (code < 0x007F) { // 1個(gè)字節(jié)
// 0000 0000 ~ 0000 007F 0 ~ 127 1個(gè)字節(jié)
// (code | 0b100000000).toString(2).slice(1)
result += (code).toString(2).padStart(8, '0');
} else if ((code > 0x0080) && (code < 0x07FF)) {
// 0000 0080 ~ 0000 07FF 128 ~ 2047 2個(gè)字節(jié)
// 0x0080 的二進(jìn)制為 10000000 ,8位,所以大于0x0080的,至少有8位
// 格式 110xxxxx 10xxxxxx
// 高位 110xxxxx
result += ((code >> 6) | 0b11000000).toString(2);
// 低位 10xxxxxx
result += ((code & 0b111111) | 0b10000000).toString(2);
} else if (code > 0x0800 && code < 0xFFFF) {
// 0000 0800 ~ 0000 FFFF 2048 ~ 65535 3個(gè)字節(jié)
// 0x0800的二進(jìn)制為 1000 00000000,12位,所以大于0x0800的,至少有12位
// 格式 1110xxxx 10xxxxxx 10xxxxxx
// 最高位 1110xxxx
result += ((code >> 12) | 0b11100000).toString(2);
// 第二位 10xxxxxx
result += (((code >> 6) & 0b111111) | 0b10000000).toString(2);
// 第三位 10xxxxxx
result += ((code & 0b111111) | 0b10000000).toString(2);
} else {
// 0001 0000 ~ 0010 FFFF 65536 ~ 1114111 4個(gè)字節(jié)
// https://www.unicode.org/charts/PDF/Unicode-13.0/U130-2F800.pdf
throw new TypeError("暫不支持碼點(diǎn)大于65535的字符")
}
}
return result;
}
方法中有三個(gè)地方稍微難理解一點(diǎn),我們一起來(lái)解讀一下:
二字節(jié) (code >> 6) | 0b11000000
其作用是生成高位二進(jìn)制。
我們以實(shí)際的一個(gè)栗子來(lái)講解,以?為例,其碼點(diǎn)為0x633,在0000 0080 ~ 0000 07FF之間,占兩個(gè)字節(jié), 在其二進(jìn)制編碼為11 000110011 , 其填充格式如下, 低位要用6位。
110xxxxx 10xxxxxx
為了方便觀察,我們把 11 000110011 重新調(diào)整一下 11000 110011。
(code >> 6) 等于 00110011 >> 6,右移6位, 直接干掉低6位。為什么是6呢,因?yàn)榈臀恍枰?位,右移動(dòng)6位后,剩下的就是用于高位操作的位了。
11000000
11000 | 110011
--------------
11011000
二字節(jié) (code & 0b111111) | 0b10000000
作用,用于生成低位二進(jìn)制。以?為例,11000 110011, 填充格式
110xxxxx 10xxxxxx
(code & 0b111111)這步的操作是為了干掉6位以上的高位,僅僅保留低6位。一元&符號(hào),兩邊都是1的時(shí)候才會(huì)是1,妙啊。
11000 110011
111111
------------------
110011
接著進(jìn)行 | 0b10000000, 主要是按照格式10xxxxxx進(jìn)行位數(shù)填補(bǔ), 讓其滿8位。
11000 110011
111111 (code & 0b111111)
------------------
110011
10 000000 (code & 0b111111) | 0b10000000
-------------------
10 110011
Base64編碼和解碼
utf-8轉(zhuǎn)Base64編碼規(guī)則
獲取每個(gè)字符的Unicode碼,轉(zhuǎn)為utf-8編碼 三個(gè)字節(jié)作為一組,一共是24個(gè)二進(jìn)制位
字節(jié)數(shù)不能被 3 整除,用0字節(jié)值在末尾補(bǔ)足按照6個(gè)比特位一組分組,前兩位補(bǔ)0,湊齊8位 計(jì)算每個(gè)分組的數(shù)值 以第 4步的值作為索引,去ASCII碼表找對(duì)應(yīng)的值替換第 2步添加字節(jié)數(shù)個(gè)數(shù)的=
比如第2添加了2個(gè)字節(jié),后面是2個(gè)=
以大掘A為例, 我們通過(guò)上面的utf8_to_binary方法得到utf8的編碼11100110 10001110 10011000 11000001, 其字節(jié)數(shù)不能被3整除,后面填補(bǔ)
11100110
10001110
10011000
01000001
--------
00000000
00000000
6位一組分為四組, 高位補(bǔ)0, 用| 分割一下填補(bǔ)的。
00 | 111001 => 57 => 5
00 | 101000 => 40 => o
00 | 111010 => 58 => 6
00 | 011000 => 24 => Y
00 | 110000 => 16 => Q
00 | 010000 => 16 => Q
00 | 000000 => => =
00 | 000000 => => =
結(jié)果是:5o6YQQ==, 完美。
utf-8轉(zhuǎn)Base64編碼規(guī)則代碼實(shí)現(xiàn)
基于上面的to_binary方法和base64的轉(zhuǎn)換規(guī)則,就很簡(jiǎn)單啦:
先看看執(zhí)行效果,very good, 和 base64.us[19] 結(jié)果完全一致。
console.log(utf8_to_base64("a")); // YQ==
console.log(utf8_to_base64("?")); // yII=
console.log(utf8_to_base64("中國(guó)人")); // 5Lit5Zu95Lq6
console.log(utf8_to_base64("Coding Writing 好文召集令|后端、大前端雙賽道投稿,2萬(wàn)元獎(jiǎng)池等你挑戰(zhàn)!"));
//Q29kaW5nIFdyaXRpbmcg5aW95paH5Y+s6ZuG5Luk772c5ZCO56uv44CB5aSn5YmN56uv5Y+M6LWb6YGT5oqV56i/77yMMuS4h+WFg+WlluaxoOetieS9oOaMkeaImO+8gQ==
完整代碼如下:
const BASE64_CHARTS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
function utf8_to_base64(str: string) {
let binaryStr = to_binary(str);
const len = binaryStr.length;
// 需要填補(bǔ)的=的數(shù)量
let paddingCharLen = len % 24 !== 0 ? (24 - len % 24) / 8 : 0;
//6個(gè)一組
const groups = [];
for (let i = 0; i < binaryStr.length; i += 6) {
let g = binaryStr.slice(i, i + 6);
if (g.length < 6) {
g = g.padEnd(6, "0");
}
groups.push(g);
}
// 求值
let base64Str = groups.reduce((b64str, cur) => {
b64str += BASE64_CHARTS[+`0b${cur}`]
return b64str
}, "");
// 填充=
if (paddingCharLen > 0) {
base64Str += paddingCharLen > 1 ? "==" : "=";
}
return base64Str;
}
至于解碼,是其逆過(guò)程,留給大家去實(shí)現(xiàn)吧。
其他的成熟方案
當(dāng)然是基于已有的 btoa和atob,
但是 unescape是不被推薦使用的方法
function utf8_to_b64( str ) {
return window.btoa(unescape(encodeURIComponent( str )));
}
function b64_to_utf8( str ) {
return decodeURIComponent(escape(window.atob( str )));
}
// Usage: utf8_to_b64('? à la mode'); // "4pyTIMOgIGxhIG1vZGU=" b64_to_utf8('4pyTIMOgIGxhIG1vZGU='); // "? à la mode"
MDN的 rewriting `atob()` and `btoa()` using `TypedArray`s and UTF-8[20]
其支持到6字節(jié),但是可讀性并不好。
第三方庫(kù) base64-js[21] 與 js-base64[22]都是周下載量過(guò)百萬(wàn)的庫(kù)。
雖然有那么多成熟的,但是我們理解和自己實(shí)現(xiàn),才能更明白Base64的編碼原理。
額外補(bǔ)充一點(diǎn)
編碼關(guān)系圖
借用[你真的了解 Unicode 和 UTF-8 嗎?[23]]一張圖:

DOMString[24] 是 utf-16編碼
引用
Version-Specific Charts[25]
Unicode13.0.0[26]
Unicode? 13.0 Versioned Charts Index[27]
RFC 4648 | The Base16, Base32, and Base64 Data Encodings[28]
Base64 encoding and decoding[29]
字符編碼筆記:ASCII,Unicode 和 UTF-8[30]
Unicode與JavaScript詳解[31]
Base64 編碼入門(mén)教程[32] Base64原理[33]
詳解base64原理[34]
一文讀懂base64編碼[35]
JS 中關(guān)于 base64 的一些事[36]
Base64 的原理、實(shí)現(xiàn)及應(yīng)用[37]
圖片與Base64換算關(guān)系[38]
[你真的了解 Unicode 和 UTF-8 嗎?[39]]
Unicode中UTF-8與UTF-16編碼詳解[40]
Unicode對(duì)應(yīng)表[41]
JavaScript Source Map 詳解[42]
我組建了一個(gè)氛圍特別好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你對(duì)Node.js學(xué)習(xí)感興趣的話(后續(xù)有計(jì)劃也可以),我們可以一起進(jìn)行Node.js相關(guān)的交流、學(xué)習(xí)、共建。下方加 考拉 好友回復(fù)「Node」即可。

“分享、點(diǎn)贊、在看” 支持一波 
