不少小伙伴们以为javaScript
很简单,下面的这行 javaScript
代码可能会让你怀疑人生。javascript
(!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+[]]*~+[]]
小伙伴们学会了,之后遇到一些讲不通道理,让你不开森的人就把这段代码发给他。html
那么这段代码为何会输出sb呢?
其实这段代码考的是js的类型转化的一些基本原理java
首先要运用到的第一个知识就是js运算符的优先级,由于这么长一段运算看的人眼花,咱们必须得先根据优先级分红n小段,而后再各个击破数组
优先级的排列以下表:函数
优先级从高到低:spa
根据此规则,咱们把这一串运算分为如下16个子表达式:3d
运算符用红色标出,有一点可能你们会意识不到,其实中括号[]也是一个运算符,用来经过索引访问数组项,另外也能够访问字符串的子字符,有点相似charAt方法,如:'abcd'[1] // 返回'b'。并且中括号的优先级仍是最高的哦。code
接下来须要运用的就是javascript
的类型转化知识了,咱们先说说什么状况下须要进行类型转化。当操做符两边的操做数类型不一致或者不是基本类型(也叫原始类型)时,须要进行类型转化。htm
让咱们快速的复习一下.在JavaScript中,一共有两种类型的值:原始值(primitives)
和对象值(objects)
.对象
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
经过ToNumber()把值转换成Number,直接看ECMA 9.3的表格
参数 | 结果 |
---|---|
undefined | NaN |
null | +0 |
布尔值 | true被转换为1,false转换为+0 |
数字 | 无需转换 |
字符串 | 由字符串解析为数字.例如,"324"被转换为324 |
若是输入的值是一个对象,则会首先会调用ToPrimitive(obj, Number)将该对象转换为原始值,而后在调用ToNumber()将这个原始值转换为数字.
经过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就简单了,把1五、16求出来的填进去,就变成了这样:---1,取数组的第0个元素,而后自减,结果为-2,是不so easy!
继续往上走,子表达式14: [~+[]]
其实把1五、和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/ziyun...
https://zhidao.baidu.com/ques...