淺析js中的every()對(duì)空數(shù)組總返回true
JavaScript 語言的核心部分足夠大,以至于我們很容易誤解其某些部分的工作方式。最近在重構(gòu)一些使用 every()
方法的代碼時(shí),發(fā)現(xiàn)實(shí)際上并不理解其背后的邏輯。在我的理解中,我認(rèn)為回調(diào)函數(shù)必須被調(diào)用并返回true
, every()
才會(huì)返回 true
,但實(shí)際上并非如此。對(duì)于一個(gè)空數(shù)組, every()
無論回調(diào)函數(shù)是什么都會(huì)返回 true
,因?yàn)槟莻€(gè)回調(diào)函數(shù)從未被調(diào)用過。考慮以下情況:
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
在這個(gè)例子的每個(gè)情況中,對(duì) every()
的調(diào)用都會(huì)檢查數(shù)組中的每個(gè)項(xiàng)目是否為數(shù)字。前四次調(diào)用相當(dāng)直接, every()
產(chǎn)生了預(yù)期的結(jié)果?,F(xiàn)在考慮以下這些例子:
[].every(() => true); // true [].every(() => false); // true
這可能更令人驚訝:返回 true
或 false
的回調(diào)函數(shù)具有相同的結(jié)果。這只能發(fā)生的唯一原因是如果回調(diào)函數(shù)沒有被調(diào)用,而 every()
的默認(rèn)值是 true
。但是,為什么在沒有值來運(yùn)行回調(diào)函數(shù)時(shí),空數(shù)組會(huì)返回 true
給 every()
呢?
要理解為什么,我們需要仔細(xì)看看規(guī)范是如何描述這個(gè)方法的。
實(shí)現(xiàn) every()
ECMA-262 定義了一個(gè) 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()
假設(shè)結(jié)果是 true
,并且只有在回調(diào)函數(shù)對(duì)數(shù)組中的任何一項(xiàng)返回 false
時(shí)才返回 false
。如果數(shù)組中沒有任何項(xiàng)目,那么就沒有機(jī)會(huì)執(zhí)行回調(diào)函數(shù),因此,該方法無法返回 false
。
現(xiàn)在的問題是:為什么 every() 會(huì)表現(xiàn)出這樣的行為?
在數(shù)學(xué)和JavaScript中的“對(duì)所有”的量詞
MDN頁面 提供了為什么 every()
會(huì)對(duì)空數(shù)組返回 true
的答案:
every 的行為就像數(shù)學(xué)中的“全稱量詞”。特別是對(duì)于空數(shù)組,它返回真值。(空集中的所有元素都滿足任何給定條件,這是顯然的真理。)
空真(Vacuous truth)是一個(gè)數(shù)學(xué)概念,意味著如果給定的條件(稱為前件)不能被滿足(即給定的條件不為真),那么某件事就是真的。用JavaScript的術(shù)語來說,every()
對(duì)于一個(gè)空集合返回 true
,因?yàn)闆]有辦法調(diào)用回調(diào)函數(shù)。回調(diào)函數(shù)代表要測(cè)試的條件,如果由于數(shù)組中沒有值而無法執(zhí)行它,那么 every()
必須返回 true
。
“全稱量詞”("for all" quantifier)是數(shù)學(xué)中更大主題“全稱量化”(universal quantification)的一部分,它允許你對(duì)數(shù)據(jù)集進(jìn)行推理??紤]到JavaScript數(shù)組在進(jìn)行數(shù)學(xué)計(jì)算方面的重要性,尤其是在使用類型數(shù)組(typed arrays)的情況下,內(nèi)置支持這樣的操作是合理的。而**every()**方法并不是唯一的例子。
在數(shù)學(xué)和JavaScript中的“存在量詞”
JavaScript的 some()
方法實(shí)現(xiàn)了存在量化(existential quantification)中的“存在量詞”(“存在”有時(shí)也被稱為“存在”或“對(duì)某些”)。這個(gè)“存在量詞”規(guī)定,對(duì)于任何空集合,結(jié)果都是假的。因此,some()
方法對(duì)空集合返回 false
,并且也不會(huì)執(zhí)行回調(diào)函數(shù)。以下是一些相關(guān)的示例:
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并不是唯一實(shí)現(xiàn)了集合或可迭代對(duì)象的量化方法的編程語言:
- Python:
all()
函數(shù)實(shí)現(xiàn)了“對(duì)所有” ,而any()
函數(shù)實(shí)現(xiàn)了“存在” 。 - Rust:
Iterator::all()
方法實(shí)現(xiàn)了“對(duì)所有” ,而any()
函數(shù)實(shí)現(xiàn)了“存在” 。
“全稱量詞”(for all)的 every() 方法的含義與影響
無論你是否認(rèn)為 every()
方法的行為違反直覺都是可以討論的。然而,無論你的觀點(diǎn)如何,你都需要了解 every()
的“全稱量詞”(for all)特性以避免錯(cuò)誤。簡而言之,如果你使用 every() 方法或可能為空的數(shù)組,你應(yīng)該事先進(jìn)行明確的檢查。例如,如果你有一個(gè)依賴于數(shù)字?jǐn)?shù)組的操作,并且在數(shù)組為空時(shí)會(huì)失敗,那么在使用 every()
之前,你應(yīng)該檢查數(shù)組是否為空。
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); } }
再次強(qiáng)調(diào),只有當(dāng)你有一個(gè)數(shù)組在為空時(shí)不應(yīng)該被用于操作時(shí),這才重要;否則,你可以避免這個(gè)額外的檢查。
結(jié)論
當(dāng)我第一次看到 every()
在空數(shù)組上的行為時(shí),我感到很驚訝,但一旦你理解了這個(gè)操作的更大背景和這個(gè)功能在各種語言中的廣泛應(yīng)用,就會(huì)覺得它是有道理的。如果你也對(duì)這個(gè)行為感到困惑,那么我建議你改變閱讀 every()
調(diào)用的方式。不要把 every()
理解為“這個(gè)數(shù)組中的每一項(xiàng)是否都符合這個(gè)條件?”而應(yīng)該理解為“這個(gè)數(shù)組中是否有任何一項(xiàng)不符合這個(gè)條件?”這種思維方式的轉(zhuǎn)變可以幫助你避免在未來的JavaScript代碼中出現(xiàn)錯(cuò)誤。
以上就是淺析js中的every()對(duì)空數(shù)組總返回true的詳細(xì)內(nèi)容,更多關(guān)于js every()的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
js實(shí)現(xiàn)數(shù)組和對(duì)象的深淺拷貝
這篇文章主要為大家詳細(xì)介紹了js實(shí)現(xiàn)數(shù)組和對(duì)象的深淺拷貝,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09JS實(shí)現(xiàn)的數(shù)組全排列輸出算法
這篇文章主要介紹了JS實(shí)現(xiàn)的數(shù)組全排列輸出算法,實(shí)例分析了全排列的原理與相關(guān)的javascript實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-03-03Angular組件拿不到@Input輸入屬性問題探究解決方法
最近在工作中實(shí)現(xiàn)一個(gè)feature的時(shí)候,碰到一個(gè)小問題:Angular組件拿不到@Input輸入屬性的問題,盡管對(duì)這些問題都比較了解,但是找問題是需要一個(gè)過程的,所以還是把這個(gè)問題總結(jié)記錄了下2023-01-01ionic2自定義cordova插件開發(fā)以及使用(Android)
這篇文章主要為大家詳細(xì)介紹了ionic2自定義cordova插件開發(fā)以及使用,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06js實(shí)現(xiàn)小球在頁面規(guī)定的區(qū)域運(yùn)動(dòng)
這篇文章主要為大家詳細(xì)介紹了js控制小球在規(guī)定范圍運(yùn)動(dòng),碰到邊界就改變運(yùn)動(dòng)方向,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-06-06三劍客:offset、client和scroll還傻傻分不清?
這篇文章主要給大家介紹了三劍客:offset,client和scroll的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12javascript中this做事件參數(shù)相關(guān)問題解答
有關(guān)this想必大家早有所耳聞,只不過在使用中不是那么在意而已,接下來為大家介紹下this做事件參數(shù)問題,感興趣的你可不要錯(cuò)過了哈2013-03-03