1. 程式人生 > >前端入門9-JavaScript語法之運算子

前端入門9-JavaScript語法之運算子

  原創: 請叫我大蘇 dasuAndroidTv 今天宣告本系列文章內容全部梳理自以下幾個來源:

  《JavaScript權威指南》

  MDN web docs

  Github:smyhvae/web

  Github:goddyZhao/Translation/JavaScript

  作為一個前端小白,入門跟著這幾個來源學習,感謝作者的分享,在其基礎上,通過自己的理解,梳理出的知識點,或許有遺漏,或許有些理解是錯誤的,如有發現,歡迎指點下。

  PS:梳理的內容以《JavaScript權威指南》這本書中的內容為主,因此接下去跟 JavaScript 語法相關的系列文章基本只介紹 ES5 標準規範的內容、ES6 等這系列梳理完再單獨來講講。

  正文-運算子程式中的程式碼其實就是利用各種運算子來輔助完成各種指令功能,在 JavaScript 中,有一些不同於 Java 中的運算子處理,這次就來講講這些運算子。

  由於我已經有了 Java 的基礎了,本節不會講基礎的運算子介紹,比如算術表示式中的加減乘除取餘等、關係表示式中的大於小於等、邏輯表示式中的自增、自減、移位等等,這些基礎運算子的含義、用法、優先順序這些跟 Java 基本沒有區別,所以就不介紹了。

  下面著重講一些在 JavaScript 比較不同的行為的一些運算子:

  "+" 運算子

  任何資料型別的變數都可以通過 "+" 運算子來進行計算,所以它有一套處理規則,通常要麼就是按數字的加法運算處理、要麼就是按照字串的拼接處理,處理規則如下:

  如果運算元中存在物件型別,先將其按照上節介紹的轉換規則,轉成原始值;

  如果運算元已經全部是原始值,此時如果有字串型別的原始值,那麼將兩個原始值都轉為字串後,按字串拼接操作處理;

  如果運算元已經全部是原始值且沒有字串型別的,那麼將運算元都轉為數字型別後,按數字的加法處理;

  NaN 加上任意型別的值後都是 NaN.

  以上的處理規則是針對於通過 "+" 運算子處理兩個運算元的場景,如果一個表示式中存在多個 "+" 運算子,那麼分別以優先順序計算過程中,每一次計算 "+" 運算子的兩個運算元使用上述規則進行處理。

  舉個例子:

  1 + 2 // => 3, 因為運算元都是數字型別的原始值1 + "2" // => "12",因為運算元中存在字串型別的原始值,所以是按字串拼接來處理1 + {} // => "1[object Object]",因為有操作是物件型別,先將其轉為原始值,{} 轉為原始值為字串 "[object Object]",所以將運算元都轉為字串後,按字串拼接處理1 + true // => 2,因為兩個都是原始值,且沒有字串型別,所以將 true 轉為數字型別後是 1,按加法處理1 + undefined // => NaN,因為 undefined 轉為數字型別後為 NaN,NaN 與任何數運算結果都為 NaN 1 + 2 + " dasu" // => "3 dasu", 因為先計算 1+2=3,然後再計算 3 + " dasu",所以是 "3 dasu"1 + (2 + " dasu") // => "12 dasu",因為先計算 2 + " dasu" = "2 dasu",再計算 1 + "2 dasu" = "12 dasu"

  因為 "+" 運算子在程式設計中很常見,也很常用,而 JavaScript 又是弱型別語言,變數無需宣告型別,那麼程式中,"+" 運算子的兩個運算元究竟是哪兩種型別在進行計算,結果又會是什麼,這點在心裡至少是要明確的。

  "==" 和 "===" 相等運算子

  "==" 和 "===" 都是用於判斷兩個運算元是否相等的運算子,但它們是有區別的。

  "==" 比較相等的兩個運算元會自動進行一些隱式的型別轉換後,再進行比較,俗稱不嚴格相等。

  "===" 比較相等的兩個運算元,不會進行任何型別轉換,相等的條件就是型別一樣,數值也一樣,所以俗稱嚴格相等。

  而 "!=" 和 "!==" 自然就是這兩個相等運算子的求反運算。下面分別來看看:

  "==="

  當通過這個運算子來比較兩個運算元是否嚴格相等時,具體規則如下:

  如果兩個運算元的型別不相同,則它們不相等

  如果其中一個運算元是 NaN 時,則它們不相等(因為 NaN 跟任何數包括它本身都不相等)

  如果兩個運算元都是物件型別,那麼只有當兩個運算元都指向同一個物件,即它們的引用一樣時,它們才相等

  如果兩個運算元都是字串型別時,當字串一致時,在某些特殊場景下,比如具有不同編碼的 16 位值時,它們也不相等,但大部分情況下,字串一致是會相等,但要至少清楚不是百分百

  如果兩個運算元都是布林型別、數字型別、null、undefined,且值都一致時,那它們相等

  總之,這裡的規則跟 Java 裡的相等比較類似,Java 裡沒有嚴格不嚴格之分,它處理的規則就是按照 JavaScript 這裡的嚴格相等來處理,所以大部分比較邏輯可參考 Java。

  需要注意的就是,NaN 與任何數包括它本身也不相等、同一個字串內容可能會有不同的編碼值,所以並不是百分百相等。

  "=="

  這個通常稱為不嚴格相等,當比較是否相等的兩個運算元的資料型別不一樣時,會嘗試先進行轉換,然後再進行比較,相比於上面的 "===" 嚴格相等運算子來說,它其實就是放寬了比較的條件,具體規則如下:

  如果兩個運算元的型別一樣,那麼規則跟 "===" 一樣

  如果一個型別是 null,另一個型別是 undefined,此時,它們也是相等的

  如果一個型別是數字,另一個型別是字串,那麼先將字串轉為數字,再進行比較

  如果一個型別是布林,先將布林轉成 1(true)或 0(false),然後再根據當前兩個型別是否需要再進一步處理再比較

  如果一個型別是物件,那麼先將物件轉換成原始值,然後再根據當前兩個型別是否需要再進一步處理再比較

  總之,"==" 的比較相對於 "===" 會將條件放寬,下面可以看些例子:

  null === undefined // => false,兩個型別不一樣null == undefined // => true,不嚴格情況下兩者可認為相等 1 == "1" // => true,"1" 轉為數字 1 後,再比較1 == [1] // => true,[1] 先轉為字串 "1",此時等效於比較 1 == "1",所以相等2 == true // => false,因為 true 先轉為數字 1,此時等效於比較 2 == 1

  "&&” 邏輯與

  邏輯與就是兩個條件都要滿足,這點跟 Java 裡的邏輯與操作 && 沒有任何區別。

  但 JavaScript 裡的邏輯與 && 操作會更強大,在 Java 裡,邏輯與 && 運算子的兩個運算元都必須是關係表示式才行,而且整個邏輯與表示式最終的結果只返回 true 或 false。

  但在 JavaScript 裡,允許邏輯與 && 運算子的兩個運算元是任意的表示式,而且整個邏輯與 && 表示式最終返回的值並不是 true 或 false,而是其中某個運算元的值。

  什麼意思,來看個例子:

  x == 0 && y == 0

  這是最基本的用法,跟 Java 沒有任何區別,當且僅當 x 和 y 都為 0 時,返回 true,否則返回 false。

  上面那句話,是從這個例子以及延用 Java 那邊對邏輯與 && 運算子的理解所進行的解釋。

  但實際上,在 JavaScript 裡,它是這麼處理邏輯與 && 運算子的:

  如果左運算元的值是假值,那麼不會觸發右運算元的計算,且整個邏輯與 && 表示式返回左運算元的值

  如果左運算元的值是真值,那麼整個邏輯與 && 表示式返回右運算元的值

  假值真值可以通俗的理解成,上節介紹各種資料型別間的轉換規則中,各型別轉換為布林型別的值,轉為布林後為 true,表示這個值為真值。反之,為假值。

  所以,按照這種理論,我們再來看看上面那個例子,首先左運算元是個關係表示式:x == 0,如果 x 為 0,這個表示式等於 true,所以它為真值,那麼整個邏輯與 && 表示式返回右運算元的值。右運算元也是個關係表示式:y == 0,如果 y 也等於 0,右運算元的值就為 true,所以整個邏輯與 && 表示式就返回 true。

  雖然結果一樣,但在 JavaScript 裡對於邏輯與 && 表示式的解釋應該按照第二種,而不是按照第一種的 Java 裡的解釋。如果還不理解,那麼再來看幾個例子:

  function getName() { return "dasu"}null && getName() //輸出 => null,因為左運算元 null 轉成布林是 false,所以它是假值,所以邏輯與 && 直接返回左運算元的值 nullgetName && getName() //輸出 => "dasu",因為左運算元是一個函式物件,如果該函式物件被宣告定義了,那麼轉為布林值就是 true,所以邏輯與 && 表示式返回右運算元的值,右運算元是 getName(),呼叫了函式,返回了 "dasu",所以這個就是這個邏輯與 && 表示式的值。

  第一個邏輯與表示式:null && getName() 會輸出 null,是因為左運算元 null 轉成布林是 false,所以它是假值,所以邏輯與 && 直接返回左運算元的值 null。

  第二個邏輯與表示式:getName && getName() 會輸出 "dasu",是因為左運算元是一個函式物件,如果該函式物件被宣告定義了,那麼轉為布林值就是 true,所以邏輯與 && 表示式返回右運算元的值,右運算元是 getName(),呼叫了函式,返回了 "dasu",所以這個就是這個邏輯與 && 表示式的值。

  所以 JavaScript 裡的邏輯與 && 表示式會比 Java 更強大,它有一種應用場景:

  應用場景

  function queryName(callback) { //... //回撥處理 callback && callback();}

  在 Java 中,我們提供回撥機制的處理通常是定義了一個介面,然後介面作為函式的引數,如果呼叫的時候,傳入了這個介面的具體實現,那麼在內部會去判斷如果傳入的介面引數不為空,就呼叫接口裡的方法實現通知回撥的效果。

  在 JavaScript 裡實現這種回撥機制就特別簡單,通過邏輯與 && 表示式,一行程式碼就搞定了,如果有傳入 callback 函式,那麼 callback 就會是真值,邏輯與 && 表示式就會去執行右運算元的 callback()。

  當然,如果你想嚴謹點,你可以多加幾個邏輯與 && 表示式來驗證傳入的 callback 引數是否是函式型別。

  "||" 邏輯或

  邏輯或 || 跟邏輯與 && 就基本是一個東西了,理解了上面講的邏輯與 && 運算子的理論,那麼自然也就能夠理解邏輯或 || 運算子了。

  它們的區別,僅在於對錶達式的處理,邏輯或 || 表示式是這麼處理的:

  如果左運算元的值是真值,那麼不會觸發右運算元的計算,且整個邏輯或 || 表示式返回左運算元的值

  如果左運算元的值是假值,那麼整個邏輯或 || 表示式返回右運算元的值

  假值真值可以通俗的理解成,上節介紹各種資料型別間的轉換規則中,各型別轉換為布林型別的值,轉為布林後為 true,表示這個值為真值。反之,為假值。

  這裡就直接來說下它的一個應用場景了:

  應用場景

  function queryNameById(id) { //引數的預設值 id = id || 10086; //...}

  處理引數的預設值,如果呼叫函式時,沒有傳入指定的引數時。

  當然,還有其他很多應用場景。總之,善用邏輯與 && 和邏輯或 || 運算子,可以節省很多程式設計量,同時實現很多功能。

  "," 逗號運算子

  在 Java 中,"," 逗號只用於在宣告同一型別變數時,可同時宣告,如:

  int a, b, c;

  在 JavaScript 裡,"," 逗號運算子同樣具有這個功能,但它更強大,因為帶有 "," 逗號運算子的表示式會有一個返回值,返回值是逗號最後一項運算元的值。

  逗號運算子跟邏輯與和邏輯或唯一的區別,就在於:逗號運算子會將每一項的運算元都進行計算,而且表示式一直返回最後一項的運算元的值,它不管每個運算元究竟是真值還是假值,也不管後續運算元是否可以不用計算了。

  舉個例子:

  function getName() { return "dasu"}function queryNameById(id, callback) { id = id || 10086; callback && callback();}function myCallback() { console.log("I am dasu");}var me = (queryNameById(0, myCallback), getName()) //me會被賦值為 "dasu",且控制檯輸出 "I am dasu"

  逗號運算子

  變數 me 會被賦值為 "dasu",且控制檯輸出 "I am dasu"。

  typeof 運算子

  返回指定運算元的資料型別,例:

  typeOf

  在 JavaScript 中資料型別大體上分兩類:原始型別和引用型別。

  原始型別對應的值是原始值,引用型別對應的值為物件。

  對於原始值而言,使用 typeof 運算子可以獲取原始值所屬的原始型別,對於函式物件,也可以使用 typeof 運算子來獲取它的資料型別,但對於其他自定義物件、陣列物件、以及 null,它返回的都是 object,所以它的侷限性也很大。

  delete 運算子

  delete 是用來刪除物件上的屬性的,因為 JavaScript 裡的物件有個特性,允許在執行期間,動態的為物件新增某個屬性,那麼,自然也允許動態的刪除屬性,就是通過這個運算子來操作。

  這個在物件一節還會拿出來講,因為並不是所有的屬性都可以成功被刪除的,屬性可以設定為不可配置,此時就無法通過 delete 來刪除。

  另外,之前也說過,在函式外宣告的全域性變數,本質上都是以屬性的形式被存在在全域性物件上的,但這些通過 var 或 function 宣告的全域性變數,無法通過 delete 來進行刪除。

  之前也說過,如果在宣告變數時,不小心漏掉了 var 關鍵字,此時程式並不會出異常,因為漏掉 var 關鍵字對一個不存在的變數進行賦值操作,會被 js 直譯器認為這行程式碼是要動態的為全域性物件新增一個屬性,這個動態新增的屬性就可以通過 delete 來進行刪除,因為動態新增的屬性預設都是可配置的。

  instanceof 運算子

  在 Java 中,可以通過 instanceof 運算子來判斷某個物件是否是從指定類例項化出來的,也可以用於判斷一群物件是否屬於同一個類的例項。

  在 JavaScript 中有些區別,但也有些類似。

  var b = {}function A() {}A.prototype = b;var a = new A();if (a instanceof A) { //符合,因為 a 是從A例項化的,繼承自A.prototype即b console.log("true"); }function B() {}B.prototype = b;var c = new B();if (c instanceof A) {//符合,雖然c是從B例項化的,但c也同樣繼承自b,而A.prototype指向b,所以滿足 console.log("true");}if (c instanceof Object) {//符合,雖然 c 是繼承自 b,但 b 繼承自 Object.prototype,所以c的原型鏈中有 Object.prototype console.log("true");}

  在 JavaScript 中,instanceof 運算子的左側是物件,右側是建構函式。但他們的判斷是,只要左側物件的原型鏈中包括右側建構函式的 prototype 指向的原型,那麼條件就滿足,即使左側物件不是從右側建構函式例項化的物件。

  例子程式碼看不懂麼事,這個在後續介紹原型時,還會再拿出來說,先清楚有這麼個運算子,運算子大概的作用是什麼就可以了。

  大連包皮手術哪家好 http://bpmobile.84211111.com/

  大連婦科醫院排名 http://yyk.39.net/dl/zonghe/f9a8f.html