美文网首页Web前端之路让前端飞JavaScript 进阶营
从hello world看JavaScript隐藏的黑魔法

从hello world看JavaScript隐藏的黑魔法

作者: Annnnnn | 来源:发表于2017-09-05 10:09 被阅读57次

    写在最前

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

    _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下交流。

    相关文章

      网友评论

        本文标题:从hello world看JavaScript隐藏的黑魔法

        本文链接:https://www.haomeiwen.com/subject/svcujxtx.html