Пустые массивы верны, но они также равны 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??");
Пустые массивы верны, но они также равны 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??");
Здесь вы тестируете разные вещи.
if (arr)
вызываемый объект (Array - экземпляр объекта в JS) проверяет наличие объекта и возвращает true/false.
Когда вы вызываете if (arr == false)
, вы сравниваете значения этого объекта и примитивного значения false
. Внутри вызывается arr.toString()
, который возвращает пустую строку ""
.
Это потому, что toString
, вызываемый в Array, возвращает Array.join()
, а пустая строка - это одно из значений фальшивки в JavaScript.
Относительно строки:
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)
Чтобы дополнить ответ Уэйна и попытаться объяснить, почему 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
Это поведение позволяет сравнить одноэлементный массив с числами и получить ожидаемый результат.
В if (arr) он всегда оценивается (ToBoolean) в true, если arr является объектом, потому что все объекты в JavaScript являются правдивыми, (null - это не объект!)
[] == false
оценивается в итеративном подходе. Сначала, если одна сторона ==
является примитивной, а другая - объектом, она сначала преобразует объект в примитив, а затем преобразует обе стороны в число, если обе стороны не string
(сравнение строк используется, если обе стороны являются строками). Таким образом, сравнение повторяется так: [] == false
→ '' == false
→ 0 == 0
→ true
.
Пример:
const array = []
const boolValueOfArray = !!array // true
Это происходит потому что
ToNumber(ToPrimitive([])) == ToNumber(false)
[]
является пустым объектом Array
→ ToPrimitive([])
→ "" → ToNumber("")
→ 0
ToNumber(false)
→ 0 Вы можете очистить массив JavaScript, ссылаясь на него в новом массиве, используя list = []
или удалив элементы текущего массива list.length = 0
.
Источник: JavaScript Пустой массив
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
Ничто из перечисленного не помогло мне при попытке использовать плагин отображения knockout.js, возможно, поскольку "пустой массив" на самом деле не пуст.
В итоге я использовал: data-bind="if: arr().length"
, который сделал трюк.
Это специфично для нокаута, а не для вопроса ОП, но, возможно, это поможет кому-то другому в этой ситуации.