# 仅用 6 个字符编写 JavaScript

你可能在网上见过这样的情况,某些人在网上只是用了 6 种不同的字符,这看起来很有趣但却能运行。但是,这些代码具体是怎么工作的呢?

基本上,你可以使用以下字符编写任意的 JavaScript 程序。

[]()!+
1

这是众所周知的技巧,但是很少的开发者知道它具体是如何工作的。今天,我们将要探寻其背后的运行机制。让我们只通过这几个字符来创建一个字符串"self"

我们的目标字符串将是"self",以向编程语言 Self 致敬,因为它是 JavaScript 的灵感之一。

# 它是如何工作的:基础部分

我们可以滥用 JavaScript 的类型系统和奇怪的类型转换机制,这使得我们可以让其他字符串变得多余。

我们将通过以下方法来使用这 6 个字符:通过[]创建数组,通过逻辑非运算符!和加法运算符+,我们可以对齐进行运算,最后通过使用()进行分组。

让我们从一个简单的数组开始:

[]
1

对 array 进行 !操作,将 array 转化为 boolean 值。array 被认为 truthy 。因此,对其进行取反操作得到false

![] === false
1

不同类型的值不能进行相加,除非转换为相似的数据类型。JavaScript 进行类型转换时遵循特定的规则:

  • 在表达式2 + true中,JavaScript 将true转化为 number 类型,从而表达式变为2 + 1
  • 在表达式2 + "2"中,JavaScript 将会将 number 类型转换为 string 类型,从而表达式变为2 + "2" ==== "2 + 2"

这些类型转换机制并不是那么糟糕,但是它马上就会变得有趣。

# JavaScript Array 转换

多个数组进行相加,它们将会转换成字符串并连接在一起。空数组将被转换为空字符串。因此,将两个空数组相加将会得到一个空字符串。

[] + [] === "" + "" === ""
1

当数组与其他数据进行相加操作,会发生同样的类型转换。比如说布尔值。

![] + [] === "false" + "" === "false"
1

是的,我们现在已经创建一个字符串,它包含了目标字符串"self"中的字符。

如果我们能创建一些数字,我们就能通过正确的顺序将字符提取出来。

"flase"[3] === "s"

(![] + [])[3] === "s"
1
2
3

让我们一起寻找数字吧!

# 创建数字

在上一节中,我们将数组转换为了布尔值。我们能否通过+操作符将其转换为数字呢?

+[] ===  ???
1

JavaScript 首先会尝试调用数组的valueOf方法,valueOf将会调用失败然后 JavaScript 尝试调用数组的toString()方法。

+[] === +""
1

字符串转换为数字遵循以下规则:

+"42" === 42
+"esg" === NaN
+"" === 0
1
2
3

空字符串是一个 falsy 值,就像nullundefined和 0,因此将其中任意一个转换为数字都会得到 0。

+null === 0
+undefined === 0
+false === 0
+NaN === 0
+"" === 0
1
2
3
4
5

因此,将空数组转换为数字,首先会将其转换为字符串,最后得到数字 0。

+[] === +"" === 0
1

是的,我们已经掌握了如何创建数字!虽然不是很有用。但是,我们可以继续进行转换的游戏。

!0 === !false
!false === true

!0 === true
1
2
3
4

对 0 进行取反操作后将 falsey 值 0 转换为了布尔值。一个 truthy 布尔值将会转换为数字 1。

+true === 1
1

万岁,两个上述类型转换为两个 1,这样我们就可以得到 2 了。

通过我们已经学到的替换来创建数字:

1 === +true === +(!0) === +(![]) === +!+[]

1 === +!+[]
2 === +!+[] + +!+[]
3 === +!+[] + +!+[] + +!+[]
4 === +!+[] + +!+[] + +!+[] + +!+[]
1
2
3
4
5
6

# 融会贯通

让我们把所学过的东西放在一起。

  • 空数组是 truthy 值,因此对其取反,将会得到 false:![] //flase
  • JavaScript 转换规则表明,两个数组进行相加,会分别对数组使用 toString 方法:[] +[] // ""
  • 将空数组转换为数字 0,当对其取反得到 true, 在将其转换为数字获得 1:+(!(+[])) === 1
![] + [] === "false"
+!+[] === 1
(![] + [])[3] + (![] + [])[4] + (![] + [])[2] + (![] + [])[0]
 ^^^^^^^^        ^^^^^^^^        ^^^^^^^^        ^^^^^^^^
  "false"         "false"         "false"         "false"
  
^^^^^^^^^^^^^   ^^^^^^^^^^^^^   ^^^^^^^^^^^^^^	^^^^^^^^^^^^^^
   "false"         "false"         "false"         "false"
1
2
3
4
5
6
7
8

因此我们最后的表达式为:

(![] + [])[+!+[] + !+[] + !+[]] +
(![] + [])[+!+[] + !+[] + !+[] + !+[]] +
(![] + [])[+!+[] + !+[]] +
(![] + [])[+[]]
1
2
3
4

最后将我们的表达式适用于 JavaScript 这个特定的语言,当然空格和换行是被禁止的:

(![]+[])[+!+[]+!+[]+!+[]]+(![]+[])[+!+[]+!+[]+!+[]+!+[]]+(![]+[])[+!+[]+!+[]]+(![]+[])[+[]]
1

简直不费吹灰之力!

谢谢你,Brendan Eich ❤️