写本篇文章的缘由是之前群里@墨尘发了一段js代码,如下:
  (!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+[]]*~+[]]
  然后让大家运行,出来的结果让人有点出乎意料,请看:
  
  太风骚了有木有!如果有人诋毁前端瞧不起js的话,那就可以把这段代码发给他了~
  不过话说回来了,这到底是什么原理呢?为什么一堆符号运算结果竟然能是两个字符,而且恰巧还是个sb!
  其实靠的是js的类型转化的一些基本原理,本篇就来揭密"sb"是如何炼成的。相信你如果能把这个理清楚了,以后遇到类型转化之类的题目,就可以瞬间秒杀了。
 
  首先要运用到的第一个知识就是js运算符的优先级,因为这么长一段运算看的人眼花,我们必须得先根据优先级分成n小段,然后再各个击破。优先级的排列如下表:
  优先级从高到低:
运算符

说明

.[ ] ( )

字段访问、数组索引、函数调用和表达式分组

++ -- - ~ ! delete new typeof void

一元运算符、返回数据类型、对象创建、未定义的值

* / %

相乘、相除、求余数

+ - +

相加、相减、字符串串联

<< >> >>>

移位

< <= > >= instanceof

小于、小于或等于、大于、大于或等于、是否为特定类的实例

== != === !==

相等、不相等、全等,不全等

&

按位“与”

^

按位“异或”

|

按位“或”

&&

逻辑“与”

||

逻辑“或”

?:

条件运算

OP=

赋值、赋值运算(如 += 和 &=)

,

