javascript
JavaScript面试时候的坑洼沟洄——表达式与运算符
上篇博客JavaScript面試時候的坑洼溝洄——數據類型總結了一下JavaScript數據類型幾轉換的相關知識,很多朋友可能和我一樣,買了書后對數據類型啊、運算符啊、語句啊都是掃兩眼或直接略過的,自己為搞定原型、閉包、作用域鏈就可以秒殺JavaScript筆試題,結果一次次死在毫不起眼的基礎知識上,看似平淡無奇實則暗流涌動,一不小心就會栽倒。好了不扯淡了,回正題
神馬是表達式
表達式是由數字、運算符、數字分組符號(如括號)、自由變量和約束變量等以能求得數值的有意義排列方法所得的組合。~~約束變量在表達式中已被指定數值,而自由變量則可以在表達式之外另行指定數值。一個表達式代表一個函數,其輸入為自由變量的定值,而其輸出則為表達式因之后所產生出的數值。 ——維基百科
看起來很不接地氣的趕腳,表達式是JavaScript中的一個短語,JavaScript會將其計算出一個結果,常量如1、"hello"、null這些都是表達式;變量名也是表達式,JavaScript計算出的結果就是賦值給變量的值,這些都是簡單的表達式,幾個簡單的表達式可以組合為復雜的表達式,[3,4,5,6]這也是一個表達式,計算結果是數組,我們也可以通過運算符將簡單表達式組合位復雜表達式,8+9這樣,JavaScript表達式有幾種形式
原始表達式
常量、變量、保留字
對象、數組初始化表達式
var obj={a:1,b:2};
var a=[1,2,3];
函數定義表達式
var fn=function(){}
屬性訪問表達式
Math.abs
調用表達式
alert('hello');
對象創建表達式
new object();
函數定義
我們想使用一個函數的時候通常有幾種做法
函數表達式
函數表達式中函數名稱并不是必需的,所以我們經常這么使用
var fn=function(n) { console.log(n) };
函數聲明
更常見的做法是這樣
function fn(n){ console.log(n);}
使用Function構造函數
偶爾也會這樣 var fn=new Function('n',"console.log(n);");
這幾種做法都很好理解,但是如果函數表達式使用了名字呢,我們看個題目
var f = function g(){ console.log(g);}; f();//function g(){ console.log(g);}; typeof g();//g is not defined不知道結果和同學們的預期是否一致,但看起來這種結果似乎互相矛盾,當我們使用函數聲明的方式定義一個函數的時候,實際上聲明了一個變量,在上面例子中就是f,并把函數賦值給這個變量,普通的函數表達式沒有創建該變量,也就是我們所說的創建了一個匿名函數,但是如果函數表達式包含名稱,也就是上面例子的g,那么函數的局部作用域將包含將包含該名稱,并且把創建的函數綁定到該名稱上,在上面例子中g變成了函數的局部變量,變量指向函數本身,所以我們調用f的時候會把其本身打印出來。但是g只作為函數的局部變量存在,我們在外部調用g的時候就會報錯了。
命名函數表達式在創建的時候,會在當前作用域最前段添加一個新的對象
{func_name:refer_function_expression},然后,將作用域鏈添加到
函數表達式的[[scope]]中,接著在刪除該對象。
看個題目
var x=1; if(function f(){}){x+=typeof f; } console.log(x);//'1undefined'是不是覺得自然就能想到答案了
立即執行函數
初學JavaScript的同學很容易被類似這樣的東西唬住
(function(){})();其實我們了解了表達式就能很清楚的看明白這是什么結構了
(函數定義表達式)函數調用表達式
也就是說先創建了一個匿名函數,然后不傳入參數調用它,這就變成了“立即執行函數”,知道了這些看個傳入參數調用的立即執行函數題目
(function f(f){return typeof f();// "number" })(function(){return 1;});這個題目事實上還涉及了一些其它的知識,立即執行函數不再是以空括號()來調用了,同事傳入了一個function作為參數傳入調用。再一個疑惑就是typeof f() 中的f究竟指誰,這個知識我們后面會介紹道,簡單說一下,當函數執行有命名沖突的時候,函數依次填入 變量=》函數=》參數,所以最后被填入的參數f會覆蓋函數定義f,typeof f()是對參數的調用,參數是立即執行函數傳入的function參數,返回數字1,typeof 1是 "number"。
表達式返回值
表達式看明白了,我們卻經常忽略其計算結果,也就是我們常說的返回值,對于原始表達式、對象數組初始化表達式、屬性訪問表達式很簡單不會有什么問題。
函數定義表達式返回的是函數對象本身,我們在調用alert或者console.log的時候會調用其toString方法
console.log(function(){alert('a');}) //function (){alert('a');}
函數調用表達式自然是返回函數的return結果,但在JavaScript中并不是所有的函數都有return語句,對于沒有return語句的function,其調用表達式返回undefined,對于只寫個return的坑爹做法同樣也是返回undefined
(function(){})(); //undefined (function(){return;})();//undefined對象創建表達式本來也應該很簡單,返回new的對象就可以了
typeof new Date(); //"object"但是總有特殊的,看個題目
function Test(){return new Date(); } var test=new Test(); console.log(test instanceof Test);//false console.log(test);//Sat Jan 18 2014 14:57:08 GMT+0800 (CST)很奇怪啊,new Test()沒有返回Test的實例對象,返回的卻是Date對象,這是為什么呢?是不是有返回值的function使用構造函數的時候就會返回return指令的結果呢?看個例子
function Test(){return new Date(); } function Test2(){return 2; } typeof new Test(); new Test2() instanceof Test2;//true,竟然是true剛才的推測明顯不正確,Test2有返回值,new test2() 返回的是Test2的實例,但是我們已經可以看出一絲端倪了
當使用function的構造函數創建對象(new XXX)的時候,如果函數return基本類型或者沒有return(其實就是return undefined)的時候, new 返回的是對象的實例;如果 函數return的是一個對象,那么new 將返回這個對象而不是函數實例。
這里千萬別把構造函數(使用new)和普通函數調用混淆了,普通函數調用還是該返回什么返回什么的。看個題目
'foo' == new function(){ return String('foo'); }; //false 'foo' == new function(){ return new String('foo'); };//true怎么樣,答對沒有?
正則表達式
關于表達式還有一個重點沒有說——正則表達式,相關內容已經總結位單獨博客,有興趣同學可以看看
JavaScript 正則表達式上——基本語法
JavaScript正則表達式下——相關方法
運算符
JavaScript中運算符主要用于連接簡單表達式,組成一個復雜的表達式。常見的有算數表達式、比較表達式、邏輯表達式、賦值表達式等,也有單目運算符,指操作原始表達式。大多數運算符都由標點符號組成(+、>=、!),也有關鍵字表示的運算符,如typeof、delete、instanceof等。
一些運算符可以作用于任何數據類型(typeof),但大部分操作符“希望”操作數是特定的類型,而且大部分操作符會計算出(我們也常說返回)一個特定類型的值(typeof返回的全是string)。在JavaScript中運算符通常會根據需要對操作數進行類型轉換,乘法操作符 "" 希望操作數是數字,但是 "3""5"也是合法的,JavaScript會自動將其轉換為數字計算,返回Number 15。
有些操作符對不同的數據類型有不同的含義,比如 "+"
console.log(2+4);//6 console.log("2"+"4");//"24" console.log(2+"4");//"24" console.log(2+new Date());//"2Mon Jan 20 2014 17:15:01 GMT+0800 (China Standard Time)" console.log(+"4");//4- 在兩個操作數都是數字的時候,會做加法運算
- 兩個參數都是字符串或在有一個參數是字符串的情況下會把另外一個參數轉換為字符串做字符串拼接
- 在參數有對象的情況下會調用其valueOf或toString
- 在只有一個字符串參數的時候會嘗試將其轉換為數字
- 在只有一個數字參數的時候返回其正數值
運算符優先級與結合性
優先級什么意思大家都清楚,結合性是指多個具有同樣優先級的運算符表達式中的運算順序。有的運算符是左結合的,即運算從左到右執行,下面兩個運算是一樣的
w=x+y+z; w=(x+y)+z;有的運算符是右結合的
w=x=y=z; w=(x=(y=z)); w=a:b:c?d:e?f:g; w=a?b:(c?d:(e?f:g));運算符的優先級《JavaScript權威指南》中有個表闡述的很好(我去掉了位運算部分),其中R/L代表結合性是右結合還是左結合,num->num表示操作符期望的數據類型和計算結果類型,lval指左值
| ++ | 自增 | R | lval->num |
| -- | 自減 | R | lval->num |
| - | 求反 | R | num->num |
| +(一個操作數) | 轉換為數字 | R | num->num |
| ~ | 按位求反 | R | int->int |
| ! | 邏輯非 | R | bool->bool |
| delete | 刪除屬性 | R | lval->bool |
| typeof | 檢測數據類型 | R | any->str |
| void | 返回undefined | R | any->undefined |
| *、/、% | 乘、除、求余 | L | num,num->num |
| +、- | 加、減 | L | num,num->num |
| + | 字符串拼接 | L | str,str->str |
| 、>= | 數字大小或字母表順序 | L | num/str,num/str->bool |
| instanceof | 對象類型 | L | obj,function->bool |
| in | 測試屬性是否存在 | L | str,obj->bool |
| == | 判斷相等 | L | any,any->bool |
| != | 判斷不等 | L | any,any->bool |
| === | 判斷恒等 | L | any,any->bool |
| !== | 判斷非恒等 | L | any,any->bool |
| && | 邏輯與 | L | any,any->any |
| || | 邏輯或 | L | any,any->any |
| ?: | 條件運算符 | R | bool,any,any->any |
| =賦值 *=、/=、+=、-= | 賦值 運算且賦值 | R | lval,any->any |
| , | 忽略第一個操作數,返回第二個操作數 | L | any,any->any |
有幾個我們需要注意的地方
- typeof的優先級相當的高,比加減乘除神馬的都高,所以雖然是操作符,在在復雜表達式的時候我們還是習慣家括號,看個例子
- ++、--是右結合的操作符(優先級最高的幾個都是右結合),而且比加減乘除優先級高。同時自增、自減運算符的運算數得是左值(可以放在賦值符號左邊的值),而不能是常數
- 賦值運算符的優先級相當的低
- 邏輯非!也在優先級隊列的前端,比加減乘除高,但邏輯與、邏輯或優先級很低,不如加減乘除
- 一個關于邏輯運算符的有意思地方是其“短路”功能,相信大家都有所了解,但有些題目不那么單純,會結合表達式計算值來考察
了解了邏輯運算符的“短路”特點,在知道原始表達式的“返回值”就是本身,題目就很簡單了
運算順序
我們在運算符的優先級和“返回值”上關注了很多,一個經常被我們忽略的知識點就是運算順序問題,復雜的表達式是由運算符和子表達式組成,優先級和結合性決定了表達式的運算順序,但是卻沒有規定子表達式的運算順序,在JavaScript中嚴格按照從左到右的順序計算表達式,然后再按照優先級和結合性計算各個表達式和運算符作用結果。說的比較晦澀,看個例子
var a=1; b=(a=3)+a++;這個例子中運算順序是這樣的
相等
我們知道可以使用"=="或"==="判斷兩個值的相等性,其中區別相信大家清楚,"==="是嚴格意義的相等,只需注意NaN和NaN不等就行了。而使用"=="的時候,javascript會幫我們做類型轉換,造成一些匪夷所思的結果,那么使用"=="的時候會在哪些情況下做類型轉換,又會換成什么樣子?
******
- 如果一個是null,一個是undefined,那么相等
- 如果一個是數字,一個是字符串,先將字符串轉為數字,然后比較
- 如果一個值是true/false則將其轉為1/0比較
- 如果一個值是對象,一個是數字或字符串,則嘗試使用valueOf和toString轉換后比較
- 其它就不相等了
轉載于:https://www.cnblogs.com/dolphinX/p/3524977.html
超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生總結
以上是生活随笔為你收集整理的JavaScript面试时候的坑洼沟洄——表达式与运算符的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深夜,先给自己记录个东西
- 下一篇: jQuery 实现上下,左右滑动