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

来源:https://www.cnblogs.com/dasusu/archive/2018/12/02/10055476.html
-Advertisement-
Play Games

聲明 本系列文章內容全部梳理自以下幾個來源: 《JavaScript權威指南》 "MDN web docs" "Github:smyhvae/web" "Github:goddyZhao/Translation/JavaScript" 作為一個前端小白,入門跟著這幾個來源學習,感謝作者的分享,在其基 ...


聲明

本系列文章內容全部梳理自以下幾個來源:

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

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

正文-運算符

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

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

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

"+" 運算符

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

  1. 如果操作數中存在對象類型,先將其按照上節介紹的轉換規則,轉成原始值;
  2. 如果操作數已經全部是原始值,此時如果有字元串類型的原始值,那麼將兩個原始值都轉為字元串後,按字元串拼接操作處理;
  3. 如果操作數已經全部是原始值且沒有字元串類型的,那麼將操作數都轉為數字類型後,按數字的加法處理;
  4. 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,所以它是假值,所以邏輯與 && 直接返回左操作數的值 null

getName && 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 指向的原型,那麼條件就滿足,即使左側對象不是從右側構造函數實例化的對象。

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


大家好,我是 dasu,歡迎關註我的公眾號(dasuAndroidTv),公眾號中有我的聯繫方式,歡迎有事沒事來嘮嗑一下,如果你覺得本篇內容有幫助到你,可以轉載但記得要關註,要標明原文哦,謝謝支持~
dasuAndroidTv2.png


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 介紹 索引用於加快數據訪問的速度。把電腦的磁碟比作一本字典,索引就是欄位的目錄,當我們想快速查到某個詞語的時候只需要通過查詢目錄找到詞語所在的頁數,然後直接打開某頁就可以。MySQL最常用的索引是B+樹索引,為什麼使用B+作為MySQL的索引,這是許多面試官必問的問題。 為什麼B+樹 硬體相關知識 ...
  • 一.概述 Redis伺服器是一個事件驅動程式,伺服器需要處理兩類事件:1文件事件,2時間事件。文件事件是關於客戶端與伺服器之間的通信操作。時間事件是關於伺服器內部的一些定時操作。本篇還是參照"Redis設計與實現"書,簡要瞭解下Redis事件。 1. 文件事件 文件事件(file event)是服務 ...
  • 數據備份恢覆在資料庫管理中至關重要,今天,總結一下資料庫備份與恢復的方案,需要註意的方面和實際操作!、 ...
  • 大數據大數據,個個都在喊 但究竟什麼是大數據開發,如何成為一個大數據開發工程師? 我還是要推薦下我自己創建的大數據資料分享群142973723,這是大數據學習交流的地方,不管你是小白還是大牛,小編都歡迎,不定期分享乾貨,包括我整理的一份適合零基礎學習大數據資料和入門教程。 大數據通用處理平臺 1. ...
  • 時間序列資料庫簡稱時序資料庫(Time Series Database),用於處理帶時間標簽(按照時間的順序變化,即時間序列化)的數據,帶時間標簽的數據也稱為時間序列數據。 時序數據的幾個特點 1. 基本上都是插入,沒有更新的需求。 2. 數據基本上都有時間屬性,隨著時間的推移不斷產生新的數據。 3 ...
  • 0.準備工作: 關閉itunes 在任務管理器中殺掉iTunes開頭的服務 1,找到iTunes預設備份路徑:C:\Users\xxx\AppData\Roaming\Apple Computer\MobileSync xxx為里的用戶名 2,刪掉預設路徑下的MobileSync文件夾,以前有備份的 ...
  • frameset 定義 W3C是這樣定義frameset框架的,通過使用框架,你可以在同一個瀏覽器視窗中顯示不止一個頁面。每份HTML文檔稱為一個框架,並且每個框架都獨立於其他的框架。註意,這是HTML框架,不是前端框架,與node.js,vue.js等不同。 垂直切割 屬性為cols。例如: <f ...
  • 先說幾個概念: 1、js代碼從上往下執行 2、變數提升: 變數提升是瀏覽器的一個功能,在運行js代碼之前,瀏覽器會給js一個全局作用域叫window,window分兩個模塊,一個叫記憶體模塊,一個叫運行模塊,記憶體模塊找到當前作用域下的所有帶var和function的關鍵字,執行模塊執行js代碼,從上到 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...