前言
我是有過這樣的經歷,獲取年月日時寫出這樣的代碼:
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關,或者研究這個打印出來是什么,我就不打擾各位老爺了(ε=ε=ε=┏(゜ロ゜;)┛
(!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+[]]*~+[]]