你可能在網上見過有人用 幾個不同的字符寫的各種稀奇古怪的 JavaScript 代碼,雖然看起來奇怪,但是能正常運行!比如這個:
(!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+[]]*~+[]]
你猜運行結果是什么?你可以自己去控制台試一下。
看起來很神奇,但這到底是怎么回事呢?
事實上,你幾乎可以用下面這 6 個字符寫出任意的 JavaScript 程序:
[]()!+
很多人都知道這個技巧,但是沒有多少開發人員知道它到底是如何工作的。今天,我們就來看看它背后的執行原理。我們的目標是用這幾個字符來寫出字符串“self”
。姑且用這個字符串向 Self 語言致敬,JavaScript 的靈感來源之一就是它。
基本原理
我們之所以能夠拋開其他字符不用,要歸功於 JavaScript 的類型系統和數據類型轉換機制。
這 6 個字符是這樣各顯神通的:[]
可以用來創建數組,!
和+
可以在數組上執行一些操作,再用()
給這些操作分組。
先看一個簡單的數組:
[]
數組前加上!
會把它轉成布爾值。數組被認為是真值,因此取非之后變成了false
:
![] === false
除非轉換為類似類型,否則無法將不同類型的值加在一起。JavaScript 在進行轉換時遵循一個預定義的規則:
在表達式2 + true
中,JavaScript 會將true
轉成數字,得到表達式2+1
。
在表達式2 + "2"
中,JavaScript 會將數字轉成字符串,得到2 + "2" === "22"
。
這些轉換規則還不算糟糕,但是對於其他類型,好戲馬上來了。
JavaScript 數組強制轉換
數組相加會轉換成字符串並連接起來。空數組轉換為空字符串,因此將兩個數組相加將得到空字符串。
[] + [] === "" + "" === ""
數組跟其他類型值相加時也一樣:
![] + [] === "false" + "" === "false"
驚不驚喜?我們得到了目標字符串"self"
所包含的幾個字符!
如果我們能產生一些數字,就可以按正確的順序提取所需的字符:
"false"[3] === "s"
(![] + [])[3] === "s"
那么,如何生成數字呢?
生成數字
前面提到了,可以把數組轉成布爾值。那如果用加號+
把它轉成數字會怎樣?
+[] === ???
JavaScript 會嘗試調用數組的valueOf
方法,但是發現不存在這個方法,然后就轉而調用toString()
方法了。因此上面的代碼等效於:
+[] === +""
將字符串轉換為數字將產生以下結果:
+"42" === 42
+"esg" == NaN
+"" === 0
空字符串是一個 false
值,跟 null,undefined和數字零類似,因此將其中任何一個轉換為數字都會變成零:
+null === 0
+undefined === 0
+false === 0
+NaN === 0
+"" === 0
因此,將數組轉換為數字需要先將其轉換為字符串,最后轉成 0:
+[] === +"" === 0
第一個數字已經造出來了!我們還需要更多數字,繼續:
!0 === !false
!false === true
!0 === true
將 0
取否就得到一個為真的布爾值。為真的布爾值轉成數字,就是1
:
+true === 1
有了 1
,自然就可以得到2
,所謂道生一,一生二,二生三,三生萬物……
用上面的轉換大法,可以輕松得到我們想要的這些數字:
1 === +true == +(!0) ==== +(!(+[])) === +!+[]
1 === +!+[]
2 === +!+[] +!+[]
3 === +!+[] +!+[] +!+[]
4 === +!+[] +!+[] +!+[] +!+[]
臨門一腳,大功告成
總結下這些規則:
- 數組屬於真值,取否就得到
false
:![] // false
- 數組相加時會轉換成字符:
[] + [] // ""
- 空數組轉成數字得到
0
,再去否得到true
,再轉成數字得到1
:+(!(+[])) === 1
根據這些規則,我們就能得到想要的字符串。看下面這個示意圖就很清楚了:
![] + [] === "false"
+!+[] === 1
(![] + [])[3] + (![] + [])[4] + (![] + [])[2] + (![] + [])[0]
^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^
"false" "false" "false" "false"
^^^^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^^^^^^
s e l f
最終的表達式就是這樣:
(![] + [])[+!+[]+!+[]+!+[]] +
(![] + [])[+!+[]+!+[]+!+[]+!+[]] +
(![] + [])[+!+[]+!+[]] +
(![] + [])[+[]]
整理下空格和換行,就是一行代碼:
(![]+[])[+!+[]+!+[]+!+[]]+(![]+[])[+!+[]+!+[]+!+[]+!+[]]+(![]+[])[+!+[]+!+[]]+(![]+[])[+[]]
現在你應該明白了那些神奇 JavaScript 代碼的原理了吧?發揮你的想象,看還能寫出其他什么來?比如,2020 年剛到,來個 “Happy New Year”?
Anyway,Happy New Year!
出處:https://www.cnblogs.com/lzkwin/p/12131718.html
=======================================================================================
JSFuck原理解析(一) —— 基礎原理
本系列博客是對jsfuck代碼的一些分析,暫定為三篇。第一篇為jsfuck基礎原理,第二篇為官方案例分析,第三篇為源碼解析。
不出意外的話就是這三篇,我實在是比較懶,第一篇過了一年半才去寫第二篇,但願第三篇不會再拖了……
如果你也有對知識的渴望,可以直接訪問原作者的GitHub:https://github.com/aemkei/jsfuck 原作者其實已經解釋很清楚了。不過學習嘛,自己分析才有樂趣。
那下面開始正文:
什么是jsfuck?
想象一下自己第一次見到代碼的樣子,滿屏的奇怪字符?WTF?而當我看到jsfuck的時候,再次體會到了這種感覺。請看下面的代碼:
[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+[+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]])()
WTF???
沒錯!這確實是一串js代碼,你可以直接放到控制台去執行,最后執行的結果是alert(1)。JS就是這么一門神奇的語言。本着對知識的渴求與對未知事物的探索精神(霧),下面我們就一起來研究下jsfuck的原理。
在講jsfuck之前,先要說起一個js的經典面試題:[]==![]
這道題是考驗js基礎的,利用的是js的一些基本原理——類型轉換。類型轉換大家都了解,比如簡單的 ‘a’+1結果就是’a1’,在這個計算過程中1被隱式轉換成string類型,這道面試題也是這個原理。Js語法中’==’兩邊如果不是同一類型則會進行類型轉換。
但是相信很多接觸過前端的同事見到這個題目時候的第一反應,是這個表達式的結果是false,然而拿到控制台執行之后卻發現是true,這讓很多人十分不解。特別是當我們在控制台打印代碼的時候,會出現一些令人費解的事情。如:
Boolean(![]) //false
其實上述代碼,我們進行了強制轉換,在js中在把對象做布爾值轉換的時候,會把所有對象都轉換為true(包括創建的new Boolean(false)對象轉換之后也是true,具體可參考犀牛書3.8.3)。而相等運算符卻不是這么計算的,在相等運算符中,如果運算符兩邊的值有一個是布爾類型,則會把true轉換成1,false則轉換為0。而在這道題目中![]的結果肯定是布爾值false,則會轉換為0。
[]==0
這時候解釋起來就簡單了很多,相等運算符如果有一邊是數字,而另一邊是對象,則會把對象轉換成數字。而空的數組對象轉換成數字是0(犀牛書3.8)那么結果便是:
0==0 //true
講了這么多,終於可以進入正題了。之所以開篇講這么一個題目,是為了更好理解jsfuck的原理。在官網上(直接百度jsfuck就行),我們能看到官方給出的jsfuck的Basics,下面我們來逐條分析。(水平有限,如果有發現錯誤可以指出,大家一起研究)
false => ![]
//非運算符會把[]的布爾值取反
true => !![]
//再取反則是true
undefined => [][[]]
//這一個就比較復雜,在js中數組的索引可以為負數和非整數,而在這種情況下會將索引轉換成字符串,作為對象的屬性訪問,我們知道[]轉換為字符串為空字符串,則上面等價於:[][‘’]。然而數組對象並沒有名為’’的屬性,在js中訪問一個對象並不存在的屬性時,會給出undefined
NaN => +[![]]
//這里利用了+運算符的特性,這里+不是加號運算符(姑且這么叫)而是一元運算符的一種,一元加法,用於把操作數轉換為數字或者NaN並返回這個數字。上式等價於+[false],而根據類型轉換的規則我們知道,這樣得到的結果為NaN
0 => +[]
//根據類型轉換規則,[]轉換為數字為0
1 => +!+[]
//本式為上面的變式,根據運算符的優先級,先執行+[]為0,再轉換成布爾取反為true,true轉換為數字為1
2 => !+[]+!+[]
//中間一個+為加法運算符優先級比較低,等價於true+true,值為2
10 => [+!+[]]+[+[]]
//可拆分成[+!+[]]加[+[]],左邊為[1],右邊為[0],+作為連接字符串運算符,而根據數組轉換成字符串我們可以知道上面等價於‘1’+‘0’,得到的是字符串‘10’
Array => []
//[]就是數組對象的事例,用於獲取字符串’Array’,用["constructor"]找到對應函數,再將其變成字符串,在最后一篇源碼解析中會講到。
Number => +[]
//與上式相同,后面會詳細講解
String => []+[]
//與上式相同,后面會詳細講解
Boolean => ![]
//與上式相同,后面會詳細講解
Function => []["filter"]
//與上式相同,后面會詳細講解
eval => []["filter"]["constructor"]( CODE )()
//這里比較復雜,我們先來看前半部分[]["filter"]["constructor"],這一部分可以看到拿的是一個數組對象的‘filter’屬性下面的‘constructor’屬性,‘filter’屬性是數組的一個方法,但在這里並沒有用到該方法,你把它替換成別的方法也可以照常運行(例如‘map’),關鍵是下面這一步,filter的constructor屬性,我們知道在js中任何方法都可以看做是Function對象的一個實例,而Function就作為所有函數的構造函數。這里拿到的就是函數的Function()構造函數。而Function()構造函數的最后一個參數會作為函數體執行。因此這段語句會執行‘CODE里的內容’。
window => []["filter"]["constructor"]("return this")()
//與上式相同,拿到的是函數的構造函數,第一個括號內便是函數體。並且Fuction()構造函數所創建的函數並不使用詞法作用域(划重點!),他是直接執行在頂層函數中的,也就是全局作用域,因此this返回window。
說了這么多,卻還是無法解釋開篇那外星語言一般的代碼,但是有了這篇博客的基礎,接下來就方便了很多下一篇會詳細分析jf里面的一些字母與語句的由來,敬請期待
出處:https://blog.csdn.net/qq_36539075/article/details/79946099
=========================================================================
JSfuck原理解析(二) —— 官方例子分析
上一篇我們已經解釋了jsfuck的基本原理,簡單來說,如果我們想要用jsfuck加密一段可執行代碼,那么代碼最后應該是這樣的類型:
Function(code)()
在上一篇中我們提到,Function()()這一段可以轉換成如下代碼:
[]["filter"]["constructor"]()()
那么,我們就得到了一段完全由“ ()+[]! ”與字符串組合的代碼,接下來我們只需要把字符串也加密成“ ()+[]! ”就可以了!
所需要加密的字符串,包含了所有可顯示字符在此,我們依然拿“ alert(1) ”來舉例:
字符串“ alert(1) ”可拆解為a、l、e、r、t、(、1、),八個字符,這些字符我們很容易在上一篇文章中找到:
1 => +!+[]
false => ![]
true => !![]
由上可得:
'a' == 'false'[1] == (false + '')[1] == (![]+[])[+!+[]]
'l' == 'false'[2] == (false + '')[2] == (![]+[])[!+[]+!+[]]
'e' == 'true'[0] == (true + '')[3] == (!![]+[])[!+[]+!+[]+!+[]]
'r' == 'true'[0] == (true + '')[1] == (!![]+[])[+!+[]]
't' == 'true'[0] == (true + '')[0] == (!![]+[])[+[]]
則:'alert' == (![]+[])[+!+[]] + (![]+[])[!+[]+!+[]] + (!![]+[])[!+[]+!+[]+!+[]] + (!![]+[])[+!+[]] + (!![]+[])[+[]]
那么我們如何得到括號字符呢?首先我們要找到包含括號的字符串--方法,方法的基本樣式就是function name (){ code }這樣我們只需要把它轉換成字符串,按照上面的方法實現一遍就行了。jsfuck官方給出的方法是:
([][[]]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[!+[]+!+[]+[!+[]+!+[]]]
拆分可得:
(undefined + []['fill'])['22'] == "undefinedfunction fill() { [native code] }"['22']
這里只是用了數組的fill方法,和方法本身沒有關系,換成find沒有影響,一樣可以得到括號。即:
(undefined + []['find'])['22'] == "undefinedfunction find() { [native code] }"['22']
既然得到了左括號,那么右括號只需要把數字22改成23就可以得到,即:
([+[]]+![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[!+[]+!+[]+[+[]]]
把以上加密后的代碼組合(部分代碼記得加上括號),我們便得到了:
(![]+[])[+!+[]] + (![]+[])[!+[]+!+[]] + (!![]+[])[!+[]+!+[]+!+[]] + (!![]+[])[+!+[]] + (!![]+[])[+[]]+([][[]]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[!+[]+!+[]+[!+[]+!+[]]]+(+!+[])+([+[]]+![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[!+[]+!+[]+[+[]]] == 'alert(1)'
到目前為止,字符串我們已經拿到了,接下來只需要按照上面的方法,拿到Function就行了,最終組合成:
[]["filter"]["constructor"]('alert(1)')() == Function('alert(1)')()
這樣一個完美的jsfuck加密代碼已經完成了!
出處:https://blog.csdn.net/qq_36539075/article/details/102463521
===================================================================================================
JSFuck原理解析(三) —— 源碼解析
通過前兩章我們已經了解了jsfuck的基本原理與實現,現在不妨先設想一下,假設我們要自己實現這么一個加密代碼,應該如何去做。
首先,我們拿到了一段明文代碼“alert(1)”,為了把他變成jsf的模式,我們要按照第二章的描述對各個字符挨個加密,最后拼接成想要的代碼。那么如果我們想把這個過程工程化,我們就需要一個map,里面有每個字符對應的jsf代碼,這樣我們加密一串代碼只需要拼接就行了,事實上jsf的原作者也是這么做的。
打開github:https://github.com/aemkei/jsfuck,根目錄下有一個叫做jsfuck.js的文件,里面部分代碼如下:
const SIMPLE = {
'false': '![]',
'true': '!![]',
'undefined': '[][[]]',
'NaN': '+[![]]',
'Infinity': '+(+!+[]+(!+[]+[])[!+[]+!+[]+!+[]]+[+!+[]]+[+[]]+[+[]]+[+[]])' // +"1e1000"
};
const CONSTRUCTORS = {
'Array': '[]',
'Number': '(+[])',
'String': '([]+[])',
'Boolean': '(![])',
'Function': '[]["fill"]',
'RegExp': 'Function("return/"+false+"/")()',
'Object': '[]["entries"]()'
};
const MAPPING = {
'a': '(false+"")[1]',
'b': '([]["entries"]()+"")[2]',
'c': '([]["fill"]+"")[3]',
'd': '(undefined+"")[2]',
'e': '(true+"")[3]',
'f': '(false+"")[0]',
'g': '(false+[0]+String)[20]',
'h': '(+(101))["to"+String["name"]](21)[1]',
……
在這里我們可以看到三個常量,其中的MAPPING確實如我們所想的一樣,是一個字符與代碼的對應,不過對應的值看起來並不是jsf的代碼,並且有些甚至是缺失的,比如:'P': USE_CHAR_CODE。這些字符我們不能如a、b、c那樣輕易的在現有的字符串中找到,需要特殊的處理。
那么jsf的源碼到底是怎么工作的呢,接下來我們就來整體分析一下jsfuck.js文件的代碼。
我們可以把代碼折疊一下,得到下圖:
由上圖可看出,代碼分為三個部分,常量、方法、執行。
MAPPING補全:
我們直接來看第三部分,方法的執行,第一個方法fillMissingDigits,字面意思可以看出是填補缺失的數字,代碼如下:
function fillMissingDigits(){ var output, number, i; for (number = 0; number < 10; number++){ output = "+[]"; if (number > 0){ output = "+!" + output; } for (i = 1; i < number; i++){ output = "+!+[]" + output; } if (number > 1){ output = output.substr(1); } MAPPING[number] = "[" + output + "]"; } }
這段代碼很簡單,從中可以看出,這個方法的功能是填補MAPPING中數字(0 - 9)的鍵值對,如:0: '[+[]]'、1: '[+!+[]]'。(在外面加了一個括號可以方便的轉換為字符串。)
那么很自然就得出下面的fillMissingChars方法的功能是填補MAPPING中缺失的其他字符的鍵值對,下面的一串代碼也比較少,我也把它貼出來:
function fillMissingChars(){ var base16code, escape; for (var key in MAPPING){ if (MAPPING[key] === USE_CHAR_CODE){ //Function('return"\\uXXXX"')() base16code = key.charCodeAt(0).toString(16); escape = ('0000'+base16code).substring(base16code.length).split('').join('+'); MAPPING[key] = 'Function("return"+' + MAPPING['"'] + '+"\\u"+' + escape + '+' + MAPPING['"'] + ')()'; } } }
看代碼可以知道,此方法處理了MAPPING中的所有值為USE_CHAR_CODE的字符,原理比較簡單暴力,直接用到了charCodeAt,由於key是一個字符,就相當於返回了key這個字符的Unicode編碼,並轉換成16進制。我們拿'a'舉例,會得到'61'
接下來的代碼做了三件事:
1. 將得到的16進制數轉換成4位,缺少的位數在左方補0。(例如’61‘ => '0061')
2. 分割字符串
3. 用'+'合並字符串。(例如'0061' => '0+0+6+1')
為什么要這么做呢?下面的代碼我們就能得到答案,下面的代碼是拼接字符串。
我們可以在MAPPING中找到MAPPING['"']的值為:'("")["fontcolor"]()[12]',我們將字符串拼接之后就會得到如下字符串:
'Function("return"+("")["fontcolor"]()[12]+"\\u"+0+0+6+1+("")["fontcolor"]()[12])()'
我們已經知道Function( code )()的作用,它可以將字符串作為可執行代碼執行,那么我們只需要把code部分組合出來就能得到:
"return"+("")["fontcolor"]()[12]+"\\u"+0+0+6+1+("")["fontcolor"]()[12]
這段字符串執行了之后,其實就是:return "\u0061",得到的Unicode字符就是'a'.
到此為止我們已經填充了所有的缺失字符,但是他並不是我們需要的jsf代碼,而是半成品。
替換字符:
從下面兩個方法的名字就能看出來,接下來要做的是把所有字符全部替換成[]()!+這些符號。
我們先來看第一個替換方法:replaceMap。源代碼太長,就不貼了,大家可以看github的jsfuck.js文件的146行。我們可以看到這個方法依然是先定義了三個方法,然后執行了一個循環MIN(32) => MAX(126)細心的同學肯定發現了,這個定義好的范圍正好是ascii表的所有可顯示字符,相當於遍歷了一遍MAPPING的所有鍵值對。
在循環中首先執行了String.fromCharCode方法,將數字轉換成了Unicode字符,相當於MAPPING中的鍵,然后在通過索引拿到對應的鍵中的值。
首先執行了如下代碼:
-
for (key in CONSTRUCTORS){
-
replace( "\\b" + key, CONSTRUCTORS[key] + '["constructor"]');
-
}
我們拿 'A': '(+[]+Array)[10]'舉例,經過調用,最終的執行代碼是這樣的:
newRegExp("Array", "gi"),
'[]["constructor"]'
);
得到的結果是字符串:'(+[]+[]["constructor"])[10]'。
這個替換我們可以理解為,將含有最開始定義的CONSTRUCTORS下的key全部替換掉,替換原因是這些key包含的都是一個個對象,並不能經過簡單的字符串拼接就能得到。這樣我們距離最終的加密代碼又緊了一步。
第二步與第一步意義相同,是將含有SIMPLE的key全部替換掉,這些是可以直接使用的jsf代碼成品。
再接下來的是連着六個replace,分別為:
1. 將兩位以上的數字全部替換。(兩位以上的數字作為Number替換,因為如果我們直接用MAPPING,只會打得到相加的字符串,
例:MAPPING[1] + MAPPING[1] = [+!+[]]+[+!+[]] = '11')
2. 將‘(Number)’替換成MAPPING[key]。
3. 將‘[Number]’替換成MAPPING[key]。
4. 將'GLOBAL'替換成Function("return this")()。
5. 將'+""'替換成+[]。(+""是為了轉換成字符串)
6. 將""替換成[]+[]。(括號中的空字符串)
經過這一輪替換,我們得到的MAPPING離目標更近了一步,得到如下圖的MAPPING。
下一步便是最后一個方法:replaceStrings。這個方法開頭便定義了一個正則表達式:
var regEx = /[^\[\]\(\)\!\+]{1}/g
它匹配了所有的非目標字符( 非[]()!+ ),那么我們就能猜測到,經過這一輪替換,將得到我們的最終map。那么它是如何工作的呢?我們逐行來看。首先定義了一些變量和方法,然后執行了下面一段代碼:
for (all in MAPPING){ MAPPING[all] = MAPPING[all].replace(/\"([^\"]+)\"/gi, mappingReplacer); }
這段代碼替換了MAPPING中的所有帶雙引號的字符串( “fill” => f+i+l+l ),完成之后是一個while循環,這個循環涉及到了兩個方法,我們來一一分析:
function findMissing(){ var all, value, done = false; missing = {}; for (all in MAPPING){ value = MAPPING[all]; if (value.match(regEx)){ missing[all] = value; done = true; } } return done; }
代碼很簡單,循環了MAPPING,如果有值含有非jsf字符,則返回true,一遍循環之后,生成了一個對象missing,里面是MAPPING的所有含有非jsf字符的key。
2. valueReplacer:更簡單,用來替換非jsf字符,如果MAPPING[key]全是jsf字符,則直接替換,不是則不變。
經過一遍又一遍的循環,最終將所有的非jsf字符替換成了jsf字符。最終的MAPPING就誕生了。
不過這里有一個問題,我們可以留意一下作者寫下的一段error提示:
if (count-- === 0){ console.error("Could not compile the following chars:", missing); }
上述循環其實是有可能造成死循環的,如果幾個非jsf代碼的value相互引用,且最終無法轉換成jsf字符,就會造成死循環。其實我們最初看作者定義的常量的時候就能看出,這些被定義的常量都是經過篩選的,不會造成循環引用。
最后一個方法就是暴露出encode方法,很簡單就不做分析了。
(填坑完畢)
拓展:
AAencode:js顏文字加密
Brainfuck:Brainfuck是一種極小化的計算機語言,它是由Urban Müller在1993年創建的。jsfuck就是按照這種思維模式開發出來的。
其他字符編碼還有很多,例如:JJencode,可以從網上搜索看看
圖靈完備:圖靈完備是指機器執行任何其他可編程計算機能夠執行計算的能力。圖靈完備也意味着你的語言可以做到能夠用圖靈機能做到的所有事情,可以解決所有的可計算問題。
出處:https://blog.csdn.net/qq_36539075/article/details/102502775