从hello world看JavaScript隐藏的黑魔法

写在最前

事情的起因是这段看起来不像代码的代码:

_20170825232240

有兴趣的同学可以自己先尝试下!

([]+[][(![]((!![]+[])[+!![]((!![]+[])[+!![]((!![]+[])[+!![]((!![]+[])[+!![]((!![]+[])[+!![]((!![]+[])[+!![]((!![]+[])[+!![]((!![]+[])[+!![]((!![]+[])[+!![]]+([][[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+([]+{})[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][[]]+[])[!+[]+!![]+!![]+!![]+!![]]+([]+{})[+!![]]+([][[]]+[])[+!![]])())[!+[]+!![]+!![]]+([][[]]+[])[!+[]+!![]+!![]])()(([]+{})[+[]])[+[]]+(!+[]+!![]+[])+(+!![]+[]))

作者对着这段代码足足看了一下午,我只想说这不是什么深奥的黑魔法。一点点看下来你就知道其中的原理了。最后会有一段作者自己封装的代码叫nb.js(源码在这里),它实现了输入数字字母后自动生成这种玄学代码片段。就像这样:

_20170825233827

欢迎关注我的博客,不定期更新中——

JavaScript小众系列开始更新啦

——何时完结不确定,写多少看我会多少!这是已经更新的地址:

这个系列旨在对一些人们不常用遇到的知识点,以及可能常用到但不曾深入了解的部分做一个重新梳理,虽然可能有些部分看起来没有什么用,因为平时开发真的用不到!但个人认为糟粕也好精华也罢里面全部蕴藏着JS一些偏本质的东西或者说底层规范,如果能适当避开舒适区来看这些小细节,也许对自己也会有些帮助~文章更新在我的博客,欢迎不定期关注。

转换思路

基础思路:通过关键字来获取字母

什么意思?比如:f。看到f你会想到哪个关键字?同时这个关键字是要在类型转换的机制下能够被打印出来的。如果类型转换你还不是很了解,可以先读下这篇来理解一下:从[] == ![]看隐式强制转换机制。我相信很多同学可以想到是false这个关键字。那么我们的思路就有了也就是要让代码实现'false'[0]这件事,这个认识统一之后我相信下面的代码一定不难理解了:

[[[] == []] + []][+![]][+![]]
//过程理解为
[] == [] => false
[[] == []] => [false]
[[[] == []] + []] => ['false'], [+![]] => 0
[[[] == []] + []][+![]] => 'false'
[[[] == []] + []][+![]][+![]] => 'false'[0] => 'f'

其中大体形式可以理解为:['false'][0][0] => 'f'

是不是瞬间觉得也不过如此?

可通过关键字获取的字符

当你知道可以用上面的方式来获取自己需要的字母之后,接下来要做的是思考一下你能从关键字中获取哪些字母呢,作者总结了以下你可以通过关键字获得的字母:

([][[]]+[]) => 'undefined'
+[1+[[][0]+[]][0][3]+400][0]+[] => 'Infinity'
[[[] == []] + []][+![]] => 'false'
[[[] != []] + []][+![]] => 'true'
([]+{}) => "[object Object]"

感兴趣的同学自己打印下就明白为什么了。

接下来要说的是剩下的字母怎么办?当然了你仍然可以通过试图寻找关键字的方式来获取字母。但是如果标点我也想要呢?或者说26个字母我都想要怎么办?
具体点来说对于“hello world!”这段字符串来看,至少“w”,"!"的获取方法通过关键字的形式我们是无从下手的。

unescape

unescape() 函数可对通过 escape() 编码的字符串进行解码。但是已经废弃了

是的现在已经不建议如此使用了,但是浏览器下基本还是支持这个函数的。通过这个函数我们可以通过ascll码来直接得到我们需要的字符:

unescape('%77') => 'w'

如此看来,除了我们可以快速得得到一些关键字字母外,用这个方法我们便可以实现任意字母的组合。而作者封装的nb.js也是基于这两者来实现输出黑魔法字符串的。

那么现在的问题是如果通过字符串来执行unescape('%77')这段代码?

来看下hello world那段代码是如何实现的:


在这里也不绕弯子了,作者打印了很多次之后才发现是如此调用的:

[]['sort']['constructor']('return unescape')

因为JS调用方法不光是“.”调用,通过[]也是可以调用的。同时通过return unescape,返回了一个匿名函数形成了闭包。故调用的时候采用如下方式:

[]['sort']['constructor']('return unescape')()('%77') => 'w'

至于为什么这段代码写出来如此长是因为上面的每一个字母都是一点点拼出来的,也行好上面通过关键字的方式可以得到这些字母=。=不然的话——

封装nb.js

所以经过上面的分析你会发现,除了字符串长度感人之外,这种通过拼接字符串可以返回函数并且执行的方式还真是蛮炫酷的。为了达到装逼的效果。作者决定封装一个支持字母和数字的函数,当你传入普通的字符串之后,会返回带有黑魔法气息的冗长字符串,尽情拿去装x吧,不客气~

封装过程

维护基础对象与ascll表对象

var baseAlibrary = {
    'a': '[[[] == []][0]+[]][0][1]',
    'b': null,
    'c': '[[][[[][0] + []][0][4]+[[][0] + []][0][5]+[[][0] + []][0][1]+[[][0] + []][0][2]]+[]][0][3]',
    'd': '([][[]]+[])[+!![]+!![]]',
    'e': '([][[]]+[])[+!![]+!![]+!![]]',
    'f': '([][[]]+[])[+!![]+!![]+!![]+!![]]',
    'g': null,
    ...
    '0': '(+![])',
    '1': '(+!![])',
    '2': '(+!![]+!![])',
    ...
    ',': null,
    '!': null,
    }
    var ascll = { //ascll表可自行配置, 新添加后需要在上面对象中配置相同key,只是value为null
        'A': '41',
        'B': '42',
        ...
    }

将简单的字母转换方式直接存储下来,如果需要的字符无法从基础对象获取,就记为null,并在ascll表中写入相关转码方式。

封装unesacpe

var result = ''
    var unescapeStr = '[][(![]((!![]+[])[+!![]((!![]+[])[+!![]]+([][[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+([]+{})[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][[]]+[])[!+[]+!![]+!![]+!![]+!![]]+([]+{})[+!![]]+([][[]]+[])[+!![]])())[!+[]+!![]+!![]]+([][[]]+[])[!+[]+!![]+!![]])'
    //将[]['sort']['constructor']('return unescape')的黑魔法形式存储起来之后直接调用
    function changeAscll(ascllItem) {
        var ascllResult = ''
        var middleValue = ''
        ascll[ascllItem].split('').forEach(function(item) {
            if(isNaN(item)) { //ascll中遇到字母则需要再次进行unescape转码
                var str = ''
                ascll[item].split('').forEach(function(data) {
                    str += '+[' + baseAlibrary[data] + ']'
                })
                middleValue += '+' + unescapeStr + '()('+ baseAlibrary['%']+'+' +  str.slice(1) + ')'
            } else {
                middleValue += '+[' + baseAlibrary[item] + ']'
            }   
        })
        ascllResult += '+' + unescapeStr + '()('+ baseAlibrary['%']+'+' +  middleValue.slice(1) + ')'
        return ascllResult
    }
    function getUnEscape(str) {

    }
    strArr.forEach(function(item) {
        Object.keys(baseAlibrary).forEach(function(obj, i) {
            if(item.toLocaleLowerCase() === obj) {
                if(!baseAlibrary[item]) {
                    Object.keys(ascll).forEach(function(ascllItem) {
                        if(obj === ascllItem) {
                            var cbValue = changeAscll(ascllItem).slice(1)
                            result += '+' + cbValue
                        }
                    })
                } else {
                    result += '+' + baseAlibrary[obj]
                }
            }
        })
    })
    console.log(result.slice(1))

效果

也就是一开始大家看到的:


_20170825233827

作者将函数绑定在了this上,通过this.reallyNb()即可得到你想要的~

PS:代码请部署在服务器中再打开页面,否则个别字母通过location方法会取不到,主要就是t,p。不过这个问题之后作者会将其以ascll表的方式存储,就没有环境限制了。只是作者嫌弃那样做打印的字符串太长了~

最后

不定时更新中——
有问题欢迎在issues下交流。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容