A Javascript journey with only six characters

2016年11月04日Web前端

偶然发现的一片关于JS中使用六个字符串,就能实现js代码的文章。英文原版后面对应的是翻译。

原文链接:http://jazcash.com/a-javascript-journey-with-only-six-characters/

Javascript is a weird and wonderful language that lets us write some crazy code that's still valid. It tries to help us out by converting things to particular types based on how we treat them. If we add a string to something, it'll assume we want it in text form, so it'll convert it to a string for us. If we add a plus or minus prefix to something, it'll assume we want its numerical representation, and will convert the string to a number for us, if possible. If we negate something it will convert it to a boolean. We can exploit Javascript's help and use it to do some magical things using only the characters: [,],(,),! and +. If you're not on mobile, you can pop open your browser's JS console to follow along. You can just paste any of the code examples into the console and they should evaluate to true. Let's start with the basics. Some golden rules to remember:

  1. Preceding with ! converts to Boolean
  2. Preceding with + converts to Number
  3. Adding [] converts to String

Here they are in action:

![] === false
+[] === 0
[]+[] === ""

Another thing you should know is that it's possible to retrieve specific letters from strings using square brackets like so:

"hello"[0] === "h"

Also remember it's possible to make multiple digit numbers by adding the string representations together, then converting the whole thing back to a number:

+("1" + "1") === 11

Right, so let's go ahead and combine some stuff together to get the letter a.

![] === false
![]+[] === "false"
+!![] === 1
------------------------
(![]+[])[+!![]] === "a"  // same as "false"[1]

Neat! So, with some relatively simple combinations we can get out all the letters from the words true and false. a,e,f,l,r,s,t,u. Ok, can we get letters from anywhere else? Well, there's undefined, which we can get by doing something silly like [][[]]. Converting it to a string using our golden rule gives us the extra letters d,iand n.

[][[]] + [] === "undefined"

With all the letters we have so far, we can spell fill, filter and find. Sure there are some other words we can spell too, but what's important about these words is that they're all Array methods. This means they are part of the Arrayobject and can be called directly on array instances. E.g. [2,1].sort(). Now, another important thing to know about Javascript is that an object's properties can be accessed with dot notation or square bracket notation. As the array methods above are all properties of the Array object itself, we can call those methods using the square bracket notation instead of dot notation. So [2,1]["sort"]() is the same as [2,1].sort(). Let's go ahead and see what happens when we try to use one of our array methods, that we can spell using our collection of letters so far, but without invoking it.

[]["fill"]

This produces function fill() { [native code] }. We can convert this method header to a string using our golden rule again:

[]["fill"]+[] === "function fill() { [native code] }"

So now we have acquired the following extra characters: c,o,v,(,),{,[,],},. With our newly found c and o we can now form the word constructor. constructor is a method that all JS objects have which simply returns their constructor function. Let's get the string representation of the constructor functions for all the objects we've dealt with so far:

true["constructor"] + [] === "function Boolean() { [native code] }"
0["constructor"] + [] === "function Number() { [native code] }" 
""["constructor"] + [] === "function String() { [native code] }"
[]["constructor"] + [] === "function Array() { [native code] }" 

From these, we can add the following characters to our arsenal: B,N,S,A,m,g,y. We can now craft "toString", which is a function we can use with the square bracket notation. Except this time we're going to actually invoke it.

(10)["toString"]() === "10"

But we can already convert anything we want to a string using our golden rule anyway, so how can this be useful? Well, what if I told you that the toString method of the Number type has a secret argument called radix that changes the base of the returned number before converting it to a string. Watch this:

(12)["toString"](10) === "12" // base 10 - normal to us
(12)["toString"](2) === "1100" // base 2, or binary, for 12
(12)["toString"](8) === "14" // base 8 (octonary) for 12
(12)["toString"](16) === "c" // hex for 12

But why stop at 16? The maximum is 36, which encompasses all characters from 0-9 and a-z. So now we can get any alphanumeric character we want:

(10)["toString"](36) === "a"
(35)["toString"](36) === "z"

Awesome! But what about other characters like punctuation and capital letters? Further down the rabbit hole we go! Depending on where your JS is executing, it may or may not have access to particular pre-defined objects or data. If you're running it in the browser, chances are you can access some legacy HTML wrapper methods. For example, bold is a String method that wraps a string in <b> tags.

"test"["bold"]() === "<b>test</b>"

This gets us the characters <, > and /. You may have heard of the escape function. It basically converts a string into a URI-friendly format that simple browsers can interpret. This function is a vital part of our quest, so we need access to it. We can spell it, but how can we actually execute it? It's not a function that belongs to any of the types we've dealt with so far, it's a global level function. What's the constructor of any function? The answer is function Function() { [native code] }, the actual Function object itself.

[]["fill"]["constructor"] === Function

Using this, we can pass a string of code to actually create a function.

Function("alert('test')");

Produces:

Function anonymous() { 
    alert('test')
}

Which we can invoke immediately to execute the alert by just popping () on the end. Yep, that's right, we can actually execute strings of code now! So, now we use our escape function like this:

[]["fill"]["constructor"]("return escape(' ')")() === "%20"

If we pass our < from earlier to the escape function, we get %3C. That capital Cis very important if we want to get the rest of the characters that we're missing.

[]["fill"]["constructor"]("return escape('<')")()[2] === "C"

Using it, we can now write the fromCharCode function, which returns a Unicode character from a given decimal representation. It is part of the String object, which we can get by calling any string's constructor like we did before.

""["constructor"]["fromCharCode"](65) === "A"
""["constructor"]["fromCharCode"](46) === "."

