十二月已經過半,冬季是一個美妙的季節,寒冷的空氣逼得人們不得不躲在安逸舒適的環境裡生活。冬季會給人一種安靜祥和的氛圍,讓人沉浸在其中,仿佛是一個舊的階段的結束,同時也是一個新的階段的開始。這麼說來,西方和中國的聖誕節和春節都選擇在了冬季也不是沒有道理,在一年中最寒冷的時候,人們擁簇在溫暖的環境裡,彼此訴說著過去一年裡自己的成就,展望著新的一年裡美好的願望,相互掛念的人團聚,天氣的寒冷和人情的溫暖形成了強烈的對比。而在天寒地凍之中,仿佛更有利於人們思考,去探尋知識的真谛。
這次想分享的是 JS 當中的邏輯運算符與、或,也就是 && 、 || ,初來乍到的同學們看到這裡就會覺得沒趣了,這玩意有什麼好分享的,剛開始學 JS 的時候不就會了嗎,我用了無數遍都沒有什麼問題啊。而有經驗的同學可能會陷入沉思,難不成這其中會有什麼奧秘所在?沒錯,別看這簡簡單單的幾個運算符,雖然這是最基礎的知識,但其中隱藏的奧秘卻十分耐人尋味,接下來我就為大家一一揭開這簡答的運算符背後的奇妙之處。
基礎的作用我就不說了,這兩個符號是個程序員都能明白,這裡首先我想先來說一說 JS 當中的隱式轉換。
眾所周知,JS 在做邏輯判斷的時候會自動將非布爾類型的值進行隱式轉換,轉換成布爾類型的值然後在進行邏輯運算。在初學 JS 的時候,都會講到在隱式轉換中,除了幾個特定的假值,其他的均會轉換成真值,這些假值有:
NaN; ""; undefined; null; 0;
有了這些隱式轉換的規則,便構成了 JS 當中邏輯運算的核心基礎。
其實在 JS 當中,要說“邏輯運算符”其實並不完全正確,Kyle Simpson 在《You Don't Know JS》系列書當中提到:“與其說是‘邏輯運算符',不如說是‘選擇器運算符'。” 為什麼大師要這樣說呢?其實我們大多數人都被 JS 的表象給蒙蔽了,比如下面一段非常簡單的代碼:
if( "hello" && 0 ) { console.log(true); } else { console.log(false); }
如果你對 JS 了解的不夠深刻,你可能會這樣解釋這段代碼:首先在邏輯判斷中,"hello" 是一個真值,0 是一個假值,一個真值和一個假值進行與運算,結果為 false 。這也可能是大多數人的理解,但其實不然,其內部的原理可不止這麼簡單,因為 && 和 || 返回的並不是判斷條件的真假 ,而是判斷條件中的一個原始值。它將依次對條件判斷中的值進行判斷,如果是非布爾值,則轉換成布爾值做判斷,然後再根據判斷條件來決定返回哪一個值。
對於 && :該運算符返回條件語句中的第一個假值,如果所有的值都為真,則返回最後一個值,&& 也被稱為 “守護運算符” 。比如下面一段代碼:
var a = "hello" && "world"; console.log(a); //world var b = 0 && 1; console.log(b); //0
可以看出,邏輯運算符其實返回的並不是條件的真假,而是原始值。如果條件語句中有多個 && 運算符,則一樣遵循以上原則,從左向右依次判斷,如果遇到了假值,就返回該假值,如果所有值都為真,則返回最後一個值。
對於 ||:該運算符與 && 運算符相反,它返回條件語句中的第一個真值,如果所有值都為假,則返回最後一個值。比如下面一段代碼:
var a = "hello" || 0; console.log(a); //hello var b = 0 || NaN; console.log(b); //NaN
同樣,|| 返回的也不是布爾值。如果有多個 || 則同樣遵循相同的原則,從左向右依次掃描。
講到這裡也就來到了本篇文章的核心,在 JS 當中,條件判斷語句都是建立在隱式轉換之上的,也就是說所謂的邏輯運算符,實際上是在條件判斷語句中從左向右依次掃描,如果是一個布爾值,則判斷該布爾值的真假,如果是一個非布爾值,則先對該值進行隱式轉換,然後再判斷真假,如果滿足條件,則返回該值,如果沒有滿足條件值,則返回最後一個值,然後在對返回的這個值做判斷,如果是一個布爾值,則直接判斷,如果是一個非布爾值,則先隱式轉換成布爾值,再做判斷。所以我們也可以把 && 稱為 “取假運算符” ,把 || 稱為 “取真運算符” ,因為這兩個運算符的實質都是取條件語句中的第一個真值或者假值,如果始終沒有找到,則返回最後一個值。而這樣的算法也恰好滿足邏輯判斷的需求,比如 && 運算符,如果所有的值都是真值,那麼返回哪個值其實都無所謂,因為所有值都能夠被隱式轉化為 true ,而只要有一個假值,則判斷條件不成立,所以會返回第一個遇到的假值。而 || 運算符,如果所有的值都是假值,返回任意一個都會被隱式轉換成 false ,但只要遇到了一個真值,則判斷條件成立,所以會返回第一個遇到的真值。&& 和 || 運算符都是 “短路” 的。
所以我們可以自己實現一個邏輯運算的函數:
// && 等價於: function AND () { for (var i = 0; i < arguments.length; i++) { if (!arguments[i]) { return arguments[i]; } } return arguments[i-1]; }
// || 等價於: function OR () { for (var i = 0; i < arguments.length; i++) { if(arguments[i]) { return arguments[i]; } } return arguments[i-1]; }
(注:在這裡我同時也想對 ! 這個運算符做講解,但考慮到內容和篇幅的問題,暫時不做深入探究,僅做簡單講述。 ! 運算符實際上運行機制與 && 和 || 是一樣的,首先會對參數值做判斷,如果是一個布爾值,則進行取反運算,如果是一個非布爾值,則先進行隱式轉換,再進行取反運算。而我們通常寫的 if (something) 語句,實際上的意思 if (!!something))
然後我們可以這樣使用:
var a = ["hello", undefined, "world"]; console.log(AND.apply(null, a)); //undefined var b = ["", 0, NaN]; console.log(OR.apply(null, b)); //NaN
進而,我們就可以推斷出一下結論:
a = x || y; //等價於: a = x ? x : y; a = x && y; //等價於: a = x ? y : x;
這通常也是一些壓縮工具所做的事情,它們盡可能的將繁雜的條件判斷語句轉換成 && 或者 || ,因為這樣代碼更加的精簡,但是可讀性則就不那麼可觀了。
對於最開始的那一段代碼:
if( "hello" && 0 ) { console.log(true); } else { console.log(false); }
我們現在就要這樣解釋:首先這是個與運算符,與運算符的作用是取第一個假值,如果所有的值都為真,那麼則返回最後一個值。所以在這條語句中,第一個值是 "hello" ,因為該值是一個非布爾值,JS 引擎會先將它隱式轉換成布爾值,而該值不在假值的范圍內,所以會被轉化成 true 。隨後 JS 引擎會繼續查找,第二個值是 0 ,該值同樣也不是一個布爾值,所以 JS 引擎也會先將它隱式轉換成布爾值,而該值在假值的范圍內,所以會被轉化成 false ,滿足 && 運算符的查找條件,則將值 0 返回。而條件判斷語句接受到了值 0 ,該值不是一個布爾類型的值,所以會先對它進行隱式轉換,而該值在假值范圍內,所以會被轉化成 false ,然後控制台會輸出 false。
所以說以後當我們看到 && 和 || 時候,就不要僅僅的從字面上的意義去理解了,在看完了這篇文章之後,你就可以很自豪很有底氣的對別人說,你真的會用邏輯運算符嗎?
好了,就這麼兩個小玩意居然背後也有著這麼多的精髓,看來知識的深度是無窮的,冬季還真是一個能引發人們思考的季節。聖誕節即將到來,在這裡提前預祝大家聖誕快樂,Merry Christmas!
以上就是本文的全部內容,希望本文的內容對大家的學習或者工作能帶來一定的幫助,同時也希望多多支持!