這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 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 代碼中避免錯誤。