Чи погана практика JS Boolean, що має власні властивості?


41

У JS ви можете повернути булеве значення, яке має власні властивості. Напр. коли Modernizr випробовує підтримку відео, він повертається trueабо, falseале повернений Boolean (Bool є об'єктом першого класу в JS) має властивості, що визначають, які формати підтримуються. Спочатку це мене трохи здивувало, але потім мені почала подобатися ідея і почала цікавитись, чому вона, здається, використовується досить щадно?

Це виглядає як елегантний спосіб поводження з усіма тими сценаріями, коли ви в основному хочете дізнатися, чи щось є правдою чи неправдою, але вас може зацікавити додаткова інформація, яку ви можете визначити, не визначаючи спеціальний об'єкт повернення або використовуючи функцію зворотного виклику, підготовлену для прийняти більше параметрів. Таким чином ви зберігаєте дуже універсальний підпис функції без шкоди для можливості повернення більш складних даних.

Проти цього я можу собі уявити 3 аргументи:

  1. Це трохи незвично / несподівано, коли будь-який інтерфейс, мабуть, краще бути зрозумілим і не хитрим.
  2. Це може бути солом'яним аргументом, але, маючи це трохи кращий випадок, я можу уявити, що це тихо повертається у деяких оптимізаторах JS, угльовителе, VM або після незначної зміни специфікації мови тощо.
  3. Існує кращий - стислий, зрозумілий і загальний спосіб зробити саме те саме.

Тож моє запитання: чи є якісь вагомі причини уникати використання булів з додатковими властивостями? Це трюк чи частування?


Сюжет закручує попередження.

Вище - оригінальне запитання у повній красі. Як Меттью Крамлі і сеневольдсен вказали, це засноване на помилковій (хибній?) Передумові. Тонка традиція JS - це те, що Модернізр - це мовна хитрість і брудна. Він зводиться до JS, який має примітивний bool, який, якщо встановлено значення false, залишатиметься помилковим навіть після ПІДГОТОВКИ, щоб додати реквізит (який виходить з ладу) і булевий об'єкт, який може мати власні реквізити, але бути об'єктом завжди є правдою. Modernizr повертає або булева помилка, або truthy булевий об'єкт.

В моєму первісному запитанні припущення припустимо, що трюк працює по-різному, тому більшість популярних відповідей стосуються (цілком справедливого) аспекту стандартів кодування. Однак я вважаю, що відповіді, які розкривають цілий трюк, найбільш корисні (а також кінцеві аргументи проти використання методу), тому я приймаю одну з них. Дякую всім учасникам!


35
Розширення булевого типу - це класичний wtf .
hlovdal

7
Зауважте, що в JS вони могли б просто повернутися, nullякщо не підтримуються, і масив форматів, якщо так. Список вважається правдою в JS і nullє хибним.
jpmc26


3
Перш ніж відповісти на це запитання: у javascript є велика різниця між "boolean" та "boolean". Вони не одне і те ж, і будь-яка відповідь, яка не використовує великі булеві, недійсна.
Пітер Б

2
Для тих із вас, хто приголомшений бачити щось подібне в дикій природі, в бібліотеці, популярній як Modernizr, ось відповідний код з їх GitHub: github.com/Modernizr/Modernizr/blob/…
Кріс Нільсен

Відповіді:


38

Окрім загальних принципів дизайну, як-от одна відповідальність і найменший сюрприз, є специфічна для JavaScript причина, що це не дуже гарна ідея: існує величезна різниця між a booleanі BooleanJavaScript, яка заважає йому працювати в загальному випадку.

booleanце примітивний тип, а не об'єкт і не може мати власні властивості. Вирази люблять true.toString()роботу, тому що за лаштунками вона перетворюється на (new Boolean(true)).toString().

Boolean(з великою літерою В) є об'єктом, але має дуже мало корисних застосувань, і використання його як booleanоднозначно не є одним із них. Причиною тому є те, що кожен Booleanє "справжнім", незалежно від його значення , тому що всі об'єкти перетворюються на trueбулевий контекст. Наприклад, спробуйте це:

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

Таким чином, загалом, немає можливості додати властивості до булевого JavaScript у JavaScript, що все ще дозволяє йому вести себе логічно. Modernizr може відмовитися від цього, оскільки єдині додають властивості до "істинних" значень, які працюють так, як ви очікували (тобто вони працюють, якщо заяви). Якщо відео взагалі не підтримується, воно Modernizr.videoбуде фактичним boolean(із значенням false) і не може до нього додавати властивості.


18
Я вважаю, що підхід Модернізра є досить дивним, враховуючи, що наявність просто об'єкта з властивостями вже оцінюється trueяк умовне. Чому явно використовувати a Boolean?
Артуро Торрес Санчес

Велике спасибі за обґрунтований технічний аргумент. Схоже, мій швидкий тест був помилковим: jsbin.com/hurebi/3/edit?js,console . Навіть після додавання користувальницьких реквізитів falseце суворо "помилково" та хибність ( ===і ==працює як), Але дійсно ви не можете додати реквізит до примітивного bool. Відмінність між Булом і Булом, очевидно, втікало від мене.
конрад

@ ArturoTorresSánchez, оскільки те, як вони це роблять, повертає значення номінально одного типу.
Джаред Сміт

1
@ ArturoTorresSánchez тому я додав класифікатор "номінально": P
Джаред Сміт

1
Для довідки, за замовчуванням JavaScript буде робити вигляд, що дозволяє додавати властивості примітивам, не скаржившись, коли ви намагаєтеся це зробити. Використання суворого режиму виправить це та викине a, TypeErrorякщо ви спробуєте додати властивість (див. Jsbin.com/yovasafibo/edit?js,console ).
Меттью Крамлі

59

Вітаємо, ви виявили об’єкти. Причиною цього не робити називається принцип найменшого здивування . Дивуватися дизайном - це не дуже добре.

Немає нічого поганого в згуртуванні цієї інформації разом, але навіщо ви хочете сховати її у Bool? Помістіть його в те, що ви могли б очікувати, щоб мати всю цю інформацію. Бул включений.


1
LOL так, я згадував об'єкт як очевидну альтернативу у своєму питанні. Принцип найменшого здивування - це хороший момент, навіть якщо слабкі типізуючі об'єкти JS менш дивні, але все-таки заплутані, і вам потрібно перевірити документи. Що стосується використання Modernizr - хороший приклад: у вас є великий набір тестів з рівномірним істинним / хибним результатом, і лише раз у раз вам потрібно передати більше інформації - можливо, навіть необхідність у тих, хто з’явився пізніше, тож коли це можливо, ви можете або внесіть переломні зміни в цілу бібліотеку, створюючи в основному зайві обгортки навколо булевих або ви покращуєте bool. ІМО не такий німий.
konrad

1
Ще трохи про використання. Якщо ви дотримуєтесь повернення булевого значення, ви можете передати всі функції для переліку операцій, таких як будь-який (), all (), filter (). Він уникає відсутності в JS сильних машинописних чи формальних інтерфейсів ціною трохи нетрадиційного - що здається головним аргументом проти цього.
konrad

1
"Нетрадиційне" не здається мені сильним аргументом.
Роберт Харві

Я редагував питання. Ось до принципу найменшого здивування. :-)
konrad

