Является ли JS Boolean с пользовательскими свойствами плохой практикой?

41

В JS вы можете вернуть логическое значение, имеющее пользовательские свойства. Например. когда Modernizr проверяет поддержку видео, он возвращает trueили, falseно возвращенное логическое значение (Bool является объектом первого класса в JS), имеет свойства, определяющие, какие форматы поддерживаются. Сначала это меня немного удивило, но потом мне стала нравиться эта идея, и я удивился, почему она используется довольно редко?

Это выглядит как элегантный способ справиться со всеми этими сценариями, где вы в основном хотите знать, является ли что-то истинным или ложным, но вас может заинтересовать некоторая дополнительная информация, которую вы можете определить, не определяя пользовательский объект возврата или используя функцию обратного вызова, подготовленную для принять больше параметров. Таким образом, вы сохраняете очень универсальную сигнатуру функции без ущерба для возможности возврата более сложных данных.

Против этого есть три аргумента:

  1. Это немного необычно / неожиданно, когда лучше, чтобы любой интерфейс был понятным и не хитрым.
  2. Это может быть аргумент соломенного чучела, но, учитывая, что он является чем-то вроде крайнего случая, я могу себе представить, что в некоторых оптимизаторах JS, uglifier, VM или после незначительной смены спецификаций языка очистки и т. Д. Это может иметь неприятные последствия.
  3. Есть лучший - краткий, ясный и общий - способ сделать то же самое.

Итак, мой вопрос: есть ли веские причины избегать использования логических значений с дополнительными свойствами? Это трюк или удовольствие?


Участок крутит предупреждение.

Выше оригинальный вопрос в полной славе. Как указали Мэтью Крамли и Сеневольдсен, это основано на ложной (ложной?) Предпосылке. В традициях JS, что делает Modernizr, это языковой трюк и грязный. Это сводится к тому, что JS имеет примитив bool, который, если он установлен в false, останется ложным даже после того, как TRYING добавит реквизит (который завершается с ошибкой) и логический объект, который может иметь пользовательские реквизиты, но быть объектом всегда правдив. Modernizr возвращает либо логическое ложное значение, либо истинный логический объект.

Мой оригинальный вопрос предполагал, что уловка работает по-другому, и поэтому наиболее популярные ответы касаются (совершенно правильного) аспекта стандартов кодирования. Однако я считаю, что ответы, опровергающие весь трюк, наиболее полезны (а также основные аргументы против использования метода), поэтому я принимаю один из них. Спасибо всем участникам!

Konrad
источник
35
Расширение булева типа является классическим WTF .
Хловдал
7
Обратите внимание, что в JS они могли бы просто вернуть, nullесли не поддерживается, и массив форматов, если это так. Список считается правдивым в JS и nullявляется ложным.
jpmc26
3
Прежде чем ответить на этот вопрос: в javascript существует большая разница между «логическим» и «логическим». Это не одно и то же, и любой ответ без заглавной логики недействителен.
Питер Б
2
Для тех из вас, кто ошеломлен, увидев нечто подобное в дикой природе, в такой популярной библиотеке, как Modernizr, приведен соответствующий код их GitHub: github.com/Modernizr/Modernizr/blob/…
Крис Нильсен,

Ответы:

38

В дополнение к общим принципам проектирования, таким как единая ответственность и наименьшее удивление, есть специфическая для JavaScript причина, по которой это не очень хорошая идея: существует огромная разница между JavaScript booleanи BooleanJavaScript, которая мешает его работе в общем случае.

booleanявляется примитивным типом, а не объектом, и не может иметь пользовательских свойств. Выражения любят true.toString()работать, потому что за кадром это превращается в (new Boolean(true)).toString().

Boolean(с большой буквы B) является объектом, но имеет очень мало хороших применений, и его использование как booleanопределенно не является одним из них. Причина в том, что каждый Booleanявляется «истиной», независимо от его значения , потому что все объекты преобразуются trueв логический контекст. Например, попробуйте это:

var answer = new Boolean(false);
if (answer) {
  console.log("That was unexpected.");
}

Таким образом, в общем случае в JavaScript нет способа добавить свойства к логическому значению, которое по-прежнему позволяет ему вести себя логически. Modernizr может сойти с рук, потому что единственное добавление свойств к «истинным» значениям, которые работают, как вы ожидаете (т.е. они работают в операторах if). Если видео вообще не поддерживается, оно Modernizr.videoбудет действительным boolean(со значением false) и к нему не будут добавлены свойства.

