Начитывая очередную статью про малоизвестные фичи языка JavaScript и втихую пописывая какие-то невменяемые решения в консоли браузера, я часто проговариваю в голове мол ну на проде то конечно все не так!? Ведь язык давно обзавелся огромнейшим комьюнити и имеет удивительно широкий охват промышленной разработки. Раз так, то почему же мы часто забываем про его возможность быть понятным для каждого и буквально пропагандируем все эти специфичные и "запоминаемые" конструкции? Just make it Obvious!
Рассуждения на тему
Эту графоманию можно пропустить.
Если говорить о промышленной разработке, то в подавляющем большинстве случаев требование к коду быть поддерживаемым даже важнее, чем решать поставленную бизнесом задачу. Для многих это очевидно, для некоторых — отчасти (встречаются конечно и редкие Д'Артаньяны). Чем понятнее наш код, тем меньше рисков для него — попасть на пыльную полку, а для нас и наших преемников — заработать проблем с нервной системой.
Не секрет, что JavaScript удивителен своей гибкостью, что является как его величайшим достоинством, так и досадным проклятием. Путь JavaScript-разработчика долог и крайне интересен: мы поглощаем книжку за книжкой, статью за статьей и набираемся уникального опыта, но местами — действительно language-специфичного. Широчайшее распространение языка и в то же время богатое число накопившихся и подкармливаемых неочевидностей способствуют образованию двух фронтов: тех, кто едва ли не боготворит этот язык, и тех, кто смотрит на него как на неуклюжую и качающую права утку.
И все бы ничего, но часто представители обоих фронтов работают на одном проекте. И обычной, всеми принятой практикой является непонимание (нежелание понимать и даже игнорирование) кода друг друга. И в самим деле, "я же на Java-разработчика устраивался, а не это ваше!". Масла в огонь подливают и сами JavaScript-последователи мол "никто на самом деле не знает JavaScript!" да "я могу это в одну строчку написать на js!". Каюсь, что и сам злоупотребляю на досуге ненормальным программированием...
Эту проблему начинаешь чувствовать, когда занимаешь место маргинала и приобретаешь некоторый опыт работы с людьми и их кодом по обе стороны от баррикад. Планинг и прочие митинги проходят продуктивнее, когда все разработчики понимают друг друга не только на уровне бизнес-сторей, но и хоть немного на уровне их имплементации. Пресловутый бас-фактор меньше сказывается на проекте, когда в случае болезни единственного фронт-ендера остальные члены команды не брезгуют поправить какую-то строчку .js файла. Процесс обмена знаниями в команде и за ее пределы становится прозрачнее для всех, когда каждый имеет более детальную картину. Ну и все в том же духе.
Я никого не призываю "фулстекнуться" или "T-шейпнуться" (как сейчас правильно говорить?), но почему бы нам немного не приподнять этот занавес хотя бы со стороны JavaScript-сообщества? Для этого достаточно лишь привнести немного явности в наш код, используя гибкость языка не чтобы выпендриться, а чтобы нас понимали.
Взросление и принятие ответственности
Со своей стороны JavaScript уже давно осознал свою роль не как язык для интерактивности интернет-страничек и "склеивания" их ресурсов, но как мощнейший и достаточный инструмент создания полноценных кросс-платформенных и часто очень даже масштабируемых приложений.
Изначально разработанный для веб-дизайнеров этот "самый неправильно понятый язык программирования" долгое время топтался на месте, несмотря на стремительно растущую популярность и значимость. За 13-14 лет, предшествующие редакции ECMAScript 5.1, трудно вспомнить какие-то важные изменения в стандарте или понять вектор его развития. В то время огромный вклад в формирование экосистемы языка вносило его комьюнити: Prototype, jQuery, MooTools и проч. Получив эту обратную связь от разработчиков, JavaScript проделал значительную работу над ошибками: громкий 6-летний релиз ES6 в 2015 году и теперь уже ежегодные релизы ECMAScript, благодаря переработанному комитетом TC39 процессу внесения новых возможностей в спецификацию.
Что ж, когда наши приложения стали достаточно большими, прототипная модель ООП для описания пользовательских типов перестала себя оправдывать из-за непривычного подхода. Ну серьезно, что это?
function Animal() {
/* Call me via new and I will be the constructor ;) */
}
function Rabbit() {}
Rabbit.prototype = Object.create(Animal.prototype);
Rabbit.prototype.constructor = Rabbit;
В языке не появились классы, но появился их синтаксис. И код стал доступен для приверженцев традиционной класс-ориентированной парадигмы:
class Animal {
constructor() {
/* Obviously, the constructor is here! */
}
}
class Rabbit extends Animal {}
Сейчас на стадии кандидата в релиз находятся приватные поля класса. С трудом верится, что рано или поздно мы перестанем смешить друг друга соглашением об именовании приватных свойств через нижнее подчеркивание.
В то же время, в языке, где функция является объектом первого порядка и имеет место постоянная событийность, совершенно обычное дело:
let that = this;
setTimeout(function() {
that.n += 1;
}, 1000);
И тут начинаются объяснения о контекстах this и замыкании в JavaScript, что отпугивает каждого второго внешнего разработчика. Но во многих случаях, язык позволяет избежать лишних удивлений, явно используя Function.prototype.bind или вовсе так:
setTimeout(() => this.n += 1, 1000);
У нас тоже появились стрелочные функции, и это действительно — функции, а не функциональные интерфейсы (да, Java?). Вместе с расширенным набором методов работы с массивом они также помогают писать привычный декларативный пайплайн вычислений:
[-1, 2, -3, 4]
.filter(x => x > 0)
.map(x => Math.pow(2, x))
.reduce((s, x) => s + x, 0);
Язык по праву считает себя мультипарадигменным. Но вот простой пример про сигнатуру некоторой функции:
function ping(host, count) {
count = count || 5;
/* send ping to host count times */
}
Сначала проходящий мимо задастся вопросом мол вероятно функция может принимать только первый аргумент, а потом мол какого черта в этом случае count становится булевом!? И действительно, функция имеет два варианта использования: с указанием count и без. Но это совершенно неочевидно: приходится смотреть в реализацию и понимать. Разобраться может помочь использование JSDoc, но это не общепринятая практика. И здесь JavaScript пошел навстречу, добавив поддержку не перегрузки, но хотя бы дефолтных параметров:
function ping(host, count = 5) { /* ... */ }
Резюмируя, JavaScript обзавелся огромным числом привычных вещей: генераторы, итераторы, коллекции Set и словари Map, типизированные массивы, да даже регулярные выражения начали радовать поддержкой lookbehind! Язык делает все, чтобы быть пригодным для многих вещей и стать дружелюбным для всех.
Благоприятный путь к очевидному
Сам язык — безусловно молодец, и с этим трудно спорить! Но что не так с нами? Почему мы постоянно напоминаем всему миру, что JavaScript все таки какой-то не такой? Давайте посмотрим на примеры некоторых широко используемых приемов и зададимся вопросом их целесообразности.
Приведение типов
Да, JavaScript обладает динамической и слабой системой типов и позволяет проводить операции над чем угодно, неявно выполняя за нас преобразования. Но часто явное приведение типов нам все таки необходимо и можно наблюдать следующее:
let bool = !!(expr);
let numb = +(expr);
let str = ''+(expr);
Эти трюки известны каждому JavaScript-разработчику и мотивируются они тем, что мол так можно "быстро" превратить что-то во что-то: под быстротой здесь понимается короткая запись. Может еще и false записывать сразу как !1? Если разработчик так сильно переживает за печатаемые символы, то в его любимой IDE можно без труда настроить необходимый live template или автокомплит. А если — за размер публикуемого кода, то мы всегда прогоняем его через обфускатор, который знает получше нашего как все это обесчеловечить. Почему не так:
let bool = Boolean(expr);
let numb = Number(expr);
let str = String(expr);
Результат — такой же, только понятен всем.
Для строковых преобразований у нас есть toString, но для численных есть интересный valueOf, который может быть тоже переопределен. Классический пример, который вводит в ступор "непосвященных":
let timestamp = +new Date;
Но ведь есть у Date известный метод getTime, давайте использовать его:
let timestamp = (new Date()).getTime();
или готовую функцию:
let timestamp = Date.now();
Абсолютно незачем эксплуатировать неявное приведение типов.
Логические операторы
Отдельного внимания достойны логические операторы И (&&) и ИЛИ (||), которые в JavaScript не совсем логические: принимают и возвращают значения любого типа. Вдаваться в детали работы вычислителя логического выражения не будем, рассмотрим примеры. Ранее представленный вариант с функцией:
function ping(host, count) {
count = count || 5;
/* ... */
}
Вполне может выглядеть следующим образом:
function ping(host, count) {
// OR arguments.length?
if (typeof count == 'undefined') {
count = 5;
}
/* ... */
}
Такая проверка и привычней, и в некоторых случаях может помочь избежать ошибки.
Это скорее кажется дикостью для разработчика, который изначально выбрал путь JavaScript. Но для большинства других — вот этот код действительно дикий:
var root = (typeof self == 'object' && self.self === self && self) ||
(typeof global == 'object' && global.global === global && global);
Да, это компактно, и да, это могут позволить себе популярные библиотеки. Но, пожалуйста, давайте не злоупотреблять этим, так как наш код будут читать не контрибьюторы в JavaScript, а разработчики, решающие бизнес-задачи за выделенные сроки.
Может встретиться и вовсе такой паттерн:
let count = typeof opts == 'object' && opts.count || 5;
Это определенно короче обычного тернарного оператора, но при чтении такого кода первым делом вспоминаешь приоритеты используемых операций.
Если же мы пишем функцию-предикат, которую передаем в тот же Array.prototype.filter, то обернуть возвращаемое значение в Boolean — это хороший тон. Сразу становится очевидно назначение этой функции и не возникает диссонанса у разработчиков, языки которых имеют "правильные" логические операторы.
Побитовые операции
Распространенный пример проверки наличия элемента в массиве или подстроки в строке с помощью побитового НЕ (NOT), который предлагается даже некоторыми учебниками:
if (~[1, 2, 3].indexOf(1)) {
console.log('yes');
}
Какую проблему это решает? нам не приходится осуществлять проверку !== -1, так как indexOf получит индекс элемента или -1, а тильда прибавит 1 и поменяет знак. Тем самым выражение будет оборачиваться "ложью" в случае индекса -1.
Но избежать дублирования кода можно и по-другому: вынести проверку в отдельную функцию какого-нибудь utils-объекта, как это делают все, чем использовать побитовые операции не по назначению. В lodash для этого есть функция includes, и работает она не через жопу тильду. Можно возрадоваться, так как в ECMAScript 2016 закрепился метод Array.prototype.includes (у строк тоже есть).
Но не тут-то было! Еще тильду (наравне с XOR) используют для округления числа, отбрасывая десятичную часть:
console.log(~~3.14); // 3
console.log(2.72^0); // 2
Но ведь есть parseInt или Math.floor для этих целей. Побитовые операции здесь удобны для быстрого набора кода в консоли, так как они к тому же имеют низкий приоритет перед остальной арифметикой. Но на код-ревью такое лучше не пропускать.
Синтаксис и конструкции языка
Некоторые странные практики трудно отнести к какому-то конкретному разделу. Например, говорят, что скобки при вызове конструктора необязательны и следующие два выражения идентичны:
let rabbit = new Rabbit();
let rabbit = new Rabbit;
И это действительно так! но зачем создавать вопрос на пустом месте? Не каждый язык может похвастать такой "особенностью". А если все таки хочется, то пусть это будет соглашением по всему проекту. Иначе возникает ложное чувство, что есть какая-то разница.
Похожая ситуация с объявлением набора переменных. Синтаксис директив var и let позволяет объявить (и определить) сразу несколько переменных, перечисленных через запятую:
let count = 5, host, retry = true;
Кто-то использует переводы строк для читаемости, но в любом случае такой синтаксис — не частое явление в популярных языках. Никто не даст по рукам и не спросит, если написать так:
let count = 5;
let retry = true;
let host;
Опять таки, если есть соглашение о хорошем стиле на уровне проекта/компании, то вопросов нет. Просто не надо чересчур комбинировать варианты синтаксиса по настроению.
Есть в языке и вовсе специфичные конструкции, как например IIFE — позволяет вызвать функцию сразу по месту ее определения. Весь трюк в том, чтобы парсер распознал функциональное выражение, а не деклорацию функции. И это можно сделать уймой разных способов: классически обернув скобками, через void или любой другой унарный оператор. И в этом нет ничего замечательного! Необходимо выбрать единственный вариант и не отходить от него без необходимости:
(function() {
/* ... */
}());
Не надо использовать операторы, чтобы хакнуть парсер. Когда на проект приходит новичок, хочется погрузить его в бизнес-логику приложения, а не кормить объяснениями откуда были подсмотрены все эти восклицательные знаки и войды. Есть еще вторая классическая запись через скобки и интересный комментарий от Крокфорда по этому поводу.
Появление синтаксиса классов в ES6 не было сопровождено привычными модификаторами доступа. А иногда разработчику хочется и на классах пописать, и приватность соблюсти. Что приводит к такому коду Франкенштейна:
class Person {
constructor(name) {
let _name = name;
this.getName = function() { return _name; }
}
toString() {
return `Hello, ${this.getName()}`;
}
}
То есть в конструкторе для экземпляра создаются аксессоры, а приватность достигается их доступом к локальным переменным-свойствам через замыкание. Этот пример выглядит вполне даже лакончино, но это совершенно немасштабируемый подход, если вокруг него не построить документированное решение-фреймворк. Господа, давайте использовать либо имеющиеся классы (и ждать стандартизации приватных полей), либо популярный паттерн-модуль. Создавать какое-то промежуточное микс-решение здесь — такое себе, так как классы перестают быть классами, а код — вразумительным.
Подытоживая, здравой мыслью будет поделиться принятым в проекте стайл-гайдом, конфигом для линтера или просто фрагментами кода с коллегами, которые вносят в проект его не-JavaScript составляющую. Язык предлагает несколько вариантов буквально для каждой типовой задачи, поэтому улучшить понимание друг друга и попасть под общий знаменатель не составит труда (ну или почти).
Злоключение
Тема эта конечно холиварная и примеров можно привести гораздо больше, но основной посыл статьи о том, что не следует злоупотреблять неочевидностями в JavaScript там, где этого можно избежать. Природа языка — уникальна: позволяет писать как элегантные и выразительные (в меру "упоротые") решения, так и понятные и доступные для всех. Я в корне не согласен с расхожим мнением, что JavaScript "сам себя наказал" или "похоронен под грудой добрых намерений и ошибок". Потому что сейчас большую часть странностей демонстрирует не язык, а образовавшаяся вокруг него культура разработчиков и (не)равнодушных.
Комментарии (46)
Tankerxyz
12.12.2018 12:42+1Полностью согласен с тем, что в основном холиварят и поливают говном те, кто так и не осилил его в прошлом.
Сейчас это очень даже выразительный и понятный язык.
NLO
00.00.0000 00:00НЛО прилетело и опубликовало эту надпись здесь
cerberus_ab Автор
12.12.2018 12:53А в чем вопрос? это не идентичные функции, просто примеры. Конечно надо дополнить нужной проверкой на число, на ноль если нужно. Поинт в том, что явная проверка аргумента понятнее себя ведет нежели специфичный «логический» оператор.
waltter
12.12.2018 23:14Поймал себя на мысли, что не могу придумать ни одного кейса когда можно (и тем более нужно) использовать нестрогое сравнение.
cerberus_ab Автор
12.12.2018 23:23Довольно старая статья об этом: When is it OK to use == in JavaScript?.
По большому счету есть кейсы когда можно, но нет кейсов — когда нужно )
В библиотеках часто используют "когда можно", чтобы код сократить (личное наблюдение).
NLO
00.00.0000 00:00НЛО прилетело и опубликовало эту надпись здесь
cerberus_ab Автор
12.12.2018 13:09Там пример про значение аргумента по умолчанию в случае его отсутствия, не про валидацию.
paratagas
12.12.2018 13:07+2Чтобы избегать такого:
function ping(host, count) { count = count || 5; /* ... */ }
как раз и были созданы параметры по умолчанию, упоминаемые выше. Вот как бы оно выглядело после рефакторинга:
function ping(host, count = 5) { // count = count || 5; /* ... */ }
Поэтому приведенный автором способ улучшения
function ping(host, count) { // OR arguments.length? if (typeof count == 'undefined') { count = 5; } /* ... */ }
ничем не лучше антипаттернаcount = count || 5;
cerberus_ab Автор
12.12.2018 13:11Согласен ) Я поленился придумывать более сложный пример инициализации без привязки к аргументам функции: да и посыл, казалось, понятен был и так.
NLO
00.00.0000 00:00НЛО прилетело и опубликовало эту надпись здесь
cerberus_ab Автор
12.12.2018 13:49Ответ про разный результат был дан выше. Пример не об этом и мне стоило более четко выражать свою мысль и делать акцент. Постараюсь исправить ситуацию:
1. Верный вариант задать аргумент по умолчанию — это default parameters.
2. Если все таки нужно определить переменную по месту, то лучше не использовать «логический» оператор с проверкой наличия чего-либо.
3. Если в проверку еще добавляется валидация в несколько условий, то нормальный if statement еще более необходим. Хотелось бы верить, что это понятно (как было прокомментировано выше), но код с инициализацией переменной через кучу И и ИЛИ с проверками встречается.NLO
00.00.0000 00:00НЛО прилетело и опубликовало эту надпись здесь
cerberus_ab Автор
12.12.2018 14:08Я пару раз видел изумление, когда показывал код тому же Java-разработчику. И да, считаю это хорошей практикой читать и понимать код не только JavaScript как для себя, так и для коллег. Мне эти операторы угодили, но в js они только называются «логическими» и часто понятны только js-разработчикам.
ua9msn
12.12.2018 13:56+3let timestamp = +new Date;
Но ведь есть у Date известный метод getTime, давайте использовать его:
let timestamp = (new Date()).getTime();
Вообще то, правильно будет
let timestamp = Date.now();
cerberus_ab Автор
12.12.2018 14:01Уух… мне стыдно, но я об этой функции позабыл. Спасибо большое! добавил )
CoolCmd
12.12.2018 14:45let str = ''+(expr);
let str = String(expr);
Результат — такой же, только понятен всем.
строго говоря, не такой же. Если expr — это объект, у которого есть метод valueOf, то первый способ переведет в строку то, что вернул метод valueOf, а второй — то, что вернул метод toString.cerberus_ab Автор
12.12.2018 14:51-1let d = new Date; ''+d; // Wed Dec 12 2018 14:47:27 GMT+0300 +d; // 1544615247741
Здесь будет строковое преобразование, так как один из операндов — явно строка. Но замечание очень дельное! с этим надо быть осторожным )
CoolCmd
12.12.2018 15:14Date — это исключение из правил. прежде чем писать статьи о языке, неплохо бы в учебник заглянуть.
JTG
12.12.2018 14:50+1Но во многих случаях, язык позволяет избежать лишних удивлений, явно используя Function.prototype.bind или вовсе так:
Оставляя в стороне «явные» отличия в поведении обычных и стрелочных функций (от которых у Гвидо волосы на спине бы встали дыбом), this настолько не стыкуется с синтаксисом классов, что надо или выпилить bind (=сделать код обратно несовместимым), или добавить self.
setTimeout(() => this.n += 1, 1000);
Утрированный пример: допустим, есть графики D3 или highcharts, которые используют контекст в своих колбеках на всю катушку, и «старый» класс с классическим that = this, который строит конфигурацию для графика:
function Config () { let self = this; self.field = 'foo'; this.getConfig = function() { return { tooltip: { formatter: function () { return 'Value of ' + self.field + ' for X=' + this.x + ' is ' + this.y; } } } } }
Если всё это дело попытаться переписать с использованием «новых» классов, сразу возникают нестыковки:
class Config { constructor() { this.field = 'foo'; } getConfig() { return { tooltip: { // Так потеряется значение this.field formatter: function () { return 'Value of ' + this.field + ' for X=' + this.x + ' is ' + this.y; }, // Так станут недоступными this.x и this.y formatter: () => {return 'Value of ' + this.field + ' for X=' + this.x + ' is ' + this.y}, } } } }
Но как же хорошо, что в JS существует столько явных возможностей, среди которых есть даже IIFE. Так язык позволяет избежать лишних удивлений:
formatter: (self => function() {return 'Value of ' + self.field + ' for X=' + this.x + ' is ' + this.y})(this)
(Да, это потому, что либы писались ещё за царя Гороха. Но самое страшное, что так продолжают писать! Потому что пока в языке есть этот чёртов this, каждый будет вертеть им как хочет)cerberus_ab Автор
12.12.2018 15:13Оох… этот контекст в highcharts! Библиотеки — это действительно отдельная тема )
Я разделяю недовольство по поводу нечистой стрелочной функции с this в примере, но готов иногда пойти на это ради читаемости.
Призыва переписывать все на классы не было. Но если бизнес-логика это позволяет, то пусть это будут классы… а не жгучая смесь подходов и танцы вокруг контекста.
Я бы не относил IIFE к явным возможностям ) но это повсеместная штука, ее трудно игнорировать. Мысль была хотя бы не спорить о синтаксисе.
JustDont
12.12.2018 16:27Это как раз та область, где явно видно силу Typescript. У TS хватает своих проблем (основная из которых — в том, что окружающий мир в основном не TS), но вот в области превращения неочевидных конструкций JS в очевидные — он прекрасен.
Хотя автор конечно безусловно прав в утверждении, что и на JS можно понятно писать.
ilyapirogov
12.12.2018 17:03Сделать поля приватными на уровне модуля на данный момент можно так:
const nameField = Symbol('name'); class Foo { constructor(name) { this[nameField] = name; } toString() { return `Hello, ${this[nameField]}`; } }
cerberus_ab Автор
12.12.2018 17:05Да, вариантов несколько. Попробуй только потом в этом разобраться…
surefire
13.12.2018 10:36-1const foo = new Foo('world'); Object.getOwnPropertySymbols(foo).map(sym => foo[sym]); // [ 'world' ]
ilyapirogov
13.12.2018 15:56-1В Java тоже можно доступ к приватным полям через рефлексию получить, но это же не делает их публичными :)
inew-iborn
12.12.2018 21:48нужно наверное завести какой-то бестпрактикс по javascript, я вот сам с ним работаю очень мало и действительно приходится пробираться через дебри законов, нюансов этого языка. Но как правило многие вещи это непонятные оптимизации и правила написания кода, и увы с читабельностью они часто разнятся, что нарушает философию уменьшения сложности.
на мой субъективный взгляд конечно.megahertz
13.12.2018 07:28Есть Airbnb JavaScript Style Guide, не совсем best practices, но частично и их захватывает. Пожалуй, наиболее популярное соглашение в мире JS.
megahertz
13.12.2018 07:45Использование оператора || для задания значений по умолчанию настолько распространено, что уже мало кем считается антипаттерном (при условии что нельзя заменить на параметры по умолчанию). Особенно велик соблазн использовать логические операторы в длинных цепочках.
Можно добавить в список как сделать не очевидно — повсеместную замену обычных функций на стрелочные.
epishman
13.12.2018 12:09Со всем согласен кроме наезда на замыкания — по моему очень крутая штука.
cerberus_ab Автор
13.12.2018 12:12Это очень полезная штука ) на него не было наезда: там про сохранение контекста this или про расширение свойств экземпляра класса в конструкторе.
rboots
14.12.2018 15:24Идея статьи в том, что JavaScript читают и пишут, в том числе, разработчики из других языков и не нужно использовать фичи, не популярные в других языках. Но у меня зеркальный вопрос, а соблюдают ли такую практику разрабочтики других языков, чтобы их было удобно читать тем, кто привык разрабатывать на JavaScript? Почему мы должны перестраиваться? Только потому, что язык стал популярным и на нём стало писать много людей, которым лень нормально освоить синтаксис JavaScript?
Free_ze
14.12.2018 15:31Там проблемы с грамматикой, а синтаксис вполне себе си-подобный.
соблюдают ли такую практику разрабочтики других языков
Такого сочетания платформы, поддерживающей один язык и ее популярности нет, пожалуй, нигде в других областях программирования.
cerberus_ab Автор
14.12.2018 15:35Идея в том, что JavaScript позвляет это сделать без усилий в большинстве случаев. А на той, другой стороне как минимум две проблемы: 1) часто это строгие и не динамические языки и 2) на серверной стороне слишком много языков.
inew-iborn
14.12.2018 17:26как правило всякие неявные фишки языка используют в исключительных ситуациях.
в javascript я часто встречаю
if (foo === void 0)
неужели это всегда необходимо? все же
if (typeof(foo) === 'undefined')
намного читабельнее и понятнее
JustDont
14.12.2018 17:41Это тот прекрасный код, где объективно нужно по-разному обрабатывать undefined и null, или же просто обычный выпендрёж?
Потому что когда не надо — обычное "== null" таки куда понятнее.inew-iborn
14.12.2018 18:06не могу ответить, так как не я этот код писал. Только приходится иногда читать
inew-iborn
14.12.2018 20:30Постараюсь ответить более развернуто.
Одно из правил в программировании гласит что нужно уменьшать сложность. Код должен читаться как книга, конечно есть вещи которые нужно изучить для понимания ЯП, с этим я не спорю.
как одни из примеров:
()();
да я понимаю зачем это нужно, но жестить всегда, потому как "так позволяет язык, так пишут во фреймворках...", без каких либо оснований нет(я про
void 0
если что… и подобное...).
Я просто согласен с автором статьи, что сумасшедшие фишки языка нужны в библиотеках, фреймворках и т.д., они там нужны для того что бы их код не превратился бутылочное горлышко, но зачем пытаться пихать это все в код который не критичен к этому, это действительно похоже на выпендреж.
понятно что идеальных языков не бывает. Я не побоюсь этого слова, но меня иногда это бомбит, я общаясь с программистами которые пишут на javascript и они часто говорят что "без этого можно было обойтись"
как-то так...
TheShock
14.12.2018 17:46Самый читаемый и правильный вариант, который явно прописан в спецификации вот:
if (foo == null)
b360124
Ожидал очередную головоломку, а тут очередная статья, о том что js не идеальный. JSLint спасает от всех неоднозначных фич языка, а так вообще есть TypeScript, Flow где вообще все в рамках приличия, поэтому проблемы js — РЕШАЕМЫ и надуманы )))
cerberus_ab Автор
К сожалению линтером не обойтись… точки с запятой, скобки да strict equals. Многие вещи вполне допустимы для JavaScript-разработчика по самой природе языка, но со стороны очевидны не всем. И я специально не брал во внимание какое-то типизированное надмножество: у каждого свое ) но ведь можно в рамках приличия и без него.
b360124
Ну не знаю, у меня линтер справляется со всем кроме типизации. А насчет очевидности — ну тут уже зависит и от опыта. Ну проблема в js, в том что тут нельзя просто сделать deprecated конструкциям языка (потому что старые сайты должны работать в новых браузерах), поэтому и тянется легаси «неочевидностей».
cerberus_ab Автор
Мое скромное мнение в том, что не надо постоянно ссылаться на «легаси неочевидностей», показывая кому-то свой код ) Если вся команда — это JavaScript-мастера, то вопросов нет. Иначе стоит задуматься о возможностях языка быть понятнее, которыми он в конце концов располагает и не нуждается в deprecated (почти негде).
argonavtt
Практика показывает, что каждый настраивает линтер под себя, в итоге он не панацея. Проблем бы может быть с TypeScript не было, если бы ему опять таки не пришлось контактировать с библиотеками написанными на чистом js, в итоге внедрять его очень не просто, приходиться писать много типов, ну или добавлять через дженерик (а тогда теряется весь смысл в этой строгой типизации) особенно если это уже рабочий проект.