16

Основний аргумент, який я проти цього, - це принцип єдиної відповідальності , булевий, повинен говорити лише в тому випадку, якщо щось є, trueчи falseне, чому, як чи інша річ. Я твердо вірю і практикую, що для передачі тієї чи будь-якої іншої інформації слід використовувати інші об'єкти.


ти маєш на увазі булеву або булеву (з великою літерою B) у javascript, є різниця.
Пітер Б

Але якщо у вас є об'єкт, що визначає це та деякі інші речі, чи не може це порушити ті самі принципи?
Кейсі

1
@Casey Ні, оскільки призначення цього об'єкта не було б оригінальним булевим. І було б лише одна відповідальність - повідомляти про стан та причину угоди, як приклад.
Дж. Пічардо

1
@Casey проблема не в тому, що саме ця інформація є порушенням SRP. Проблема стає лише тоді, коли вона поєднується з відповідальністю, яку вже має булів - представляти правду чи хибність. Не Booleanдивлячись на те, що шенагігани JavaScript
Яків Райхле

10

Оскільки вся причина, яку називають булевою цінністю, полягає в тому, що це правда чи хибність, я не люблю цю ідею, оскільки ви майже підриваєте її цілі з вікіпедії

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

(моя сміливість)


1
правильно, але необхідність може диктувати інакше
сварог

8
@svarog єдина необхідність, яку я можу бачити, - це некваліфікований конструктор коду. Якщо вам потрібно повернути bool та щось інше, зробіть його класом, кортежем чи списком чи будь-чим іншим, що відповідає певному коду.
MatthewRock

якщо ви повертаєтеся так
svarog

Я думаю, що питання про булевий об'єкт, а не булева примітивність. Об'єкт - не значення.
Пітер Б

