进制间的转换html
base64编码node
如何实现base64git
平常工做中,频繁的使用base64取代小图标,以便减小HTTP请求进而达到性能优化的目的。基于此来聊聊编码的发展、为何须要base64以及如何实现base64。此文章首发于聊聊编码那些事,顺带实现base64转载请注明来源。github
一些前置知识,也涉及到一些经典的面试小题。面试
Number.prototype.toString(radix)json
说到parseInt,不得不提到一个颇有意思的面试题promise
// 会输出什么? [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
已经讲解的很详细,指望你们在工做之余不要太过浮躁,别作伸手党,静下心来啃一下文档并多作实践,不少面试题天然会迎刃而解。
如今的标准,有以下特色
在开发时,常常会有一些小图标图片,每个图片都会有一次HTTP请求,因为浏览器对同一个域名的并发数量有限制,因此咱们应该尽量减小HTTP请求个数。
本文主要讲解编码相关,那就只讲解从编码入手如何去减小HTTP请求。
在计算机内部,任何信息最终都是使用一系列二进制存储,图片也不例外。
并且在img
标签的src
属性后跟上一个base64
字符,若是该字符有效,那么会正常显示图片。
如下涉及的全部代码均在仓库中,感兴趣的能够自取。
首先准备一个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对象,十六进制也会被转十进制。如上输出所示。
十进制转为二进制能够经过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'...]
一个汉字在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'...]
二进制转为十进制能够经过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
中的64
其实是根据2**6
所来,表示则由大写字母、小写字母、数字、+/
构成。
const lowerCases = 'abcdefghijklmnopqrstuvwxyz' const numbers = '0123456789' const base64lib = `${lowerCases.toUpperCase()}${lowerCases}${numbers}+/` console.log(base64lib) // => ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
而后咱们则能够取到每个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,由于其逻辑较为复杂,可是本文讲解了大体思路,感兴趣的可再作深究。