所謂加密,就是經過設計算法來把字符串轉化爲看似雜亂無章的文本,讓人不容易看穿想表達的意思。git
而解密就是按照設計的算法,反推出本來的文本內容。github
在這個算法,就是開發者本身所要設計的,既可簡單又可複雜,全憑開發者本身的設計能力。算法
而對js字符串進行加密,咱們能夠利用幾個工具(方法)來輔助你進行字符串的轉化,反覆轉化幾回就能迷惑別人了。chrome
可是在此以前,我但願你能對Unicode、ASCII、utf-8以及base64等涉及編碼方面的,有所瞭解。可閱讀此篇快速瞭解常見的字符集與編碼方案的認識安全
編碼,能夠把淺顯易懂的字符轉化爲難以直觀理解的數字或者雜亂無章的字符,即能達到讓別人摸不着頭腦的加密效果了。bash
可是,單純的利用Unicode或編碼進行的字符串轉化,這種形式的加密,太簡單了,畢竟它們都是有廣爲人知的固定規則的,很容易讓別人識破或反轉化,畢竟,都是有一一對應的關係在那裏。函數
所以,要巧妙地混用它們,設計出一個相對複雜的算法,就能達到基礎的加密效果了。工具
對此,咱們先認識一些關於字符串轉化的相關函數方法ui
按照UTF-16編碼,把對應的數字碼值轉化對應的字符。是字符串的靜態方法。編碼
語法:
String.fromCharCode(n1[, n2, ...n])
複製代碼
參數爲依據UTF-16編碼的碼值,任何進製表示都行,如十進制或十六進制
返回值爲按碼值順序排列組合起來的對應字符造成的字符串,如
String.fromCharCode(68)
// 返回字符'D',D對應的Unicode就是68
String.fromCharCode(100)
// 返回字符'd',d對應的Unicode就是100
String.fromCharCode(68, 100)
// 返回字符串'Dd'
複製代碼
須要注意的是,UTF-16編碼是依據Unicode編碼的一種中間轉化格式方案,在碼值0~65535(0x0000 ~ 0xFFFF)範圍內,即BMP零號平面下,UTF-16的碼值和Unicode的碼值表明的字符是同樣的,都是1個16bit二進制表明一個字符;可是超出65535外的碼值,UTF-16是用兩個16bit二進制來表示一個字符,即用兩個碼值表示一個字符,稱兩個碼值爲代理對,這是與Unicode不一樣的。
因此該方法的參數,若是要轉換超出65535碼值外的字符,則參數是代理對,而不是直接的Unicode碼值。若是用了BMP以上的碼值,會直接對大於16bit的二進制高位部分進行截斷處理,由於參數只能是16bit表示一個字符,雖然不會報錯,可是明顯輸出結果變了,不是預期的結果。
// 如U+1F303,在Unicode裏1F303是一個獨立的碼值,表示"Night with Stars"這個字符🌃
// 可是在UTF-16裏,是用兩個碼值來表示該字符(0xD83C,0xDF03)
// 返回結果爲 🌃
String.fromCharCode(0xD83C, 0xDF03)
// 超出0xFFFF,作二進制高位截斷,會變成和
// String.fromCharCode(0xF303)的結果
String.fromCharCode(0x1F303)
複製代碼
和fromCharCode
目的相反,它是把字符轉化爲UTF-16碼值。但他它不一樣的是,他是實例方法,執行者是字符串對象實例。
語法:
string.charCodeAt(index)
複製代碼
參數爲該字符串的某個字符所在位置的下標(取值範圍是0 ~ string.length-1)
返回值
NaN
;若是是非數字類型或者空,那麼默認是0;fromCharCode
瞭解到,一個字符能夠是用代理對錶示的,那麼這種狀況下咱們要知道,這種字符的length
是2
而不是1
,因此index的取值範圍是0~1,0時返回代理對的第一個,1爲第二個,不傳時默認是0;// '🌃'的代理對是55356,57091
'🌃'.length // 2
'🌃'.charCodeAt(0) // 返回55356
'🌃'.charCodeAt(1) // 返回57091
複製代碼
'i am a teacher'.charCodeAt(2);
// 返回第三個字符a的Unicode值,97
複製代碼
從上面的fromCharCode
瞭解到,它是有缺陷的,由於可能須要用到代理對來進行轉化,這就涉及到須要進行換算出代理對這麼一個麻煩的過程了,爲了不進行沒必要要的計算,ES 2015有一個新的方法fromCodePoint
。
從名字都能直觀瞭解到,它是直接基於碼值進行的轉化,即直接根據Unicode的碼值來,而沒必要用UTF-16的代理對來表示一個字符。靜態方法
語法:
String.fromCodePoint(n1[, n2, ...n]);
複製代碼
參數爲Unicode的碼值,即數字,什麼進制均可以。
返回值爲按照參數的順序Unicode碼值對應的字符拼接的字符串。
仍是以字符'🌃'來舉例說明,該字符在Unicode裏用U+1F303表示,碼值爲0x1F303;在UTF-16裏,是用代理對(0xD83C,0xDF03)表示
// 該方法就要用代理對轉換
String.fromCharCode(0xD83C, 0xDF03)
// 該方法就能夠直接用碼值轉換
String.fromCodePoint(0x1F303)
複製代碼
fromCodePoint
與fromCharCode
的區別就在於前者可用Unicode碼值直接轉換,可是IE不支持,兼容沒後者好。
與fromCodePoint
相對應的,與charCodeAt
相對比的,codePointAt
的用途也很明顯。
把字符轉化Unicode碼值,實例方法,一樣是ES 2015的新方法。
語法:
string.codePointAt(index)
複製代碼
參數 爲該字符串的某個字符所在位置的下標(取值範圍是0 ~ string.length-1),要注意,若是字符是能夠用代理對錶示的,即65535碼值外的字符,則直接傳該字符所在下標的第一個位置,返回的就是該字符的實際Unicode碼值,傳第二個位置的話,就是代理對裏的第二個碼值。
'🌃'.codePointAt(0) // 返回127747,爲該字符Unicode碼值
'🌃'.codePointAt(1) // 返回57091,爲該字符代理對(55356, 57091)的第二個碼值
複製代碼
返回值
undefined
;''
,null
,undefined
,false
或不傳,那麼默認是0(chrome下的表現);一樣,IE不支持。
關於這兩個函數的說明,能夠查看我這邊文章URI編碼的兩方法異同與場景
btoa
,即binary to ascii,同理,atob就是ascii to binary。都是window對象下的方法。IE9及如下不支持。
從名字上大致瞭解基本做用,btoa
做用就是把字符轉化其ASCII值base64編碼後的字符。而atob
則是它的逆運算,解碼base64編碼後的字符,即還原原字符。
這裏主要說btoa
,由於atob
很簡單,就是把btoa
的結果當作參數執行就行了。如下內容全是針對btoa
所說。
語法
window.btoa(stringToEncode)
複製代碼
它會把參數的每個字符都被視爲一個二進制數據字節來進行轉換。可是該方法所能識別的的碼位是基於ASCII來的,因此超出 0x00 ~ 0xFF 範圍,則會引起 InvalidCharacterError 異常。因此若是字符是Unicode字符就會報錯了
參數若是是數字類型,那也會當成是String類型處理,如100,即對1字符,0字符,0字符分別base64編碼處理。
很明顯,該方法不能針對Uncode字符進行編碼,即你不能對中文編碼啦。
有對其進行加強的處理,以支持Unicode的編碼:
// ucs-2 string to base64 encoded ascii
function utoa(str) {
return window.btoa(encodeURIComponent(str));
}
// base64 encoded ascii to ucs-2 string
function atou(str) {
return decodeURIComponent(window.atob(str));
}
// Usage:
utoa('✓ à la mode'); // 4pyTIMOgIGxhIG1vZGU=
atou('4pyTIMOgIGxhIG1vZGU='); // "✓ à la mode"
複製代碼
介紹了好幾種方法,先簡單分類總結下,避免混淆。
fromCharCode
& charCodeAt
是針對UTF-16編碼的;fromCodePoint
& codePointAt
是針對Unicode編碼的;encodeURI
& encodeURIComponent
是針對UTF-8編碼的;btoa
& atob
是針對base64編碼的這裏舉個簡單的例子,你們能夠從這裏拿到點啓發,可是不太建議直接套用,仍是本身動手作點改造,這樣比較安全一點,還能夠本身設計更復雜點。
例子僅爲爲你取得啓發之用。
這裏設計的算法比較簡單:
咱們針對要進行加密的字符串,將其每一個字符先轉化爲Unicode值,而後基於該值作一些運算,而後獲得一個新的數字,再將該數字轉化爲Unicode對應的字符。這樣,一個新的字符串誕生了。爲了更加複雜點,對其再作編碼工做。
// 加密方法
function encryptChar(target) {
let result = '';
for (let i = 0, j = 0; j < target.length; j++, i++) {
let code = target.codePointAt(j);
result += String.fromCodePoint(code + i + j);
// 超出BMP的碼值,是兩個碼值表示一個字符
if (code > 65535) { j += 1; }
}
return encodeURIComponent(result);
}
// 解密方法
function decryptChar(target) {
let newTarget = decodeURIComponent(target);
let result = '';
for (let i = 0, j = 0; j < newTarget.length; j++, i++) {
let code = newTarget.codePointAt(j);
result += String.fromCodePoint(code - i - j);
// 超出BMP的碼值,是兩個碼值表示一個字符
if (code > 65535) {
j += 1;
}
}
return result;
}
複製代碼
這裏跟不少網上的資料(不少都是複製粘貼的吧?)不一樣,這裏採用的是codePointAt
和fromCodePoint
,而不是charCodeAt
和fromCharCode
,由於後者不能針對Unicode進行編碼。那麼若是把範圍考慮到Unicode字符的話,則會出現代理對錶示一個字符的狀況,就是該字符的length
值爲2,因此作循環的處理字符編碼時,要用j
來作實際字符的Unicode碼值工做,codePointAt
轉化BMP外的字符傳參爲該字符所在下標的第一個便可獲得Unicode碼值,而i
僅僅是表示字符的個數。
由此得出的方案,比網上的資料可靠多了(我本身也查了很多,來來去去都是那樣,複製粘貼),起碼這裏彌補了它們的缺點所在。
看了上述例子。你大概知道怎麼混用以上的幾個工具了。你能夠設計出更加複雜的算法,各類運算加減乘除取餘之類的。
建議仍是好好消化一些編碼方面的知識,以及我上面羅列的幾個方法的優缺點,對你會有更大幫助哦。
對你有幫助的請點贊支持~
未經容許,請勿私自轉載