記錄--JavaScript 令人驚訝的一點:對於空數組every()方法返回true

来源:https://www.cnblogs.com/smileZAZ/archive/2023/09/18/17712752.html
-Advertisement-
Play Games

這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 JavaScript 語言的內核足夠大,導致我們很容易誤解它的某些部分是如何工作的。我最近重構了一些使用 every ()方法的代碼,並且發現我並不真正理解every()的邏輯。在我看來,我認為回調函數必須被調用並返回 true的時候ev ...


這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助

JavaScript 語言的內核足夠大,導致我們很容易誤解它的某些部分是如何工作的。我最近重構了一些使用 every ()方法的代碼,並且發現我並不真正理解every()的邏輯。在我看來,我認為回調函數必須被調用並返回 true的時候every() 才能返回 true,但事實並非如此。但是對於空數組,不管回調函數是什麼,every ()都返回 true,因為根本不會調用該回調函數。看一下例子:
function isNumber(value) {
    return typeof value === "number";
}

[1].every(isNumber);            // true
["1"].every(isNumber);          // false
[1, 2, 3].every(isNumber);      // true
[1, "2", 3].every(isNumber);    // false
[].every(isNumber);             // true

在此示例的每種情況下,均調用 every ()來檢查數組中的每一項是否為數字。前四個調用相當簡單,每個都會產生預期的結果。考慮如下的例子:

[].every(() => true);           // true
[].every(() => false);          // true

這樣的結果可能更令人感到驚訝: 對於every(),返回 true 或 false 的回調都具有相同的結果。發生這種情況的唯一原因是調用回調函數沒有被調用,並且 every ()的預設返回值為 true。但是,當沒有值可以用來運行回調函數時,為什麼空數組對 every ()返回 true呢?

為了理解其中的原因,重要的是看看規範是如何描述這種方法的。

實現every()方法

ECMA-262定義了一個 Array.Prototype.every ()演算法,該演算法大致可以翻譯成這段 JavaScript 代碼:

Array.prototype.every = function(callbackfn, thisArg) {
  const O = this;
  const len = O.length;
  if (typeof callbackfn !== "function") {
      throw new TypeError("Callback isn't callable");
  }
  let k = 0;
  while (k < len) {
      const Pk = String(k);
      const kPresent = O.hasOwnProperty(Pk);
      if (kPresent) {
          const kValue = O[Pk];
          const testResult = Boolean(callbackfn.call(thisArg, kValue, k, O));
          if (testResult === false) {
              return false;
          }
      }
      k = k + 1;
  }
  return true;
};

從代碼中可以看出,every ()假定結果為 true,並且只有在回調函數對數組中的任何一項返回 false 時才返回 false。如果數組中沒有元素,那麼就沒有機會執行回調函數,因此方法就沒有辦法返回 false。

現在的問題是:為什麼every()要這樣做?

數學和 JavaScript 中的全稱量詞

譯者註:全稱量詞是指“所有”的概念,常用符號為∀。例如,對於集合S中的元素x,可以表示為∀x∈S,意為“對於S中的每一個元素x都成立”

MDN 提供了為什麼 every ()對於空數組返回 true 的答案:

every 和數學中的全稱量詞"任意(∀)"類似。特別的,對於空數組,它只返回 true。
(這種情況屬於無條件正確,因為空集的所有元素都符合給定的條件。)

無條件正確是一個數學概念,它意味著如果一個給定的條件(稱為先行條件)不能被滿足(也就是說,給定的條件是不真實的) ,那麼某些東西就是真的。要將其應用到 JavaScript 中,那就是every ()對於空數組返回 true,因為沒有辦法調用回調函數 。回調代表了要測試的條件,如果由於數組中沒有值而無法執行,那麼 every ()必須返回 true。

全稱量詞是數學中一個更大的主題的一部分,這個主題被稱為“全稱量化”,它允許你對數據集進行推理。考慮到 JavaScript 數組對於執行數學計算的重要性,特別是對於類型化數組,內置支持這種操作是有意義的。要知道,every()並不是唯一的例子。

