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