• 开往
  • 设置
  • 用户
  • 回到顶部
  • 收起
  • JS,但是禁用字母和数字

    • ? 阅读
    • 948 字
    上一章下一章

    自限规则

    在源码中不使用任何字母和数字,在控制台直接输出任意字母与数字组成的字符串。

    实现思路

    利用 JS 神秘的隐式类型转换,我们可以首先得到这样一些打印结果:

    js
    1
    2
    3
    4
    5
    6
    7
    8
    ![] + ![]
    // 0
    
    ![] + !![]
    // 1
    
    {} + [] + ![] + !![]
    // [object Object]falsetrue

    在有了 0 和 1 的情况下,可以得到任意大小的数字;而对通过上述方式得到的字符串进行下标索引,则可以得到有限数量的字母。那么仅仅凭借这些,如何获取所有 26 个字母呢?

    在这之前,先将数字以等比数列的方式存入变量中:

    js
    1
    2
    3
    4
    5
    6
    _ = { _: ![] + !![] };     // 1
    _.__ = _._ + _._;          // 2
    _.___ = _.__ * _.__;       // 4
    _.____ = _.___ * _.__;     // 8
    _._____ = _.____ * _.__;   // 16
    _.______ = _._____ * _.__; // 32

    我们成功构造了一个以 2 为公比,以 1 为首项的等比数列!之后便可以自由组合这些子属性,得到任意大小的数字了。

    此外,上面得到的字符串也自然要存入变量中。然而仅仅这些是不够的,我们还需要一个 undefined,它包含了一个至关重要的字母 n

    js
    1
    2
    // [object Object]falsetrueundefined
    $_ = {} + [] + ![] + !![] + _[_];

    这时就可以开始着手最关键的步骤了。我们先将字符串 constructor 组合出来:

    js
    1
    2
    // constructor
    $_$ = $_[_.___+_._] + $_[_._] + $_[_.______-_.__] + $_[_._____+_.__] + $_[_.___+_.__] + $_[_._____+_.___+_._] + $_[_._____+_.____] + $_[_.___+_._] + $_[_.___+_.__] + $_[_._] + $_[_._____+_.___+_._];

    为什么是 constructor?考察这样一段代码:

    js
    1
    2
    3
    4
    5
    6
    7
    8
    "" + (0).constructor
    // function Number() { [native code] }
    
    "".constructor.name
    // String
    
    (() => {}).constructor
    // ƒ Function() { [native code] }

    注意到了吗?我们竟然同时得到了 String 字符串和 Function 构造器!而 name 属性中的字母 m,便需要通过第一条代码得到:

    js
    1
    2
    // name
    _$_ = $_[_.______-_.__] + $_[_._____] + ("" + _._[$_$])[_.____+_.__+_._] + $_[_.___];

    于是一路畅通无阻,直接拼接出 toString 方法:

    js
    1
    2
    // toString
    $__ = $_[_.___+_.__] + $_[_._] + ("" + ""[$_$][_$_]);

    Tip:为了保持页面的简洁,不再给出复杂代码的混沌版本。

    Number.prototype.toString() 方法可以通过传入一个整数来指定数字的基数,对于获取小写英文字母,只需要将其按照 36 进制转换即可:

    js
    1
    2
    3
    4
    5
    6
    $ = _ => (_ + 9).toString(36);
    
    $(_._);          // a
    $(_.__+_._);     // c
    $(_.____-_._);   // g
    $(_._____-_.__); // n

    字母是能够得到了,但如何将它们输出到控制台呢?想要调用控制台,必须直接获取顶层对象,而这是通过从局部变量出发的方式无论如何都获取不到的。在当前环境下,任何可获取对象的原型属性都不指向 globalThis,我们无法再根据以往的写法按部就班——

    于是,前面得到的 Function 构造器就派上用场了。学过 JS 的都知道,eval() 方法能够直接将传入的字符串作为代码运行。而我们虽然不能得到 eval(),却有着几乎拥有同样功能的 Function()

    js
    1
    (() => {}).constructor("return console")()

    看,这不就得到 console 了吗?当然,我们可以更进一步直接得到 windowglobal 对象。

    至于大写字母,同样可以利用 Function 构造器:

    js
    1
    2
    3
    4
    5
    6
    $$ = _ => (() => {}).constructor(`return "${"\\x" + (_ + 0x40).toString(16)}"`)();
    
    $$(_._);          // A
    $$(_.__+_._);     // C
    $$(_.____-_._);   // G
    $$(_._____-_.__); // N

    至此,我们不仅能够输出大小写字母和数字,还获得了一个 API 完全的纯符号框架,它能够在不输入字母与数字的同时实现 JS 的大部分功能!

    评论0

    60FPS