數學和 JavaScript 中的存在量詞

譯者註: 存在量詞是指“存在”的概念,常用符號為∃。例如,對於集合S中的元素x,可以表示為∃x∈S,意為“存在S中的一個元素x”

JavaScript 的some()方法實現了存在量詞。“存在”量詞指出,對於任何空集,結果都是 false。因此,some ()方法對於空數組返回 false,並且也不執行回調函數。下麵是一些例子:

function isNumber(value) {
    return typeof value === "number";
}
[1].some(isNumber);            // true
["1"].some(isNumber);          // false
[1, 2, 3].some(isNumber);      // true
[1, "2", 3].some(isNumber);    // true
[].some(isNumber);             // false
[].some(() => true);           // false
[].some(() => false);          // false

其他語言對量詞的實現

JavaScript 並不是唯一的一種為集合或可迭代對象實現了量詞相關方法的編程語言:

Python: all ()函數實現全稱量詞,而 any ()函數實現了存在量詞。

Rust: Iterator: : all ()函數實現了全稱量詞,而 any ()函數實現了存在量詞。

因此,JavaScript 與 every ()和 some ()都有著良好的合作關係。

意味著全稱量詞的every()

你是否認為evey()的行為是違反直覺的?這可有待商榷。然而,不管您的觀點如何,您都需要了every()所具有的全程量詞的性質以避免錯誤。簡而言之,如果可能為空的數組使用了every (),則應該事先添加一個顯式檢查。例如,如果您有一個依賴於數字數組的操作,並且該操作將以空數組失敗,那麼您應該在使用 every ()之前檢查該數組是否為空:

function doSomethingWithNumbers(numbers) {
    // first check the length
    if (numbers.length === 0) {
        throw new TypeError("Numbers array is empty; this method requires at least one number.");
    }
    // now check with every()
    if (numbers.every(isNumber)) {
        operationRequiringNonEmptyArray(numbers);
    }
}

註意,只有當數組為空時不能執行某操作時,這樣做是有必須要的。否則,可以避免這種額外的檢查。

總結

雖然我對空數組上執行 every ()方法的執行結果感到驚訝,可是當我瞭解了某個操作的更大範圍的上下文以及這種功能在各種語言之間的表現時,我就會感到釋然。如果您也對這種行為感到困惑,那麼我建議您在遇到every()的調用時改變你對它的理解。不再將 every ()讀作“此數組中的每個項是否匹配此條件?”而是讀作“數組中有沒有不符合條件的項?”這種思維上的轉變可以幫助您在以後的 JavaScript 代碼中避免錯誤。

本文轉載於:

https://juejin.cn/post/7279093851000062010

如果對您有所幫助,歡迎您點個關註,我會定時更新技術文檔,大家一起討論學習,一起進步。

 


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

