空の配列は同時にtrueとfalseに等しいようです


201

空の配列はtrueですが、falseにも等しくなります。

var arr = [];
console.log('Array:', arr);
if (arr) console.log("It's true!");
if (arr == false) console.log("It's false!");
if (arr && arr == false) console.log("...what??");

これは、等価演算子によって実行される暗黙の変換によるものだと思います。

誰かが舞台裏で何が起こっているのか説明できますか?


1
ここで問題にいくつかの光を当てる必要があります同様のスレッドです:stackoverflow.com/questions/4226101/...
リオンウィリアムズ

2
これarr == trueは真と評価されないことに注意してください;-)
Michael Krelin-ハッカー

5
うわー...あなたがこれをすべて持っていると思ったちょうどその時。
harpo

3
Javascriptタイプの強制WTFを回避するには、厳密な等価演算子を使用し===ます。次に、アレイの空性をテストする場合は、arr === []
DjebbZ

17
配列の空性をテストする場合は、を使用しarr === []ないでください。右側が新しい配列をインスタンス化しているため、常にfalseを返します。左側の変数は、作成したばかりのものを参照できません。空をテストすることは調べることによって行われるべきarr.length === 0です。
カイルベイカー

回答:


274

ここではさまざまなことをテストしています。

if (arr) オブジェクト(配列はJSのObjectのインスタンス)で呼び出されると、オブジェクトが存在するかどうかが確認され、true / falseが返されます。

呼び出すと、このオブジェクトの値とプリミティブ値if (arr == false)を比較しますfalse。内部的にarr.toString()呼び出され、空の文字列を返します""

これはtoString、Arrayで呼び出されたときにが返されArray.join()、空の文字列がJavaScriptの不正な値の1つであるためです。


2
なぜBoolean([])戻るのtrueか説明できますか?
Devy

11
これは慣例によるものです。JSでは、オブジェクトがブール型に強制変換される場合、オブジェクトは常にTRUEに強制変換されます。javascript.info/tutorial/object-conversion
Niki

2
@Devy JavaScriptのすべてのオブジェクトは真実であるため、オブジェクトをブール値に変換することは真です。2ality.com/2013/08/objects-truthy.htmlを
Thomson

62

ラインについて:

if (arr == false) console.log("It's false!");

多分これらは助けるでしょう:

console.log(0 == false) // true
console.log([] == 0) // true
console.log([] == "") // true

私が起こっていると私が信じているのは、ブール値false0オブジェクトとの比較のために強制されているということです(左側)。オブジェクトは文字列(空の文字列)に強制変換されます。次に、空の文字列は強制的に数値、つまりゼロになります。そして、最終的な比較は0== 0、つまりですtrue

編集:動作の詳細については、仕様のこのセクションをご覧ください。

ルール#1から始まるのは次のとおりです。

1. Type(x)がType(y)と異なる場合は、手順14に進みます。

適用される次のルールは#19です。

19. Type(y)がブールの場合、比較結果x == ToNumber(y)を返します。

の結果ToNumber(false)0なので、次のようになります。

[] == 0

ここでも、ルール#1はステップ#14にジャンプするように指示していますが、実際に適用される次のステップは#21です。

21. Type(x)がObjectでType(y)がStringまたはNumberの場合、ToPrimitive(x)== yの比較結果を返します。

の結果ToPrimitive([])は空の文字列なので、次のようになります。

"" == 0

ここでも、ルール#1はステップ#14にジャンプするように指示していますが、実際に適用される次のステップは#17です。

17. Type(x)が文字列でType(y)が数値の場合、比較の結果ToNumber(x)== yを返します。

の結果はToNumber("")0、次のようになります。

0 == 0

これで、両方の値の型が同じになるため、手順は#1から#7まで続きます。

7. xがyと同じ数値の場合、trueを返します。

それで、戻りtrueます。

簡単に言えば:

ToNumber(ToPrimitive([])) == ToNumber(false)

2
素晴らしい参照!混乱を避けるために、ルール#1が「ステップ14に進む」と言っていても、「適用される次のルールは#19」である理由は、ステップ14から18がタイプに一致しないためです。比較される値。
Sean the Bean

2
いい説明。空の配列は真実であると見なされ、0は偽であり、それでも[] == 0真であるというのは私には不可解です。仕様の説明に基づいてこれがどのように行われるかを理解しますが、論理的な観点からは奇妙な言語動作のように見えます。
bigh_29 2018年

7

ウェインの回答を補足し、なぜToPrimitive([])戻るのかを説明するため""に、「なぜ」の質問に対する2つのタイプの回答を検討する価値があります。最初のタイプの答えは次のとおりです。「仕様では、これはJavaScriptがどのように動作するかを示しているためです。」ES5仕様のセクション9.1では、ToPrimitiveの結果をオブジェクトのデフォルト値として説明しています。

