前言
我是有过这样的经历,获取年月日时写出这样的代码:
new Date().getFullYear()
此时的我是心虚的,因为我不知道是先执行.
运算还是new
运算,于是赶紧贴到控制台里,哎呦😀,没报错,看来是先执行new
了。
让我们再牛逼一点,把Date后面的()去掉!🤔然后变成这样--new Date.getFullYear()
。结果这回浏览器说此路不通。
或者有时候看到这样的眼花缭乱的黑魔法代码会一脸懵逼
[[[] == []] + []][+![]][+![]] //'f'
这就说明同学们,该打打怪,学学优先级了
表达式
按MDN的说法, 运算符的优先级决定了表达式中运算执行的先后顺序可以看出,优先级这个概念是针对表达式的执行而产生的。所以要谈优先级,先说什么是表达式。参考链接5给出一个不严谨的定义,但我觉得很合我的胃口:
一个表达式会产生一个值,它可以放在任何需要一个值的地方
根据这个定义,,我们可以这样来验证一段代码是不是表达式,就是让把这段代码赋值给一个变量(不严谨了^^),如果不报错,那就是一个表达式了:举个例子:
3 //a=3 ok,所以a是一个表达式
new Date //a=new Date 也是一个表达式
a=3 //a=a=3 所以a=3也是一个表达式
a>2 ? new Date : function(){} //可以赋值,也是表达式
function(){} //a=function(){} ok
a //a=a报错,然而a是一个表达式。。。,JS中的原始表达式:常量或者直接量、关键字和变量(参考链接4)
链接4总结了以下的表达式种类:
- 原始表达式:常量或者直接量、关键字和变量
- 字面量表达式
- 函数定义表达式
- 属性访问表达式
- 调用表达式
- 对象创建表达式
优先级、结合性、求值顺序
接下来进入正题,MDN上是这么解释的:
运算符的优先级决定了表达式中运算执行的先后顺序
结合性决定了拥有相同优先级的运算符的执行顺序
链接1里有知乎大神关于优先级的语法树级别的描述,看懂了你大概就明白V8是怎么看懂你的代码的。我们这里说一个不精确的描述就是优先级高的运算符就是给自己负责的运算加了一个括号。回到一开始举的那个Date的例子:
new Date().getFullYear() //等于(new Date()).getFullYear()
new Date.getFullYear() //等于(new (Date.getFullYear())报错
这里第二种方法报错就是优先级的锅。根据MDN给的优先级顺序表,可以看到,带括号的 new Date()
优先级在19的位置,不带括号的new Date
则在18的位置,而.
运算则在19的位置,优先级的差异造成了两种不同的结果。
接下来在说说什么是运算符结合性。考虑下面的代码:
a=!3
5+6+7
看到第一条的时候,你脑子里肯定是先考虑了!3
。为什么不先考虑a=
或者 =!
呢,而第二段代码我们则从左往右读的。这就是结合性,取反运算是右结合的,而且优先级比赋值运算高,所以先考虑!3
,而+
运算是从左向右的。所以我们在读代码的时候已经用到优先级和结合性,只是没意识到而已。
运算符
哦,还要说以下下运算符的作用,运算符就是拉着身边的表达式构成一个新的表达式,最后产生一个值:
3 //表达式
!3 //新的表达式 由!和表达式3构成
1+!3 //+带了俩个表达式构成一个新表达式
true ? 1+!3 : 0 // ? :运算符和true,1+!3,0一起玩4p构成的新表达式
说到这里,差不多就完了,如果各位看官老爷还嫌不过瘾,可以看看参考链接4的运算符8关,或者研究这个打印出来是什么,我就不打扰各位老爷了(ε=ε=ε=┏(゜ロ゜;)┛
(!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+[]]*~+[]]