記錄--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
  • WPF本身不支持直接的3D繪圖,但是它提供了一些用於實現3D效果的高級技術。 如果你想要在WPF中進行3D繪圖,你可以使用兩種主要的方法: WPF 3D:這是一種在WPF應用程式中創建3D圖形的方式。WPF 3D提供了一些基本的3D形狀(如立方體、球體和錐體)以及一些用於控制3D場景和對象的工具(如 ...
  • 一、XML概述 XML(可擴展標記語言)是一種用於描述數據的標記語言,旨在提供一種通用的方式來傳輸和存儲數據,特別是Web應用程式中經常使用的數據。XML並不預定義標記。因此,XML更加靈活,並且可以適用於廣泛的應用領域。 XML文檔由元素(element)、屬性(attribute)和內容(con ...
  • 從今年(2023)三月份開始,Github開始強制用戶開啟兩步驗證2FA(雙因數)登錄驗證,毫無疑問,是出於安全層面的考慮,畢竟Github賬號一旦被盜,所有代碼倉庫都會毀於一旦,關於雙因數登錄的必要性請參見:別讓你的伺服器(vps)淪為肉雞(ssh暴力破解),密鑰驗證、雙向因數登錄值得擁有。 雙因 ...
  • 第一題 下列代碼輸入什麼? public class Test { public static Test t1 = new Test(); { System.out.println("blockA"); } static { System.out.println("blockB"); } publi ...
  • 本文主要涉及的問題:用ElementTree和XPath讀寫XML文件;解決ElementTree新增元素後再寫入格式不統一的問題;QTableWidget單元格設置控制項 ...
  • QStandardItemModel 類作為標準模型,主打“類型通用”,前一篇水文中,老周還沒提到樹形結構的列表,本篇咱們就好好探討一下這貨。 還是老辦法,咱們先做示例,然後再聊知識點。下麵這個例子,使用 QTreeView 組件來顯示數據,使用的列表模型比較簡單,只有一列。 #include <Q ...
  • 一、直充內充(充值方式) 直充: 包裝套餐直接充值到上游API系統。【PID/Smart】 (如:支付寶、微信 話費/流量/語音/簡訊 等 充值系統)。 內充(套餐打包常見物聯卡系統功能): 套餐包裝 適用於不同類型套餐 如 流量、簡訊、語音 等。 (目前已完善流量邏輯) 二、套餐與計費產品 計費產 ...
  • 在前面幾天中,我們學習了Dart基礎語法、可迭代集合,它們是Flutter應用研發的基本功。今天,我們繼續學習Flutter應用另一個必須掌握知識點:非同步編程(即Future和async/await)。它類似於Java中的FutureTask、JavaScript中的Promise。它是後續Flut... ...
  • 針對改動範圍大、影響面廣的需求,我通常會問上線了最壞情況是什麼?應急預案是什麼?你帶開關了嗎?。當然開關也是有成本的,接下來本篇跟大家一起交流下高頻發佈支撐下的功能開關技術理論與實踐結合的點點滴滴。 ...
  • 1.d3.shuffle D3.shuffle() 方法用於將數組中的元素隨機排序。它使用 Fisher–Yates 洗牌演算法,該演算法是無偏的,具有最佳的漸近性能(線性時間和常數記憶體)。 D3.shuffle() 方法的語法如下: d3.shuffle(array, [start, end]) 其中 ...