只用这 6 个字符,就可以写出任意 JavaScript 代码!
你可能在网上见过有人用 几个不同的字符写的各种稀奇古怪的 JavaScript 代码,虽然看起来奇怪,但是能正常运行!比如这个:
(!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+[]]*~+[]]
你猜运行结果是什么?你可以自己去控制台试一下。
看起来很神奇,但这到底是怎么回事呢?
事实上,你几乎可以用下面这 6 个字符写出任意的 JavaScript 程序:
[]()!+
很多人都知道这个技巧,但是没有多少开发人员知道它到底是如何工作的。今天,我们就来看看它背后的执行原理。我们的目标是用这几个字符来写出字符串“self”。姑且用这个字符串向 Self 语言致敬,JavaScript 的灵感来源之一就是它。
基本原理
我们之所以能够抛开其他字符不用,要归功于 JavaScript 的类型系统和数据类型转换机制。
这 6 个字符是这样各显神通的:[]可以用来创建数组,!和+可以在数组上执行一些操作,再用()给这些操作分组。
先看一个简单的数组:
[]
数组前加上!会把它转成布尔值。数组被认为是真值,因此取非之后变成了false:
![] === false
除非转换为类似类型,否则无法将不同类型的值加在一起。JavaScript 在进行转换时遵循一个预定义的规则:
在表达式2 + true中,JavaScript 会将true转成数字,得到表达式2+1。
在表达式2 + "2"中,JavaScript 会将数字转成字符串,得到2 + "2" === "22"。
这些转换规则还不算糟糕,但是对于其他类型,好戏马上来了。
JavaScript 数组强制转换
数组相加会转换成字符串并连接起来。空数组转换为空字符串,因此将两个数组相加将得到空字符串。
[] + [] === "" + "" === ""
数组跟其他类型值相加时也一样:
![] + [] === "false" + "" === "false"
惊不惊喜?我们得到了目标字符串"self"所包含的几个字符!
如果我们能产生一些数字,就可以按正确的顺序提取所需的字符:
"false"[3] === "s"
(![] + [])[3] === "s"
那么,如何生成数字呢?
生成数字
前面提到了,可以把数组转成布尔值。那如果用加号+把它转成数字会怎样?
+[] === ???
JavaScript 会尝试调用数组的valueOf方法,但是发现不存在这个方法,然后就转而调用toString() 方法了。因此上面的代码等效于:
+[] === +""
将字符串转换为数字将产生以下结果:
+"42" === 42
+"esg" == NaN
+"" === 0
空字符串是一个 false值,跟 null,undefined和数字零类似,因此将其中任何一个转换为数字都会变成零:
+null === 0
+undefined === 0
+false === 0
+NaN === 0
+"" === 0
因此,将数组转换为数字需要先将其转换为字符串,最后转成 0:
+[] === +"" === 0
第一个数字已经造出来了!我们还需要更多数字,继续:
!0 === !false
!false === true
!0 === true
将 0 取否就得到一个为真的布尔值。为真的布尔值转成数字,就是1:
+true === 1
有了 1,自然就可以得到2,所谓道生一,一生二,二生三,三生万物……
用上面的转换大法,可以轻松得到我们想要的这些数字:
1 === +true == +(!0) ==== +(!(+[])) === +!+[]
1 === +!+[]
2 === +!+[] +!+[]
3 === +!+[] +!+[] +!+[]
4 === +!+[] +!+[] +!+[] +!+[]
临门一脚,大功告成
总结下这些规则:
- 数组属于真值,取否就得到
false:![] // false - 数组相加时会转换成字符:
[] + [] // "" - 空数组转成数字得到
0,再去否得到true,再转成数字得到1:+(!(+[])) === 1
根据这些规则,我们就能得到想要的字符串。看下面这个示意图就很清楚了:
![] + [] === "false"
+!+[] === 1
(![] + [])[3] + (![] + [])[4] + (![] + [])[2] + (![] + [])[0]
^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^
"false" "false" "false" "false"
^^^^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^^^^^^
s e l f
最终的表达式就是这样:
(![] + [])[+!+[]+!+[]+!+[]] +
(![] + [])[+!+[]+!+[]+!+[]+!+[]] +
(![] + [])[+!+[]+!+[]] +
(![] + [])[+[]]
整理下空格和换行,就是一行代码:
(![]+[])[+!+[]+!+[]+!+[]]+(![]+[])[+!+[]+!+[]+!+[]+!+[]]+(![]+[])[+!+[]+!+[]]+(![]+[])[+[]]
现在你应该明白了那些神奇 JavaScript 代码的原理了吧?发挥你的想象,看还能写出其他什么来?比如,2020 年刚到,来个 “Happy New Year”?
Anyway,Happy New Year!
更多前端技术干货尽在微信公众号:1024译站

