Пустые массивы кажутся равными true и false одновременно

Пустые массивы верны, но они также равны false.

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

Ответ 1

Здесь вы тестируете разные вещи.

if (arr) вызываемый объект (Array - экземпляр объекта в JS) проверяет наличие объекта и возвращает true/false.

Когда вы вызываете if (arr == false), вы сравниваете значения этого объекта и примитивного значения false. Внутри вызывается arr.toString(), который возвращает пустую строку "".

Это потому, что toString, вызываемый в Array, возвращает Array.join(), а пустая строка - это одно из значений фальшивки в JavaScript.

Ответ 2

Относительно строки:

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

Возможно, это поможет:

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

Я считаю, что логическое false привязано к 0 для сравнения с объектом (левая сторона). Объект принуждается к строке (пустая строка). Затем пустая строка также вводится в число, а именно, ноль. Итак, окончательное сравнение 0 == 0, которое true.

Изменить: Подробнее о том, как это работает, см. в этом разделе спецификации.

Вот что происходит, начиная с правила № 1:

1. Если Тип (x) отличается от Type (y), перейдите к шагу 14.

Следующее правило, которое применяется, - # 19:

19. Если Type (y) является логическим, верните результат сравнения x == ToNumber (у).

Результат 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) - String, а Type (y) - Number, верните результат сравнение ToNumber (x) == y.

Результат ToNumber("") равен 0, что оставляет нам:

0 == 0

Теперь оба значения имеют один и тот же тип, поэтому шаги продолжаются от # 1 до # 7, в котором говорится:

7. Если x - это то же числовое значение, что и y, верните true.

Итак, мы возвращаем true.

Вкратце:

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

Ответ 3

Чтобы дополнить ответ Уэйна и попытаться объяснить, почему ToPrimitive([]) возвращает "", стоит рассмотреть два возможных типа ответов на вопрос "почему". Первый тип ответа: "потому что спецификация говорит, что так будет вести себя JavaScript". В спецификации ES5 раздел 9.1, в котором описывается результат ToPrimitive в качестве значения по умолчанию для объекта Object:

Значение по умолчанию для объекта извлекается, вызывая внутренний метод [[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: окончательное руководство, которое, кстати, является превосходной книгой, в которой должно быть все первое место получить ответы на эти типы вопросов:

Подробности этого преобразования между объектами и номерами объясняют, почему пустой массив преобразуется в число 0 и почему массив с одним элементом может также преобразовываться в число. Массивы наследуют метод valueOf() по умолчанию, который возвращает объект, а не примитивное значение, поэтому преобразование между массивами и цифрами зависит от метода toString(). Пустые массивы преобразуются в пустую строку. И пустая строка преобразуется в число 0. Массив с одним элементом преобразуется в ту же строку, что и этот элемент. Если массив содержит один номер, это число преобразуется в строку, а затем обратно в число.

Второй тип ответа на вопрос "почему", кроме "потому что спецификация говорит", дает некоторое объяснение, почему поведение имеет смысл с точки зрения дизайна. По этому вопросу я могу только догадываться. Во-первых, как преобразовать массив в число? Единственная разумная возможность, о которой я могу думать, состояла бы в том, чтобы преобразовать пустой массив в 0 и любой непустой массив в 1. Но, как показал ответ Wayne, пустой массив будет преобразован в 0 для многих типов сравнений. Помимо этого, трудно представить разумное примитивное возвращаемое значение для Array.valueOf(). Таким образом, можно утверждать, что имеет смысл иметь Array.valueOf() значение по умолчанию и возвращать сам массив, в результате toString() будет использоваться результат, используемый ToPrimitive. Просто имеет смысл преобразовать Array в строку, а не в число.

Более того, как намечено цитатой Фланагана, это дизайнерское решение действительно позволяет некоторым типам полезных поведения. Например:

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

Это поведение позволяет сравнить одноэлементный массив с числами и получить ожидаемый результат.

Ответ 4

В if (arr) он всегда оценивается (ToBoolean) в true, если arr является объектом, потому что все объекты в JavaScript являются правдивыми, (null - это не объект!)

[] == false оценивается в итеративном подходе. Сначала, если одна сторона == является примитивной, а другая - объектом, она сначала преобразует объект в примитив, а затем преобразует обе стороны в число, если обе стороны не string (сравнение строк используется, если обе стороны являются строками). Таким образом, сравнение повторяется так: [] == false'' == false0 == 0true.

Ответ 5

Пример:

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

Это происходит потому что

ToNumber(ToPrimitive([])) == ToNumber(false)  
  1. [] является пустым объектом ArrayToPrimitive([]) → "" → ToNumber("")0
  2. ToNumber(false) → 0
  3. 0 == 0 → верно

Ответ 6

Вы можете очистить массив JavaScript, ссылаясь на него в новом массиве, используя list = [] или удалив элементы текущего массива list.length = 0.

Источник: JavaScript Пустой массив

Ответ 7

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

Ответ 8

Ничто из перечисленного не помогло мне при попытке использовать плагин отображения knockout.js, возможно, поскольку "пустой массив" на самом деле не пуст.

В итоге я использовал: data-bind="if: arr().length", который сделал трюк.

Это специфично для нокаута, а не для вопроса ОП, но, возможно, это поможет кому-то другому в этой ситуации.