一行神奇的javascript代码

  写本篇文章的缘由是之前群里有朋友发了一段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,请把这段代码发给他,如果他想知道答案,请把本文发给他~

时间: 2024-10-06 06:28:32

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

一行能装逼的JavaScript代码

一行能装逼的JavaScript代码 2016-06-28 野狗 一行神奇的JS代码,当时我就震惊了,这不就是传说中的ZB神奇么… … 哈哈. 写本篇文章的缘由是之前看到了一段js代码,如下: (!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+[]]*~+[]] 然后让大家运行,出来的结果让人有点出乎意料,请看: 太风骚了有木有!如果有人诋毁前端瞧不起JS的话,那就可以把这段代码发给他了~ 不过话说回来了,这到底是什么原理呢

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

你可能在网上见过有人用 几个不同的字符写的各种稀奇古怪的 JavaScript 代码,虽然看起来奇怪,但是能正常运行!比如这个: (!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+[]]*~+[]] 复制代码 你猜运行结果是什么?你可以自己去控制台试一下. 看起来很神奇,但这到底是怎么回事呢? 事实上,你几乎可以用下面这 6 个字符写出任意的 JavaScript 程序: []()!+ 复制代码 很多人都知道这个技巧,但是没

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

曾经,有网友抱怨FineUI中连个通知框都没有,当用户进行某个操作成功时给个右下角的提示很不方便. 强大的设置参数 现在,FineUI(专业版)提供了强大的通知框机制,一个小小的通知框居然有多达 16 种不同的设置,可见威力之强大. 下面通过一张图片来简单概括一下: 1. 模式或者非模式对话框2. 消息图标可显示(消息.警告.问题.错误.成功),也可隐藏3. 正在加载图片可显示隐藏4. 消息正文可自定义5. 对话框标题可自定义6. 关闭图标可显示隐藏7. 标题栏可拖动8. 标题栏可隐藏9. 弹出

代码示例:一些简单技巧优化JavaScript编译器工作详解,让你写出高性能运行的更快JavaScript代码

告诉你一些简单的技巧来优化JavaScript编译器工作,从而让你的JavaScript代码运行的更快.尤其是在你游戏中发现帧率下降或是当垃圾回收器有大量的工作要完成的时候. 单一同态: 当你定义了一个两个参数的函数,编译器会接受你的定义,如果函数参数的类型.个数或者返回值的类型改变编译器的工作会变得艰难.通常情况下,单一同态的数据结构和个数相同的参数会让你的程序会更好的工作. function example(a, b) { // 期望a,b都为数值类型 console.log(++a * +

【原创】如何管理你的 Javascript 代码

今天不聊技术的问题,咱们来聊聊在前端开发中如何管理好自己的 Javascript 代码.首先,咱们先来说说一般都有哪些管理方式?我相信 seajs.requirejs 对于前端开发者而言都不陌生,不错它们都是前端代码模块化开发的利器,显然以模块化的方式去管理我们的 Javascript 代码,是很不错的选择. 不过今天咱不谈模块化开发,因为上面的两个工具已经做得很好了,只要到他们的官方网站找到相应的文档资料,认真学习,不需太多时日你也能掌握模块化开发了.今天咱们要谈的是在不依赖模块管理工具的前提

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

才华横溢的Stoyan Stefanov,在他写的由O’Reilly初版的新书<JavaScript Patterns>(JavaScript模式)中,我想要是为我们的读者贡献其摘要,那会是件很美妙的事情.具体一点就是编写高质量JavaScript的一些要素,例如避免全局变量,使用单变量声明,在循环中预缓存length(长度),遵循代码阅读,以及更多. 此摘要也包括一些与代码不太相关的习惯,但对整体代码的创建息息相关,包括撰写API文档.执行同行评审以及运行JSLint.这些习惯和最佳做法可以

高级Javascript代码

Javascript是一门很吊的语言,我可能学了假的JavaScript,哈哈,大家还有什么推荐的,补充送那啥邀请码. 本文秉承着:你看不懂是你SB,我写的代码就要牛逼. 1.单行写一个评级组件 "★★★★★☆☆☆☆☆".slice(5 - rate, 10 - rate);定义一个变量rate是1到5的值,然后执行上面代码,看图 才发现插件什么的都弱爆了 来源:来自知乎用户蜗牛老湿的回答 2.如何装逼用代码骂别人SB (!(~+[])+{})[--[~+""][+

60行JavaScript代码俄罗斯方块

教你看懂网上流传的60行JavaScript代码俄罗斯方块游戏 早就听说网上有人仅仅用60行JavaScript代码写出了一个俄罗斯方块游戏,最近看了看,今天在这篇文章里面我把我做的分析整理一下(主要是以注释的形式). 我用C写一个功能基本齐全的俄罗斯方块的话,大约需要1000行代码的样子.所以60行乍一看还是很让人吃惊的. 但是读懂了代码之后发现其实整个程序并没有使用什么神秘的技术,只不过是利用一些工具或者JavaScript本身的技巧大大简化了代码. 总结起来主要是以下三点 1.使用eval

javascript 代码可读性

可读性的大部分内容都是和代码缩进相关的,必须保证代码有良好的格式.可读性的另一方面就是注释,一般而言,有如下一些地方需要进行注释 1.1.1           函数和方法 每个函数或方法都应该包含一个注释,描述其目的和用于完成任务所可能使用的算法,陈述事先的假设也非常重要,如参数代表什么,函数是否有返回值等等 1.1.2           大段代码 用于完成单个任务的多行代码应该在前面放一个描述任务的注释 1.1.3           复杂的算法 如果使用了一个独特的方式解决某个问题,则要