js骚操作骂人不带脏
前言
很多小伙伴们觉得javaScript
很简单,下面的这行 javaScript
代码可能会让你怀疑人生。
(!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+[]]*~+[]]
小伙伴们学会了,以后遇到一些讲不通道理,让你不开森的人就把这段代码发给他。
那么这段代码为什么会输出sb呢?
其实这段代码考的是js的类型转化的一些基本原理
首先要运用到的第一个知识就是js运算符的优先级,因为这么长一段运算看的人眼花,我们必须得先根据优先级分成n小段,然后再各个击破
1, js运算符的优先级
优先级的排列如下表:
优先级从高到低:
根据此规则,我们把这一串运算分为以下16个子表达式:
运算符用红色标出,有一点可能大家会意识不到,其实中括号[]也是一个运算符,用来通过索引访问数组项,另外也可以访问字符串的子字符,有点类似charAt方法,如:'abcd'[1] // 返回'b'。而且中括号的优先级还是最高的哦。
接下来需要运用的就是javascript
的类型转化知识了,我们先说说什么情况下需要进行类型转化。当操作符两边的操作数类型不一致或者不是基本类型(也叫原始类型)时,需要进行类型转化。
让我们快速的复习一下.在JavaScript中,一共有两种类型的值:原始值(primitives)
和对象值(objects)
.
- 原始值有:undefined, null, 布尔值(booleans), 数字(numbers),还有字符串(strings).
- 其他的所有值都是对象类型的值,包括数组(arrays)和函数(functions).
2.类型转化
(1)先按运算符来分一下类:
减号-,乘号*,肯定是进行数学运算,所以操作数需转化为number类型。
加号+,可能是字符串拼接,也可能是数学运算,所以可能会转化为number或string
一元运算,如+[],只有一个操作数的,转化为number类型
(2)下面来看一下转化规则。
(2).1 对于非原始类型的,通过ToPrimitive() 将值转换成原始类型:
ToPrimitive(input, PreferredType?)
可选参数PreferredType是Number或者是String。返回值为任何原始值.如果PreferredType是Number,执行顺序如下:
如果input为primitive,返回
否则,input为Object。调用 obj.valueOf()。如果结果是primitive,返回。
否则,调用obj.toString(). 如果结果是primitive,返回
否则,抛出TypeError
如果 PreferredType是String,步骤2跟3互换,如果PreferredType没有,Date实例被设置成String,其他都是Number
(2).2 通过ToNumber()将值转换为数字
通过ToNumber()把值转换成Number,直接看ECMA 9.3的表格
参数 | 结果 |
---|---|
undefined | NaN |
null | +0 |
布尔值 | true被转换为1,false转换为+0 |
数字 | 无需转换 |
字符串 | 由字符串解析为数字.例如,"324"被转换为324 |
如果输入的值是一个对象,则会首先会调用ToPrimitive(obj, Number)将该对象转换为原始值,然后在调用ToNumber()将这个原始值转换为数字.
(2).3 通过ToString()将值转换为字符串
通过ToString()把值转化成字符串, 直接看ECMA 9.8的表格
参数 | 结果 |
---|---|
undefined | "undefined" |
null | "null" |
布尔值 | "true" 或者 "false" |
数字 | 数字作为字符串,比如. "1.765" |
字符串 | 无需转换 |
如果输入的值是一个对象,则会首先会调用ToPrimitive(obj, String)将该对象转换为原始值,然后再调用ToString()将这个原始值转换为字符串.
规则就这么多,接下来实践一下,根据我们上面划分出的子表达式,一步一步将这个神奇的代码给执行出来。开工~
先看最简单的子表达式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",相同的原理可以搞出来,我这里就不一一演示了,留给你练练吧~
回顾一下这个过程其实也不复杂,只是有一些需要重复劳动的,只要你掌握了运算的优先级,能把大串分解成一个个小串,然后运用类型转化的知识挨个处理就搞定了。怎么样,看到这里你还觉得神奇吗?
同样的,中文字符也是由这样组成的,跟英文同样的道理。
参考
https://www.cnblogs.com/ziyunfei/archive/2012/09/15/2685885.html
https://zhidao.baidu.com/question/433731856769399284.html
js骚操作骂人不带脏的更多相关文章
- js 骂人不带脏字 (!(~+[]) + {})[--[~+""][+[]] * [~+[]] + ~~!+[]] + ({} + [])[[~!+[]] * ~+[]] 图解
看到掘金上翻出一个老梗,前端如何不带脏字得骂产品经理傻逼(sb),复制(!(~+[]) + {})[--[~+""][+[]] * [~+[]] + ~~!+[]] + ({} + ...
- 项目中常用的js骚操作
//打开网址window.open("http://www.runoob.com"); //判断是否为url var url = $("#url").val() ...
- switch多值匹配骚操作,带你涨姿势!
我们都知道 switch 用来走流程分支,大多情况下用来匹配单个值,如下面的例子所示: /** * @from 微信公众号:Java技术栈 * @author 栈长 */ private static ...
- Typescript骚操作,在TS里面直接插入HTML
Typescript骚操作,在TS里面直接插入HTML,还有语法提示 先给大家看一个图 因为我不喜欢用很重的框架,主要是并非专业UI,但是偶尔会用到,还是觉得直接element组装受不了,想想能在ts ...
- 第六十四天 JS基础操作
一.分支结构 1.if语句 if基础语句 if(条件表达式){ 代码块: } // 当条件表达式结果为true,会执行代码块:反之不执行 // 条件表达式可以为普通表达式 // 0.undefined ...
- 关于map 及 map 骚操作
关于map这个东西 很冷门.................. 但是,这个博客带你稍微了解一下map: map用法:一般当作一个下表无穷大的数组 关于它的骚操作:map的鬼畜用法,可以 ...
- 通过HTTP的HEADER完成各种骚操作
作为一名专业的切图工程师,我从来不care网页的header,最多关心Status Code是不是200.但是HEADER真的很重要啊,客户端从服务器端获取内容,首先就是通过HEADER进行各种沟通! ...
- js的dom对象(带实例超详细全解)
js的dom对象(带实例超详细全解) 一.总结 一句话总结: 1.DOM中的方法区分大小写么? 解答:区分 2.DOM中元素和节点的关系式什么? 解答:元素就是标签,节点中有元素节点,也是标签,节点中 ...
- Mac OS 上的一些骚操作
本帖记录个人在使用 Mac 操作系统上的一些骚操作,不断更新,以飨读者. 快速移动网页到顶部或底部 用双指上下划触摸板吗?NO,我们有更骚的操作: command + ↑ 回到顶部 command + ...
随机推荐
- 爬虫前奏——代理ip的使用
如果同一个IP短时见内多次访问统一网页,可能会被系统识别出是爬虫,因此使用代理IP可以很大程度上解决这一问题 常用的代理有: 西刺免费代理:www.xicidaili.com 快代理:www.kuai ...
- (转)协议森林10 魔鬼细节 (TCP滑窗管理)
协议森林10 魔鬼细节 (TCP滑窗管理) 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 在TCP协议与"流" ...
- .NET 开发工具盘点和现状
在这里我盘点一下.NET的开发工具:Visual Studio.Jetbrains Rider.Mono Develop.SharpDevelop.QuickSharp.Visual Studio C ...
- 关于CORS(跨域资源共享)的几个http请求头小实验
对几种与跨域相关的请求头做一个总结 关于跨域可以看:9 种常见的前端跨域解决方案(详解) 看完后可以配合我的代码做些实验,看看注释掉某个响应头会发生什么,整体代码会在最后贴出 跨域简单请求 需要在服务 ...
- 面试官再问我如何保证 RocketMQ 不丢失消息,这回我笑了!
最近看了 @JavaGuide 发布的一篇『面试官问我如何保证Kafka不丢失消息?我哭了!』,这篇文章承接这个主题,来聊聊如何保证 RocketMQ 不丢失消息. 0x00. 消息的发送流程 一条消 ...
- 面试刷题12:zero copy是怎么回事?
文件copy是java的io部分不可忽视的内容. 我是李福春,我在准备面试,今天的问题是: zero-copy是怎么回事? 操作系统的空间划分为内核态空间, 用户态空间: 内核态空间相对操作系统具备更 ...
- 控制台报错Cause: org.xml.sax.SAXParseException; lineNumber: 4; columnNumber: 78; 元素类型 "select" 必须后跟属性规范 ">" 或 "/>"
首先我的控制台报错是这样的,我找了一下原因看到是第四行的错误: 它说元素select后面必须跟属性规范">""/>"但是我把我眼睛都快丑瞎了都没发现 ...
- python之面向对象函数与方法,反射,双下方法
一.函数和方法 1.函数和方法的区别 函数: 全都是显性传参,手动传参,与对象无关 方法: 存在隐性传参,与对象有关 1.1通过函数名可以判断 len()就是函数 str.count()就是方法 de ...
- OpenCV-Python 轨迹栏作为调色板 | 九
目标 了解将轨迹栏固定到OpenCV窗口 您将学习以下功能:cv.getTrackbarPos,cv.createTrackbar等. 代码演示 在这里,我们将创建一个简单的应用程序,以显示您指定的颜 ...
- 推荐 | 7个你最应该知道的机器学习相关github项目
欢迎大家关注我们的网站和系列教程:http://www.tensorflownews.com/,学习更多的机器学习.深度学习的知识! 磐石 目录: 介绍 Person Blocker(人体自动遮挡) ...