オブジェクトのデフォルト値を取得するには、オブジェクトの[[DefaultValue]]内部メソッドを呼び出し、オプションのヒントPreferredTypeを渡します。

8.12.8項でこの[[DefaultValue]]方法について説明します。このメソッドは引数として「ヒント」を取り、ヒントは文字列または数値のいずれかです。詳細を省略して問題を単純化するために、ヒントがString [[DefaultValue]]toString()場合、存在する場合はの値を返し、プリミティブ値を返しますvalueOf()。それ以外の場合はの値を返します。ヒントがNumberの場合、toString()およびの優先順位valueOf()が逆になるため、valueOf()最初に呼び出され、プリミティブの場合はその値が返されます。したがって、かどうかを[[DefaultValue]]返すの結果toString()またはvalueOf()目的のために指定されるPreferredTypeに依存しているか否かを、これらの機能は、プリミティブ値を返します。

デフォルトのvalueOf()Objectメソッドはオブジェクト自体を返すだけです。つまり、クラスがデフォルトのメソッドをオーバーライドしない限りvalueOf()、オブジェクト自体を返すだけです。これはの場合ですArray[].valueOf()オブジェクト[]自体を返します。以来、Arrayオブジェクトがプリミティブでない、[[DefaultValue]]ヒントは無関係である:配列の戻り値は、の値となりますtoString()

ちなみに、David FlanaganのJavaScript:The Definitive Guideを引用すると、これらの種類の質問への回答を得るために誰もが最初に参照できる素晴らしい本です。

このオブジェクトから数値への変換の詳細は、空の配列が数値0に変換される理由と、単一の要素を持つ配列も数値に変換される理由を説明しています。配列は、プリミティブ値ではなくオブジェクトを返すデフォルトのvalueOf()メソッドを継承するため、配列から数値への変換はtoString()メソッドに依存します。空の配列は空の文字列に変換されます。また、空の文字列は数値0に変換されます。単一の要素を持つ配列は、その1つの要素と同じ文字列に変換されます。配列に単一の数値が含まれている場合、その数値は文字列に変換されてから、数値に戻ります。

「仕様が言っているから」以外の「なぜ」の質問に対する2番目のタイプの回答は、動作が設計の観点から理にかなっている理由を説明しています。この問題については、推測しかできません。まず、配列を数値に変換するにはどうすればよいですか?空の配列を0に、空でない配列を1に変換するのが唯一の賢明な可能性だと思いますが、Wayneの答えが明らかにしたように、空の配列はとにかく多くのタイプの比較で0に変換されます。これ以外に、Array.valueOf()の賢明なプリミティブな戻り値を考えるのは困難です。したがってArray.valueOf()、デフォルトにしてArray自体を返すほうが理にかなっていてtoString()、ToPrimitiveで使用される結果になると主張する人もいます。配列を数値ではなく文字列に変換するほうが理にかなっています。

さらに、フラナガンの引用が示唆するように、この設計決定は特定のタイプの有益な行動を可能にします。例えば:

var a = [17], b = 17, c=1;
console.log(a==b);      // <= true
console.log(a==c);      // <= false

この動作により、単一要素の配列を数値と比較して、期待される結果を得ることができます。


この回答をありがとう、それは質問が欠けていることの非常に詳細な説明です。
Estus Flask 2017

3
console.log('-- types: undefined, boolean, number, string, object --');
console.log(typeof undefined);  // undefined
console.log(typeof null);       // object
console.log(typeof NaN);        // number
console.log(typeof false);      // boolean
console.log(typeof 0);          // number
console.log(typeof "");         // string
console.log(typeof []);         // object
console.log(typeof {});         // object

console.log('-- Different values: NotExist, Falsy, NaN, [], {} --');
console.log('-- 1. NotExist values: undefined, null have same value --');
console.log(undefined == null); // true

console.log('-- 2. Falsy values: false, 0, "" have same value --');
console.log(false == 0);        // true
console.log(false == "");       // true
console.log(0 == "");           // true

console.log('-- 3. !NotExist, !Falsy, and !NaN return true --');
console.log(!undefined);        // true
console.log(!null);             // true

console.log(!false);            // true
console.log(!"");               // true
console.log(!0);                // true

console.log(!NaN);              // true

console.log('-- 4. [] is not falsy, but [] == false because [].toString() returns "" --');
console.log(false == []);       // true
console.log([].toString());     // ""

console.log(![]);               // false

console.log('-- 5. {} is not falsy, and {} != false, because {}.toString() returns "[object Object]" --');
console.log(false == {});       // false
console.log({}.toString());     // [object Object]

console.log(!{});               // false