Мэтью Крамли
источник
18
Я нахожу подход Модернизра довольно странным, учитывая, что наличие только объекта со свойствами уже trueприводит к условному условию. Зачем явно использовать Boolean?
Артуро Торрес Санчес
Большое спасибо за хороший технический аргумент. Похоже, мой быстрый тест был ошибочным: jsbin.com/hurebi/3/edit?js,console . Даже после добавления настраиваемых реквизитов, falseони оба являются «ложными» и ложными ( ===и ==работают как), НО на самом деле вы не можете добавить реквизиты к примитивному bool. Различие между Bool и bool, по-видимому, ускользнуло от меня.
Конрад
@ ArturoTorresSánchez, потому что, как они это делают, возвращаемые значения номинально одного и того же типа.
Джаред Смит
1
@ ArturoTorresSánchez, поэтому я добавил классификатор «номинально»: P
Джаред Смит
1
@konrad Для справки, по умолчанию JavaScript сделает вид, что позволяет добавлять свойства к примитивам, не жалуясь, когда вы пытаетесь это сделать. Использование строгого режима исправит это, и TypeErrorвы получите a, если попытаетесь добавить свойство (см. Jsbin.com/yovasafibo/edit?js,console ).
Мэтью Крамли
59

Поздравляю, вы обнаружили объекты. Причиной этого не является принцип наименьшего удивления . Быть удивленным дизайном не очень хорошая вещь.

Нет ничего плохого в том, чтобы связать воедино эту информацию, но зачем вам ее прятать в Bool? Поместите это в то, что вы ожидаете получить всю эту информацию. Бул в комплекте.

candied_orange
источник
1
LOL да, я упомянул объект как очевидную альтернативу в моем вопросе. Принцип наименьшего удивления - хороший момент, даже несмотря на то, что слабая типизация объектов в JS менее удивительна, но все еще сбивает с толку, и вам все равно нужно проверять документы. Что касается использования, Modernizr является хорошим примером: у вас есть большой набор тестов с единообразным результатом истина / ложь, и только время от времени вам нужно передавать больше информации - может быть, даже необходимость в них появилась позже, поэтому, когда это произойдет, вы можете либо вносите критические изменения во всю библиотеку, создавая по большей части избыточные обертки вокруг booleans, или вы улучшаете bool. ИМО не такой тупой.
Конрад
1
Еще немного об использовании. Если вы продолжаете возвращать логическое значение, вы можете легко передать все функции в список операций, таких как any (), all (), filter (). Это обходит отсутствие строгой типизации или формальных интерфейсов в JS за счет того, что это немного нетрадиционно - что пока кажется основным аргументом против этого.
Конрад
1
«Нетрадиционный» не кажется мне сильным аргументом.
Роберт Харви
Я редактировал вопрос. Вот принцип наименьшего удивления. :-)
Конрад
16

Основной аргумент , который я держать против него есть, единый принцип ответственности , булево должен сказать , только если что - то trueили false, не то, почему или как и любая другая вещь. Я твердо убежден и практикую, что для передачи той или иной информации следует использовать другие объекты.

Дж. Пичардо
источник
Вы имеете в виду логическое или логическое (с заглавной B) в JavaScript есть разница.
Питер Б
Но если у вас есть объект, определяющий это и некоторые другие вещи, разве это не может нарушать те же принципы?
Кейси
1
@Casey Нет, поскольку назначение этого объекта будет отличаться от исходного логического значения. И у него будет только одна обязанность - сообщать в качестве примера состояние и причину транзакции.
Дж. Пичардо
1
@ Casey проблема не в том, что само по себе содержание этой информации является нарушением SRP. Это становится проблемой только в сочетании с ответственностью, которую уже имеет логическое значение - представлять истину или ложь. BooleanНесмотря на махинации JavaScript .
Джейкоб Райле
10

Поскольку причина, по которой оно называется логическим значением, заключается в том, что оно истинное или ложное, мне не нравится эта идея, поскольку вы в значительной степени подрываете ее цели из Википедии.

В информатике булев тип данных - это тип данных, имеющий два значения (обычно обозначаемые как истина и ложь), предназначенный для представления истинных значений логики и булевой алгебры .

(мой смелый)

Отображаемое имя
источник
1
правильно, но необходимость может продиктовать иначе
сварог
8
@svarog Единственная необходимость, которую я вижу, - это неквалифицированный дизайнер кода. Если вам нужно вернуть bool и что-то еще, сделайте это классом, или кортежем, или списком, или чем-то еще, что вписывается в конкретный код.
MatthewRock
если возвращаешься да
сварог
Я думаю, что вопрос о булевом объекте, а не о булевом примитиве. Объект не является значением.
Питер Б
1
Если оно может принимать значение, отличное от true или false, оно также не является логическим значением. Что, учитывая имя, глупо.
бесполезно
2

То, что вы можете не означать, в JS вы можете в значительной степени присоединять свойства к любому объекту (включая логические значения)

Как это плохо?

С одной стороны, вы можете прикрепить больше не относящихся к делу данных к логическому значению (согласно вашему примеру), чего другой разработчик не будет ожидать, потому что зачем оно там ?! bools только для правды и лжи.

Для этого нужно добавить некоторые полезные свойства, которые могут помочь вам справиться с логическим значением (это делает Java ).