只用这 6 个字符,就可以写出任意 JavaScript 代码!的更多相关文章
- 用6个字符写出任意的Javascript代码
博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:用6个字符写出任意的Javascript代码.
- 如何写出无法维护的代码(JAVA版)
程序命名(针对那些不能混淆的代码) 容易输入的名字.比如:Fred,asdf 单字母的变量名.比如:a,b,c, x,y,z,或者干脆上中文比如(阿隆索肯德基) 有创意地拼写错误.比如:SetPint ...
- 写出优质Java代码的4个技巧
我们平时的编程任务不外乎就是将相同的技术套件应用到不同的项目中去,对于大多数情况来说,这些技术都是可以满足目标的.然而,有的项目可能需要用到一些特别的技术,因此工程师们得深入研究,去寻找那些最简单但最 ...
- 写出优质Java代码的4个技巧(转)
http://geek.csdn.net/news/detail/238243 原文:4 More Techniques for Writing Better Java 作者:Justin Alban ...
- 每个人都可以用C语言写的推箱子小游戏!今天你就可以写出属于自己项目~
C语言,作为大多数人的第一门编程语言,重要性不言而喻,很多编程习惯,逻辑方式在此时就已经形成了.这个是我在大一学习 C语言 后写的推箱子小游戏,自己的逻辑能力得到了提升,在这里同大家分享这个推箱子小游 ...
- 如何在word中写出赏心悦目的代码
短学期的VHDL终于结束了,虽然代码并不是很难,但是框框条条的规矩很多,也算折腾了一会,最后要写一个技术手册,结题报告类似物.考虑到word毕竟套主题比较方便,所以也就没有用LaTeX写,但是很快就发 ...
- 突破XSS字符数量限制执行任意JS代码
一.综述 有些XSS漏洞由于字符数量有限制而没法有效的利用,只能弹出一个对话框来YY,本文主要讨论如何突破字符数量的限制进行有效的利用,这里对有效利用的定义是可以不受限制执行任意JS.对于跨站师们来说 ...
- 在 mongodb 终端环境下写多行 javascript 代码、函数
工作中碰到一个问题,需要把某个 collection 中的某些符合条件的数据取出来,逐行处理其中某些字段.mongodb 终端下支持直接写 js 代码.函数,也可以运行 js 文件.1 首先需要设置 ...
- 子查询注意这几点, 就可以写出好的sql语句
执行sql时子查询的语句一般优先执行 理论上多表查询效率是高于子查询(根据子查询不值一个查询语句可能会有多个from但是多表查询只产生一个from), 但是在oracle中子查询效率一般会高于多表查询
随机推荐
- 【Objective-C】-空指针和野指针
一.什么是空指针和野指针 1.空指针 1> 没有存储任何内存地址的指针就称为空指针(NULL指针) 2> 空指针就是被赋值为0的指针,在没有被具体初始化之前,其值为0. 下面两个都是空指针 ...
- Vue.js 第3章 axios&Vue过渡动画
promise 它将我们从回调地狱中解脱出来 创建和使用 var fs = require('fs') // 创建promise // reslove表示执行成功后调用的回调函数 // reject表 ...
- 从DataTable中删除不被控件支持的字段类型
DataTable dt = DB.GetDataTable(sql); //从dt中删除不被控件支持的字段类型 for (int ...
- Libev源码分析01:Libev中的监视器结构(C结构体实现继承)
在Libev的源码中,用到了一种用C实现类似C++中继承的技巧,主要是用宏和结构体实现. 在Libev中,最关键的数据结构就是各种监视器,比如IO监视器,信号监视器等等.这些监视器的多数成员都是一样的 ...
- rcGIS API for JavaScript之基础篇(一)
ArcGIS API for JavaScript之基础篇(一)上一篇文章介绍了ArcGIS 10.4的安装指南也包含了所需要资源,需要的同学可以去公众号中查找.最近几天学习了2D地图.3D地图以及图 ...
- 禁用gpu首选
import osos.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID"os.environ["CUDA_VIS ...
- H3C IP网络的结构
- supersockets单个 listener
在下面的配置中,你可以配置服务器的监听 ip/port: <superSocket> <servers> <server name="TelnetServer& ...
- Python--day30--软件开发架构
软件开发架构: C/S架构: B/S架构: B/S架构和C/S架构的关系:
- python编程之操作系统基础
操作系统发展史: 最早的计算机没有操作系统的概念:是使用打孔机和打孔卡片来进行计算的.只能一个一个处理. 后来产生了批处理系统,可以批量处理,进而加快计算的速度. 批处理系统:串行 数据处理速度加快 ...