console.log('-- Comparing --');
console.log('-- 1. string will be converted to number or NaN when comparing with a number, and "" will be converted to 0 --');
console.log(12 < "2");          // false
console.log("12" < "2");        // true
console.log("" < 2);            // true

console.log('-- 2. NaN can not be compared with any value, even if NaN itself, always return false --');
console.log(NaN == NaN);        // false

console.log(NaN == null);       // false
console.log(NaN == undefined);  // false
console.log(0 <= NaN);          // false
console.log(0 >= NaN);          // false
console.log(undefined <= NaN);  // false
console.log(undefined >= NaN);  // false
console.log(null <= NaN);       // false
console.log(null >= NaN);       // false

console.log(2 <= "2a");         // false, since "2a" is converted to NaN
console.log(2 >= "2a");         // false, since "2a" is converted to NaN

console.log('-- 3. undefined can only == null and == undefined, and can not do any other comparing even if <= undefined --');
console.log(undefined == null);         // true
console.log(undefined == undefined);    // true

console.log(undefined == "");           // false
console.log(undefined == false);        // false
console.log(undefined <= undefined);    // false
console.log(undefined <= null);         // false
console.log(undefined >= null);         // false
console.log(0 <= undefined);            // false
console.log(0 >= undefined);            // false

console.log('-- 4. null will be converted to "" when <, >, <=, >= comparing --');
console.log(12 <= null);        // false
console.log(12 >= null);        // true
console.log("12" <= null);      // false
console.log("12" >= null);      // true

console.log(0 == null);         // false
console.log("" == null);        // false

console.log('-- 5. object, including {}, [], will be call toString() when comparing --');
console.log(12 < {});           // false, since {}.toString() is "[object Object]", and then converted to NaN
console.log(12 > {});           // false, since {}.toString() is "[object Object]", and then converted to NaN
console.log("[a" < {});         // true, since {}.toString() is "[object Object]"
console.log("[a" > {});         // false, since {}.toString() is "[object Object]"
console.log(12 < []);           // false, since {}.toString() is "", and then converted to 0
console.log(12 > []);           // true, since {}.toString() is "", and then converted to 0
console.log("[a" < []);         // false, since {}.toString() is ""
console.log("[a" > []);         // true, since {}.toString() is ""

console.log('-- 6. According to 4 and 5, we can get below weird result: --');
console.log(null < []);         // false
console.log(null > []);         // false
console.log(null == []);        // false
console.log(null <= []);        // true
console.log(null >= []);        // true

2

if(arr)では、JavaScriptのすべてのオブジェクトがtrueであるため、arrがオブジェクトの場合は常にtrueに評価されます(ToBoolean)。(nullはオブジェクトではありません!)

[] == false反復的なアプローチで評価されます。最初は、一方==がプリミティブで、もう一方がオブジェクトの場合、最初にオブジェクトをプリミティブに変換し、両側がそうでない場合は両側をNumberに変換しstringます(両側が文字列の場合は文字列比較が使用されます)。したがって、比較は[] == false-> '' == false-> 0 == 0->のように繰り返されtrueます。


2

例:

const array = []
const boolValueOfArray = !!array // true

それは

ToNumber(ToPrimitive([])) == ToNumber(false)  
  1. []空のArrayオブジェクトです→→ ToPrimitive([])"" ToNumber("")→→0
  2. ToNumber(false) →0
  3. 0 == 0→true

1

要素を持つ配列(0、false、または別の空の配列に関係なく)は、常にtrueAbstract Equality Comparison を使用して解決され==ます。

1. [] == false; // true, because an empty array has nothing to be truthy about
2. [2] == false; // false because it has at least 1 item
3. [false] == false; // also false because false is still an item
4. [[]] == false; // false, empty array is still an item

しかし、厳密な等価比較を使用して===、変数の内容とそのデータ型を評価しようとしています。その理由は次のとおりです。

1. [] === false; // false, because an array (regardless of empty or not) is not strictly comparable to boolean `false`
2. [] === true; // false, same as above, cannot strictly compare [] to boolean `true`
3. [[]] === false; // true, because see #1

-1

list = []現在参照されている配列の要素を使用または削除して、新しい配列を参照してJavaScript配列を空にすることができます。list.length = 0ます。

ソース:JavaScript空配列


-2

上記のいずれも、nockout.jsマッピングプラグインを使用しようとしたときに役に立ちませんでした。おそらく「空の配列」が実際には空ではないためです。

私は結局使用しました:data-bind="if: arr().length"トリックをしました。

これはOPの質問ではなく、ノックアウトに固有のものですが、同じような状況で他の誰かがここを閲覧するのに役立つかもしれません。


この回答は関係ありません
フォーヴェリズム

接線のみ、免責事項:)
domoarigato 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.