Например, вы можете прикрепить функцию, которая преобразует логическое значение в строку, dirtyфлаг, который стал истинным, если значение было изменено, наблюдатели и обратные вызовы событий, которые могут срабатывать при изменении значения и т. Д.

но возвращаемое логическое значение (Bool является объектом первого класса в JS) имеет свойства, определяющие поддерживаемые форматы.

Это звучит как что-то, что не следует хранить в логическом значении, а использовать набор логических значений или набор свойств с логическими значениями внутри них. Я думаю, что лучшим подходом было бы вернуть объект со всеми форматами и подробностями поддержки.

{
    isSomethingSupported: true,
    isSomethingElseSupported: false,
    ....
}
Сварог
источник
1
Вы имеете в виду логическое или логическое (с большой буквы)?
Питер Б
1

Вы не можете создавать логические значения с пользовательскими свойствами в JavaScript. Следующий сбой (по крайней мере, в FF):

    var x = false;
    x.foo = "bar";
    console.log(x.foo);

Вы можете использовать или наследовать от Boolean, но, как говорит Мэтью Крамли, это дает разные результаты. Booleanимеет тип Object . Когда JS требуется логическое значение выражения, оно преобразуется с помощью функции спецификации ToBoolean, которая обязывает, что для Objects результат всегда true. Таким образом, значение new Boolean(false)оценивается как true! Вы можете проверить это в этом примере: https://jsfiddle.net/md7abx5z/3/ .

Единственная причина, по которой он работает для Modernizr, - случайность. Они только создают Booleanобъект, когда условие выполняется. Когда они оценивают ложь, они просто возвращают обычное false. Так что это работает, потому что они возвращают Boolean объекты только тогда, когда результат был истинным, и никогда, когда он ложный.

senevoldsen
источник
1

Я понимаю, что контекст изменился, но я хотел бы ответить на оригинальный вопрос, который я прочитал, используя JS в качестве примера, но не ограничиваясь только JS.

Добавление свойств к логическому значению не будет проблемой, если свойства имеют отношение к true / false, а не к переменной, содержащей значение. Например, было бы хорошо добавить метод toYesNoString, а добавление numberOfChildren к значению hasChildren - нет, равно как и questionsMissed studentPassed. Существует не так много, что вы могли бы добавить к логическому значению, кроме различных представлений строк, единственное свойство, которое я могу придумать, имеет смысл, это originalExpression. Но добавление к этому не обязательно плохая идея в теории.

jmoreno
источник
0

Если я смотрю на код, то речь идет не о булевых пользовательских свойствах, а о методе, имеющем методы возврата с разными сигнатурами.

Если это ложь, тогда вы получаете примитив ложь, и если это правда, он возвращает объект, который по определению интерпретируется как истина в JavaScript.

Так что, на мой взгляд, ваш вопрос должен состоять не в том, стоит ли давать логические пользовательские свойства, а в том, чтобы иметь методы с несколькими возвращаемыми сигнатурами.

Питер Б
источник
0

В данном примере, вы не могли бы просто вернуть массив всех поддерживаемых видеоформатов?

  • Пустой массив означает «нет видео форматов поддерживаются», которые в средствах заказа «нет видео не поддерживается».
  • В противном случае, если какой-либо формат видео поддерживается, то, очевидно, видео в целом поддерживается.

По крайней мере, я бы сказал, что использование массива несколько менее удивительно, чем использование «пользовательских логических значений».

В Javascript пустой массив даже считается ложным , в то время как непустой массив является правдивым , поэтому, если повезет, вы можете просто переключиться на массивы, и все будет работать так же, как и до редактирования Нет, глупо, я думаю, что могу вспомнить правдивость объектов в JavaScript: P

daniero
источник
Пустой массив не оценивается как ложный в операторе if. if ([]) console.log ('not false'); например, печатает «не ложь» в Chrome. typeof [] === 'object', поэтому ни один пустой массив не является ложным. См. Developer.mozilla.org/en-US/docs/Glossary/Truthy для справки.
17
@joshp ой, ты прав, мой плохой. Исправлено
daniero
Хотя я не уверен, что эта вещь что-то доказывает; ""имеет typeof, "string"но это на самом деле ложь
Даниеро
1
С другой стороны, « [].lengthложь, если длина» 0, не намного больше печатает, и, на мой взгляд, является лучшим показателем намерения, чем просто if([])было бы, даже если бы это сработало.
IllusiveBrian
@daniero Суть typeof [] === 'object' в том, что любой Object является правдивым, даже новый Boolean (false). Некоторые примитивные типы (например, строка "" и число 0) ложны, но они не являются объектами, если говорить о typeof. Это просто еще один способ запомнить. Дело не в доказательстве чего-либо. Доказательство в спецификации или тестах, в зависимости от того, что вы предпочитаете.
Joshp