js骚操作骂人不带脏
栏目: JavaScript · 发布时间: 6年前
内容简介:很多小伙伴们觉得小伙伴们学会了,以后遇到一些讲不通道理,让你不开森的人就把这段代码发给他。那么这段代码为什么会输出sb呢?
很多小伙伴们觉得 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",相同的原理可以搞出来,我这里就不一一演示了,留给你练练吧~
回顾一下这个过程其实也不复杂,只是有一些需要重复劳动的,只要你掌握了运算的优先级,能把大串分解成一个个小串,然后运用类型转化的知识挨个处理就搞定了。怎么样,看到这里你还觉得神奇吗?
同样的,中文字符也是由这样组成的,跟英文同样的道理。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- JavaScript骚操作之操作符
- Go 语言操作 MySQL 之 事务操作
- C# 数据操作系列 - 1. SQL基础操作
- Vim 跨行操作与 Ex 命令操作范围
- 并发环境下,先操作数据库还是先操作缓存?
- 关于HBase Shell基本操作的表操作示例
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Practical Django Projects, Second Edition
James Bennett / Apress / 2009 / 44.99
Build a django content management system, blog, and social networking site with James Bennett as he introduces version 1.1 of the popular Django framework. You’ll work through the development of ea......一起来看看 《Practical Django Projects, Second Edition》 这本书的介绍吧!