目錄
-
- 對任意進制的數進行任意進制轉換
- 將任意進制數轉換爲十進制數
- 幾道關於parseInt的面試題
- 編碼發展歷史
- 小結
前言
日常工作中,頻繁的使用base64取代小圖標,以便減少HTTP請求進而達到性能優化的目的。基於此來聊聊編碼的發展、爲什麼需要base64以及如何實現base64。此文章首發於聊聊編碼那些事,順帶實現base64轉載請註明來源。
進制間的轉換
一些前置知識,也涉及到一些經典的面試小題。
對任意進制的數進行任意進制轉換
Number.prototype.toString(radix)
將任意進制數轉換爲十進制數
幾道關於parseInt的面試題
說到parseInt,不得不提到一個很有意思的面試題
// 會輸出什麼?
[1, 2, 3].map(parseInt)
// => 1, NaN, NaN
map
方法第一個參數爲函數,函數有三個參數,array.map((item, index, array) => { ... })
實際上相當於
function fn (item, index) {
return parseInt(item, index)
}
[1, 2, 3].map(fn)
// parseInt迭代過程相當於如下
// parseInt(1, 0) => 1
// parseInt(2, 1) => NaN
// parseInt(3, 2) => NaN
再來看一個類似的面試題
// 會輸出什麼?
'1 2 3'.replace(/\d/g, parseInt)
// => 1, NaN, 3
replace
方法第二個參數若是一個函數,函數會有若干個參數。第一個爲匹配模式的字符串;第二個爲與模式中子表達式匹配的字符串,可以有零個或多個這樣的參數。
實際上相當於如下
function fn (...args) {
// 只會取前兩個參數
return parseInt(args[0], args[1])
}
'1 2 3'.replace(/\d/g, fn)
// parseInt迭代過程相當於如下
// parseInt('1', 0) => 1
// parseInt('2', 2) => NaN
// parseInt('3', 4) => 3
其實在mdn
中對parseInt/map/replace
已經講解的很詳細,期望大家在工作之餘不要太過浮躁,別做伸手黨,靜下心來啃一下文檔並多做實踐,很多面試題自然會迎刃而解。
編碼發展歷史
ASCII
GBK2312
GBK
GB18030/DBCS
Unicode
UTF-8
現在的標準,有如下特點
- UTF-8 就是在互聯網上使用最廣的一種 Unicode 的實現方式
- UTF-8就是每次以8個位爲單位傳輸數據
- 而UTF-16就是每次 16 個位
- UTF-8 最大的一個特點,就是它是一種變長的編碼方式
- Unicode 一箇中文字符佔 2 個字節,而 UTF-8 一箇中文字符佔 3 個字節
- UTF-8 是 Unicode 的實現方式之一
base64編碼
爲什麼需要base64
在開發時,經常會有一些小圖標圖片,每一個圖片都會有一次HTTP請求,由於瀏覽器對同一個域名的併發數量有限制,所以我們應該儘可能減少HTTP請求個數。
本文主要講解編碼相關,那就只講解從編碼入手如何去減少HTTP請求。
在計算機內部,任何信息最終都是使用一系列二進制存儲,圖片也不例外。
而且在img
標籤的src
屬性後跟上一個base64
字符,如果該字符有效,那麼會正常顯示圖片。
如何實現base64
以下涉及的所有代碼均在倉庫中,感興趣的可以自取。
讀取buffer轉爲json對象
首先準備一個2.txt
文件。
馮蘭蘭啊我說今晚月色這麼美,你說是的。
case.js代碼
const fs = require('mz/fs')
const path = require('path')
// 讀取成buffer對象
async function read2JSON () {
let ret = await fs.readFile(path.resolve(__dirname, '2.txt'))
console.log(ret.toJSON())
return ret.toJSON()
}
read2JSON()
// => { type: 'Buffer', data: [ 229, 134, 175, 229... ] }
上面的依賴mz/fs已經將fs
都包裝成promise
,所以我們能寫的更像同步。
readFile
函數如果第二個參數沒有指定會讀取成一個buffer
流,是由一個個16進制
數組合在一起的。
buffer.toJSON可以將一個buffer流轉爲一個json對象,十六進制也會被轉十進制。如上輸出所示。
將10進制轉爲2進制
十進制轉爲二進制可以通過Number.toString(2)
方法
// 將10進制轉爲2進制
async function data2b () {
let data = await read2JSON()
let ret = []
data.data.forEach(item => {
ret.push(item.toString(2))
})
console.log(ret)
return ret
}
data2b()
// => [ '11100101', '10000110', '10101111', '11100101'...]
將2進制拼一起3*8然後分隔成4*6
一個漢字在UTF-8
規範中由三個字節組成,一個字節由8
個二進制物理位構成。所以一個漢字實際佔用內存3*8
,base64
中我們實際需要6
個物理位表示一個字節即2**6
,所以做重新分割4*6
。
async function split () {
let data = await data2b()
let dataStr = data.join('')
let ret = []
let splitUnit = 6
let flag = 0
while (flag < dataStr.length) {
ret.push(dataStr.substr(flag, splitUnit))
flag = flag + splitUnit
}
console.log(ret)
return ret
}
split()
// => [ '111001', '011000', '011010', '101111'...]
然後將2進制轉成10進制
二進制轉爲十進制可以通過parseInt(string, 2)
方法
async function data20 () {
let data = await split()
let ret = data.map(item => {
return parseInt(item, 2)
})
console.log(ret)
return ret
}
data20()
// => [ 57, 24, 26, 47, 57, 24, 22, 48, 57, 24, 22, 48 ]
base64碼
base64
中的64
實際上是根據2**6
所來,表示則由大寫字母、小寫字母、數字、+/
構成。
const lowerCases = 'abcdefghijklmnopqrstuvwxyz'
const numbers = '0123456789'
const base64lib = `${lowerCases.toUpperCase()}${lowerCases}${numbers}+/`
console.log(base64lib)
// => ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
取到每一個base64碼
然後我們則可以取到每一個base64
碼
async function main () {
let data = await data20()
let ret = []
data.forEach(item => {
ret.push(base64lib[item])
})
console.log(ret.join(''))
return ret.join()
}
main()
// => 5Yav5YWw5YWw5ZWK5oiR6K+05LuK5pma5pyI6Imy6L+Z5LmI576O77yM5L2g6K+05piv55qE44CC
我們可以前往base64在線轉碼解碼進行驗證。
簡化代碼
對以上思路進行代碼簡化
const CHARTS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
function transfer (str) {
let buf = Buffer.from(str)
let result = ''
for(let b of buf){
result += b.toString(2)
}
return result.match(/(\d{6})/g).map(val => parseInt(val, 2)).map(val => CHARTS[val]).join('')
}
let fl = transfer('馮')
console.log(fl) // => 5Yav
小結
如上我們可以實現將中文轉爲base64,同理我們也可以轉換圖片。
async function read2JSON () {
// let ret = await fs.readFile(path.resolve(__dirname, '2.txt'))
// 讀取圖片
let ret = await fs.readFile(path.resolve(__dirname, '../assets/encoding-base64-example.png'))
console.log(ret.toJSON())
return ret.toJSON()
}
特別的: 由於將2進制拼一起3*8然後分隔成4*6,原來存儲一個漢字需要三個字節,現在需要四個字節存儲,所以轉換爲base64
後會比之前大3/1
。
下面笑臉圖片則是由img的src屬性展示的
,不過本文並沒有實現圖片轉base64,因爲其邏輯較爲複雜,但是本文講解了大致思路,感興趣的可再做深究。