We can use a Unicode lookup to easily find the decimal representation for any character. PHEW. We're done! We now have access to any and every character, can concatenate them together to form code, and can execute it too. That means we're Turing complete in Javascript using only the six characters [,],(,),+ and !. Want proof? Run this code in your console: Here's a tool that can automate the conversion for you, and here's how it translates each character.


翻译链接:http://www.zcfy.cc/article/a-javascript-journey-with-only-six-characters-1370.html

JavaScript 是一个奇怪而有趣的语言,我们可以写一些疯狂却仍然有效的代码。它试图帮助我们把事情转换到基于我们如何对待他们的特定类型。 如果我们添加一个字符串,JavaScript会假定我们希望为文本形式表示,所以将它转换为一个字符串。如果我们添加一个正负前缀符号,JavaScript会假定我们希望为数值形式表示,如果可能的话,对我们来说并将字符串转换为一个数字。如果我们添加一个否定符号,JavaScript会将将字符串转换为一个布尔值。

我们可以使用Javascript中[,],(,),! and +这六个符号写一些神奇的代码。如果你现在不是在手机,你可以打开浏览器的控制台,你可以将任何代码示例粘贴到控制台,并且代码值为true。 让我们从最基本的开始,要记住一些黄金规则: !后面跟的字符会被转换成布尔值 +后面跟的字符会被转换成数值 []后面跟的字符会被转换成字符串 来看下面的例子:

![] === false
+[] === 0
[]+[] === ""

另一件事你应该知道的是,它可以从字符串使用方括号检索特定的字母,像这样:

"hello"[0] === "h"

还记得可以使多个数字号码通过添加字符串表示在一起,然后把整个表达式转换成一个数字:

+("1" + "1") === 11

我们们继续把一些东西结合在一起得到字母a

![] === false
![]+[] === "false"
+!![] === 1
------------------------
(![]+[])[+!![]] === "a"  // same as "false"[1]

举一反三! 我们可以通过truefalse得到相似的字母a,e,f,l,r,s,t,u,那么我们可以从其他地方得到的字母吗?

我们可以通过一些特别的式子如[][[]]得到undefined,利用我们上面讲到的黄金法则得到另外的字母d,in

[][[]] + [] === "undefined"

到目前为止,利用我们已经获得的所有字母,我们可以拼fill, filterfind。当然也有一些其他的单词,我们也可以拼写,但这些单词最重要的是,他们都是数组的方法。这意味着他们是数组对象的一部分,可以直接调用数组实例,如:[2,1].sort()

现在,了解JavaScript的另一件重要的特性是一个对象的属性可以通过点符号.或方括号[]访问。上述数组方法是数组对象本身的属性,我们可以使用方括号代替点符号调用这些方法。

所以[2,1]["sort"]() 等效于 [2,1].sort(). 我们继续看看,当我们试图使用一个数组的方法会发生什么,我们可以使用到目前为止我们拼写的但没有调用的字母。

[]["fill"]

这会得到function fill() { [native code] },我们可以把这个方法头作为一个字符串再次使用我们的黄金法则:

[]["fill"]+[] === "function fill() { [native code] }"

所以现在我们又得到其他的字符:c,o,v,(,),{,[,],}

随着我们新得到的co,我们现在可以形成constructor这个单词。构造函数是一个方法,所有JS对象仅返回自己的构造函数。

到目前为止我们已经处理的对象,我们可以得到它用字符串表示的构造器函数:

true["constructor"] + [] === "function Boolean() { [native code] }"  
0["constructor"] + []    === "function Number() { [native code] }"  
""["constructor"] + []   === "function String() { [native code] }"
[]["constructor"] + []   === "function Array() { [native code] }"
({})["constructor"] + [] === "function Object() { [native code] }"

通过这些式子,我们可以将下面的字符加入到我们的库中:B,N,S,A,O,m,b,g,y,j。 现在我们可以构造一个我们可以使用方括号的函数"toString"`,我们可以这样调用:

(10)["toString"]() === "10"

使用我们的黄金法则,我们已经可以将任何我们想要转换成一个字符串,但是上面这个式子怎么用呢? 好吧,我告诉你,Number类型的toString方法有一个称为radix(“基数”)的秘密的论点。它可以将数值在转换为一个字符串之前先经过基数换算,像这样:

(12)["toString"](10) === "12"  // 十进制
(12)["toString"](2) === "1100" // 二进制
(12)["toString"](8) === "14"   // 八进制
(12)["toString"](16) === "c"   // 十六进制
``
但是为什么基数只写到16最大值是36包括所有的字符`0`-`9`  `a`-`z`所以现在我们可以得到任何我们想要的字母数字
```js
(10)["toString"](36) === "a"
(35)["toString"](36) === "z"

太棒了!但是其它符号如标点符号和大写字母呢?我们接着深入探索。 这取决于你的JS执行时,它可能会或可能不会访问特定的预定义的对象或数据。如果你在浏览器中运行它,那么你可以访问一些存在的HTML包装器方法。 例如,bold是一个包装在<>标签中的字符串方法。

"test"["bold"]() === "<b>test</b>"

通过这个我们得到<>/两个字符。 你可能听说过escape方法,它主要将字符串转换为一个URI友好的格式,可以让简单的浏览器解释。如果我们传递一个空格字符,我们得到的"%20"。 这里有一个工具可以自动将每个字符自动转换。

工具地址:http://www.jsfuck.com/

源代码地址:https://raw.githubusercontent.com/aemkei/jsfuck/master/jsfuck.js

目录