1
Якщо він може приймати значення, відмінне від істинного чи хибного, воно також не є булевим. Що, даючи ім’я, нерозумно.
Марно

2

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

Як це погано?

З одного боку, ви можете приєднати більш нерелевантні дані до булевих даних (на ваш приклад), чого інший розробник не очікує, бо навіщо це бути там ?! булі - це просто істинно і неправдиво.

Протилежним моментом для цього є приєднання деяких відповідних корисних властивостей, які можуть допомогти вам впоратися з булевим значенням ( Java це робить ).

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

але повернений булевий (Bool є об'єктом першого класу в JS) має властивості, що вказують, які формати підтримуються

Це звучить як щось, що не слід зберігати в булевому стилі, а скоріше використовувати набір булевих наборів або набір властивостей із булевими нумерами всередині них. Я думаю, що кращим підходом було б повернення об'єкта з усіма форматами та деталями підтримки.

{
    isSomethingSupported: true,
    isSomethingElseSupported: false,
    ....
}

1
ви маєте на увазі булева чи булева (з великої літери Б)?
Пітер Б

1

Ви не можете створювати булеви з користувацькими властивостями в JavaScript. Наступні помилки (принаймні у FF):

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

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

Єдина причина, по якій він працює для Modernizr, - випадкова ситуація. Вони створюють Booleanоб'єкт лише тоді, коли умова справжня. Коли вони оцінюють помилкові, вони просто повертаються звичайними false. Так це працює, тому що вони повертають Boolean об'єкти лише тоді, коли результат був істинним, і ніколи, коли він хибний.


1

Я розумію, що контекст змінився, але я хотів би відповісти на початкове запитання, яке я читав, використовуючи JS як приклад, але не обмежуючись лише JS.

Додавання властивостей до булевого значення не буде проблемою, якщо б властивості мали щось спільне з істинним / хибним, а не зі змінною утримуванням значення. Наприклад, додавання методу toYesNoString було б добре, додавання числаOfChildren до значення hasChildren не є, і жодних запитань не буде Міс студентPassed. Існує не так багато, що ви могли б додати до булевого, крім різних рядкових репрезентацій, єдине властивість, про яке я можу подумати, має сенс originalExpression. Але додавання до цього не обов’язково є поганою ідеєю в теорії.


0

Якщо я дивлюсь на код, це стосується насправді не надання булевих власних властивостей, а методу, що має методи повернення з різними підписами.

Якщо це false, то ви отримуєте примітивну false, а якщо це правда, він повертає об'єкт, який за визначенням інтерпретується як true у JavaScript.

Тож, на мою думку, у вашому питанні не повинно бути, чи є гарною ідеєю надати булеві користувальницькі властивості, а чи корисно мати методи з декількома зворотними підписами.


0

У наведеному прикладі ви не могли просто повернути масив усіх підтримуваних форматів відео?

  • Порожній масив означає "не підтримуються відеоформати ", що означає "жодне відео не підтримується".
  • В іншому випадку, якщо підтримується будь-який формат відео , очевидно, що відео взагалі підтримується.

Принаймні, я б сказав, що використання масиву дещо менш дивно, ніж "користувацькі булі".

У Javascript порожній масив навіть вважається хибним , тоді як непустий масив є правдоподібним , тож з деякою удачею ви могли просто переключитися на масиви, і все спрацювало б так, як до редагування Nope, нерозумно мені за те, що я міг згадати правдивість об'єктів у JavaScript: P


Порожній масив не визначається як хибний у операторі if. if ([]) console.log ('не помилково'); друкує, наприклад, "не помилково" в Chrome. typeof [] === 'об’єкт', тому жоден порожній масив не є хибним. Для ознайомлення дивіться developer.mozilla.org/en-US/docs/Glossary/Truthy .
Джошп

@joshp На жаль, ти маєш рацію, моя погана. Виправлено
daniero

Хоча, я не впевнений, що typeof річ щось доводить; ""має typeof, "string"але насправді
фальшивий

1
З іншого боку, [].lengthфальси, якщо довжина є 0, не набагато більше набирати текст, і на мою думку, це кращий показник наміру, ніж просто if([])було б, якби це працювало.
ІлюзивнийБріан

@daniero Суть про typeof [] === 'object' полягає в тому, що будь-який Об'єкт є трибунним, навіть новим булевим (false). Деякі примітивні типи (наприклад, рядок "" і число 0) є хибними, але вони не є Об'єктами, що стосується typeof. Це просто ще один спосіб запам'ятати. Справа не в тому, щоб щось доводити. Доказ міститься в специфікаціях або тестах, що б вам не було зручніше.
joshp
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.