多个计算

  根据此规则,我们把这一串运算分为以下16个子表达式:

  
  运算符用红色标出,有一点可能大家会意识不到,其实中括号[]也是一个运算符,用来通过索引访问数组项,另外也可以访问字符串的子字符,有点类似charAt方法,如:'abcd'[1] // 返回'b'。而且中括号的优先级还是最高的哦。
 
  预处理结束,接下来需要运用的就是javascript的类型转化知识了。我们先说说什么情况下需要进行类型转化。当操作符两边的操作数类型不一致或者不是基本类型(也叫原始类型)时,需要进行类型转化。先按运算符来分一下类:
  • 减号-,乘号*,肯定是进行数学运算,所以操作数需转化为number类型。
  • 加号+,可能是字符串拼接,也可能是数学运算,所以可能会转化为number或string
  • 一元运算,如+[],只有一个操作数的,转化为number类型
  下面来看一下转化规则。
  1. 对于非原始类型的,通过ToPrimitive() 将值转换成原始类型:
  ToPrimitive(input, PreferredType?)
  可选参数PreferredType是Number或者是String。返回值为任何原始值.如果PreferredType是Number,执行顺序如下:(参考:http://es5.github.io/#x9.1
  如果input为primitive,返回
  否则,input为Object。调用 obj.valueOf()。如果结果是primitive,返回。
  否则,调用obj.toString(). 如果结果是primitive,返回
  否则,抛出TypeError
  如果 PreferredType是String,步骤2跟3互换,如果PreferredType没有,Date实例被设置成String,其他都是Number
 
  2. 通过ToNumber()把值转换成Number,直接看ECMA 9.3的表格http://es5.github.io/#x9.3
  规则如下:
参数 结果
undefined NaN
null +0
boolean true被转换为1,false转换为+0
number 无需转换
string 由字符串解析为数字。例如,"324"被转换为324

  3. 通过ToString()把值转化成字符串, 直接看ECMA 9.8的表格http://es5.github.io/#x9.8

  规则如下:
参数 结果
undefined "undefined"
null "null"
boolean "true" 或者 "false"
number 数字作为字符串。比如,"1.765"
string 无需转换
  规则就这么多,接下来实践一下,根据我们上面划分出的子表达式,一步一步将这个神奇的代码给执行出来。开工~
  

  先看最简单的子表达式16:+[]
  只有一个操作数[],肯定是转化为number了,根据上面的规则2,[]是个数组,object类型,即对象。所以得先调用toPrimitive转化为原始类型,并且PreferredType为number,这个参数表示更“倾向于”转化的类型,这里肯定是number了。然后首先调用数组的valueOf方法,数组调用valueOf会返回自身,如下:
   
  这个时候,我们得到一个空串“”,还没有结束,看上面的规则2描述,继续调用toNumber,转化为number类型,如下:
  
  大功告成!子表达式16转化完毕,+[],最终得到0。
 
  来看子表达式15:[~+""]
  空串""前面有两个一元操作符,但是操作数还是只有一个,所以,最终要转化为的类型是number。看规则2吧,空串调用toNumber得到0。接下来是~,这是个什么东东呢?它是位运算符,作用可以记为把数字取负然后减一,所以~0就是-1 。
  别忘了,这个子表达式外头还包着中括号,所以最终的值为[-1],即一个数组,里面只有一个元素-1.
 
  接下来看子表达式13就简单了,把15、16求出来的填进去,就变成了这样:--[-1][0],取数组的第0个元素,然后自减,结果为-2,是不so easy!
 
  继续往上走,子表达式14: [~+[]]
  其实把15、和16的原理用上就非常明显了,答案[-1]
 
  继续来求子表达式9,此刻它已变成:-2*[-1],有稍许不一样,不过没关系,我们还是按照规则来,运算符是乘号*,当然是做数学运算,那后面的[-1]就得转化为number,与16的求法类似,过程如下:
  ①调用toPrimitive,发现是object类型
  ②调用valueOf,返回自身[-1]
  ③因为不是原始类型,继续调用toString,返回"-1"
  ④"-1"是原始类型了,然后调用toNumber,返回-1
  ⑤与-2相乘,返回2
 
  子表达式10:~~!+[],不多说了,答案1. 就是从右往左依次一元计算。
 
  有了9和10,我们来到了子表达式4,此刻它已经长这样了:2+1, 好,我不多说了。
 
  继续看表达式7:!(~+[]),~+[]=-1,这个根据上面已经知道了,那!-1是什么呢?这里要说一下这个感叹号,它是逻辑取非的意思,会把表达式转化为布尔类型,转化规则和js的Truthy和Falsy原则是一样的,后面跟数字的,除0以外都为false,后面跟字符串的,除空串以外都为false。这里的!-1当然就是false了。
 
  接下来这个表达式3:false+{}有点关键。一个布尔加一个对象,那这个{}应该先转化为原始类型,流程如下:
  ①调用toPrimitive,发现是object类型
  ②调用valueOf,返回自身{},
  ③不是原始类型,调用toString,返回"[object Object]"
  ④false与"[object Object]"相加,false先转化为字符串"false"
  ⑤相加得结果"false[object Object]"
 
  知道了表达式3和4,我们就可以来看表达式1了,此时它是这样的:"false[object Object]"[3],因为这个[]可以取字符串的子字符,像charAt一样,所以得到了结果"s"
 
  经过上面艰难的流程,我们拿到了字符"s",也就是那张图的左半边,剩下的那个"b",相同的原理可以搞出来,我这里就不一一演示了,留给你练练吧~
     
  回顾一下这个过程其实也不复杂,只是有一些需要重复劳动的,只要你掌握了运算的优先级,能把大串分解成一个个小串,然后运用类型转化的知识挨个处理就搞定了。怎么样,看到这里你还觉得神奇吗?
  如果有人瞧不起js,请把这段代码发给他,如果他想知道答案,请把本文发给他~
 

一行神奇的javascript代码的更多相关文章

  1. 一行神奇的 javascript 代码

    写本篇文章的缘由是之前群里@墨尘发了一段js代码,如下: (!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+[]]*~ ...

  2. 一行能装逼的JavaScript代码

    一行能装逼的JavaScript代码 2016-06-28 野狗 一行神奇的JS代码,当时我就震 惊了,这不就是传说中的ZB神奇么… … 哈哈. 写本篇文章的缘由是之前看到了一段js代码,如下: (! ...

  3. 翻译 | 一行 JavaScript 代码的逆向工程

    原文地址:Reverse Engineering One Line of JavaScript 原文作者:Alex Kras 译者:李波 校对者:冬青.小萝卜 几个月前,我看到一个邮件问:有没有人可以 ...

  4. 一行能装逼的JavaScript代码的延伸

    前段就是坑,入坑水真深. 先看看一个黑科技, 纳尼,这是什么东西. (!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+ ...

  5. 只用这 6 个字符,就可以写出任意 JavaScript 代码!

    你可能在网上见过有人用 几个不同的字符写的各种稀奇古怪的 JavaScript 代码,虽然看起来奇怪,但是能正常运行!比如这个: (!(~+[])+{})[--[~+""][+[] ...

  6. FineUI(专业版)实现百变通知框(无JavaScript代码)!

    博客园已经越来越不公正了,居然说我这篇文章没有实质的内容!! 我其实真的想问哪些通篇几十个字,没任何代码和技术分享,嚷嚷着送书的文章的就能雄霸博客园首页几天,我这篇文章偏偏就为管理员所容不下. 其实我 ...

  7. 利用chrome调试JavaScript代码

    看见网上很多人问怎么用chrome调试JavaScript代码,我也对这个问题抱着疑问,但是没有找到一篇能用的中文文章(可能我的google有问题),也不知道怎么点出一篇E文的,感觉作者写得不错,所以 ...

  8. 深入理解JavaScript系列(1):编写高质量JavaScript代码的基本要点

    深入理解JavaScript系列(1):编写高质量JavaScript代码的基本要点 2011-12-28 23:00 by 汤姆大叔, 139489 阅读, 119 评论, 收藏, 编辑 才华横溢的 ...

  9. javascript 代码可读性

    可读性的大部分内容都是和代码缩进相关的,必须保证代码有良好的格式.可读性的另一方面就是注释,一般而言,有如下一些地方需要进行注释 1.1.1 函数和方法 每个函数或方法都应该包含一个注释,描述其目的和 ...

随机推荐

  1. python list dict 去重的两种方式

    def dedupe(items, key=None): seen = set() for item in items: val = item if key is None else key(item ...

  2. 腾讯云CentOS系统配置apache和tomcat

    本文使用yum软件包管理工具基于CentOS7.2版本配置apache和tom. 云服务器选购完毕后,安装Xshell软件,输入用户名密码即可远程登陆登录(centos用户名默认是root). 1,下 ...

  3. 在Ubuntu Server 14.04中搭建FTP服务器(VMWare)

    自己搭建ftp服务器,方便主机与虚拟机中的Ubuntu传输文件. 选用的ftp软件为vsftpd. 1.命令行: sudo apt-get install vsftpd 2.安装完配置: vsftpd ...

  4. java程序设计之循环链表

    照例来说点废话,愿自己生一颗温柔细腻的心,这样女朋友也许一个眼神,一个微笑,我就知道如何做,可惜内心与外表一样粗糙,这一生有幸认识她,愿天下有情人终成眷属. 循环链表,顾名思义循环就好了,所以建一个线 ...

  5. 解决 node-gyp rebuild 卡住 的问题

    node-gyp在编译前会首先尝试下载node的headers文件,像这样: gyp http GET https://nodejs.org/download/release/v6.8.1/node- ...

  6. redis3.0常用命令

    1.服务器启动 1)快捷启动 $redis-server 2)指定配置文件启动 $redis-server redis.conf 2.客服端启动 1)快捷无密码启动 $redis-cli 2)有密码和 ...

  7. AppBox v6.0中实现子页面和父页面的复杂交互

    前言 1. AppBox是捐赠开源(获取源代码至少需要捐赠作者 1 元钱),基于的 FineUI(开源版)则是完整开源,网址:http://fineui.codeplex.com/ 2. 你可以通过捐 ...

  8. VS2015 使用及插件推荐

      环境搭建与插件推荐 Cpp in Win10 x64 vs2015 + Resharper/Visual Assist X vs2015Pro: Download or see BaiDu 中文专 ...

  9. bzoj2243

    2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6753  Solved: 2496[Submit][Status ...

  10. COGS2531. [HZOI 2016]函数的美 打表+欧拉函数

    题目:http://cogs.pw/cogs/problem/problem.php?pid=2533 这道题考察打表观察规律. 发现对f的定义实际是递归式的 f(n,k) = f(0,f(n-1,k ...