-Advertisement-
Play Games
更多相關文章
  • Ubuntu20.04安裝Mysql8主從 一.主資料庫安裝 1.下載安裝包並初始化資料庫 # 進入目錄 cd /opt # 下載安裝包 wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.20-linux-glibc2.12-x ...
  • 本文分享自華為雲社區《DTSE Tech Talk | 第43期:數倉數據可靠保證——物理細粒度備份恢復》,作者:華為雲社區精選。 大數據時代,數據對企業的重要性不言而喻,如果發生數據丟失或因為誤操作而造成數據丟失,將對企業的經營決策帶來不可估量的損失。本期《備份恢復全掌握,數倉數據更安全》的主題直 ...
  • UUID(通用唯一識別碼)是由32個十六進位數組成的無序字元串,通過一定的演算法計算出來。為了保證其唯一性,UUID規範定義了包括網卡MAC地址、時間戳、名字空間(Namespace)、隨機或偽隨機數、時序等元素,以及從這些元素生成UUID的演算法。一般來說,演算法可以保證任何地方產生的任意一個UUID都... ...
  • Redis是游戲資料庫重要選型之一,華為雲GaussDB(for Redis)能及時上報用戶下線行為,被廣泛應用於排行榜等多種業務場景。 ...
  • Ubuntu20.04安裝Postgres主從備份 一.查看可安裝的Postgres包 #列出相關的軟體包,這裡安裝的是14版本 apt list | grep -w postgresql-14 | tail -1 #下載Postgres apt install -y postgresql-14/f ...
  • 基於OpenHarmony和華為雲平臺打造的智能家居設備,分別為智能門鎖,儲物精靈 NFC版,儲物精靈Pro版三個設備。 ...
  • 之前在 《iOS16新特性:靈動島適配開發與到家業務場景結合的探索實踐》 里介紹了iOS16新的特性:實時更新(Live Activity)中靈動島的適配流程,但其實除了靈動島的展示樣式,Live Activity還有一種非常實用的應用場景,那就是鎖屏界面實時狀態更新: ...
  • 前端遠程調試方案 Chii 的使用經驗分享 Chii 是與 weinre 一樣的遠程調試工具 ,主要是將 web inspector 替換為最新的 chrome devtools frontend 監控列表頁面可以看到網站的標題鏈接,IP,useragent,可以快速定位調試頁面,監控頁信息完善,支 ...
一周排行
    -Advertisement-
    Play Games
  • 1、預覽地址:http://139.155.137.144:9012 2、qq群:801913255 一、前言 隨著網路的發展,企業對於信息系統數據的保密工作愈發重視,不同身份、角色對於數據的訪問許可權都應該大相徑庭。 列如 1、不同登錄人員對一個數據列表的可見度是不一樣的,如數據列、數據行、數據按鈕 ...
  • 前言 上一篇文章寫瞭如何使用RabbitMQ做個簡單的發送郵件項目,然後評論也是比較多,也是準備去學習一下如何確保RabbitMQ的消息可靠性,但是由於時間原因,先來說說設計模式中的簡單工廠模式吧! 在瞭解簡單工廠模式之前,我們要知道C#是一款面向對象的高級程式語言。它有3大特性,封裝、繼承、多態。 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 介紹 Nodify是一個WPF基於節點的編輯器控制項,其中包含一系列節點、連接和連接器組件,旨在簡化構建基於節點的工具的過程 ...
  • 創建一個webapi項目做測試使用。 創建新控制器,搭建一個基礎框架,包括獲取當天日期、wiki的請求地址等 創建一個Http請求幫助類以及方法,用於獲取指定URL的信息 使用http請求訪問指定url,先運行一下,看看返回的內容。內容如圖右邊所示,實際上是一個Json數據。我們主要解析 大事記 部 ...
  • 最近在不少自媒體上看到有關.NET與C#的資訊與評價,感覺大家對.NET與C#還是不太瞭解,尤其是對2016年6月發佈的跨平臺.NET Core 1.0,更是知之甚少。在考慮一番之後,還是決定寫點東西總結一下,也回顧一下.NET的發展歷史。 首先,你沒看錯,.NET是跨平臺的,可以在Windows、 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 添加節點(nodes) 通過上一篇我們已經創建好了編輯器實例現在我們為編輯器添加一個節點 添加model和viewmode ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...
  • 類型檢查和轉換:當你需要檢查對象是否為特定類型,並且希望在同一時間內將其轉換為那個類型時,模式匹配提供了一種更簡潔的方式來完成這一任務,避免了使用傳統的as和is操作符後還需要進行額外的null檢查。 複雜條件邏輯:在處理複雜的條件邏輯時,特別是涉及到多個條件和類型的情況下,使用模式匹配可以使代碼更 ...
  • 在日常開發中,我們經常需要和文件打交道,特別是桌面開發,有時候就會需要載入大批量的文件,而且可能還會存在部分文件缺失的情況,那麼如何才能快速的判斷文件是否存在呢?如果處理不當的,且文件數量比較多的時候,可能會造成卡頓等情況,進而影響程式的使用體驗。今天就以一個簡單的小例子,簡述兩種不同的判斷文件是否... ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...