Как стать автором
Обновить

Комментарии 118

Все просто: JSLint про ошибки, JSCS про код-стайл.
Не могли бы вы привести больше примеров? Пока смутно понимаю, в чём принципиальное отличие. И в JSLint, и в вашем проекте можно настроить, например, необходимость фигурных скобок у if .. else, однако это не «ошибка», как вы пишете, а именно стиль оформления кода.
JSLint не покрывает требований по кодстайлу наших проектов. Да и не развивается он. Если бы и развивался, то архитектура в JSLint настолько ужасно, что контрибьютить — это последнее, что хочется.
Ну так может вы приведете список стилевых правил, которые поддерживает сабж? :)

А то получается как в анекдоте:

— Я сделал изобретение, позволяющее видеть сквозь стены!
— Но оно уже давно существует.
— Как же оно называется?
— Окно.
Их более 60-ти и все они описаны по ссылке, которую я привел в конце статьи: github.com/mdevils/node-jscs
1) JSLint — не только про ошибки.
2) Вы привели только один пример недостатка кода, который обнаруживает сабж: отсутствие пробела между function и скобкой. JSLint это отлавливает.
Цель моего вопроса была как раз в том, чтобы понять, какие уникальные настройки вы предоставляете в JSCS.
Предположим, есть проект, который использует JSLint. Как мне сконфигурировать(и для каких кейсов, что самое важное) JSCS, чтобы они не конфликтовали(ведь насколько я понял, у них есть дублирующий функционал)?
1) А с помощью утюга можно приготовить яичницу, например. Но вот испечь пирог будет проблемно.
2) Вы правы, пример с пробелом не очень-то наглядный, но идея понятна и всегда же можно перейти по урлу и мелько пробежаться глазами. Не поленитесь. Настроек очень много.
Более того, поддержка JSLint имеется практически у всех IDE и редакторов кода. Недостатки кода подсвечиваются во время написания, а не при запуске консольной команды.
Чуть выше ответ.


Дело времени.
Будем ждать для PHPStorm / WebStorm. Или уже есть?
Уже есть плагин.
Время выполнения !!x — 12мс, Boolean(x) — 310. С одной стороны это — на миллионе операций, а с другой — разница больше чем на порядок. Отличный код стайл.
Скрытый текст
(function(){
  var t = +new Date(), x = true;
  for( var i = 1000000;--i;){
    x = Boolean(x);
  }
  console.log(-(t-(t=new Date())));
  for( var i = 1000000;--i;){
    x = !!x;
  }
  console.log(-(t-(t=new Date())));

})();

Не знаю как вы, но я пишу код для людей, мучаться должны роботы и переделать код как им нужно при минификации ;)
Не знаю как вы, но я согласен с Zibx. Я пишу приведение к булеву через двойное отрицание не из-за количества символов, и не понимаю, чем плоха эта запись. Прочитать !!x не сложнее, чем Boolean(x), тем более работает быстрее.
Мне как и вам прочитать не сложно. Любой не посвященный в JS скажет, что !!x — читается как магия; Boolean(x) — все понятно ;)
Для непосвящённого в JS прототипное наследование — магия, например, что ж теперь, его выкидывать? Для тех, кто программирует на Си# странно, что замыкание создаётся только на уровне функций (а for, например, новую область не создаёт), так давайте не будем пользоваться замыкамиями! Они, кстати, сами по себе для многих — магия!
Поэтому не стоит делать из языка элитарное сообщество, ограниченное трехметровым порогом входа. Не забывайте, что есть ряд людей, которые не пишут фуллтайм на JS, но им приходится с ним работать.
Не совсем понял, что вы имели в виду под «замыкание создаётся только на уровне функций (а for, например, новую область не создаёт)» — можно пример кода для ясности?
Классика:
for (var i = 0; i < 3; i++) {
    setTimeout(function () {
        alert(i);
    }, 100);
}
Я так и думал, но проверил следующим куском кода и он отработал ожидаемым образом, выведя последовательно числа от 0 до 4:

var data = [];

for(var idx = 0; idx < 5; idx++) {
    var curr = function() { alert(idx); };
    data.push(curr);
}

for(var idx = 0; idx < 5; idx++) {
    data[idx]();
}

Не могу понять, в чем разница между вашим кодом и моим?
у вас во втором цикле переменная тоже называется idx, показывается ее значение
Спасибо, разобрался!
Здесь есть тонкий момент, во втором цикле вы на самом деле используете ту же самую переменную idx, что и в первом случае, попробуйте переименовать её в напрмер в jdx и все станет на свои места:

	var data = [];

	for(var idx = 0; idx < 5; idx++) {
		var curr = function() { alert(idx); };
		data.push(curr);
	}

	for(var jdx = 0; jdx < 5; jdx++) {
		data[jdx]();
	}
Ваш код синхронный. Мой асинхронный.
Это чуть ли не худший ответ, который вы могли дать. Он не просто неправильный — он может создать у новичка ложное ощущение правильного понимания причины проблемы.
Я ответил вполне на конкретный вопрос: «Не могу понять, в чем разница между вашим кодом и моим?». Не знаю стало ли проще если я бы ответил, что в JS область видимости через var создает только функция, поэтому в следующем тике event loop значение переменной будет 3 и эта переменная будет общая для всех безымянных «замыканий» аргумента setTimeout.
Нет, проще бы не стало. Так как новый ответ снова как минимум некорректен в плане формулировки:
1) var никаких областей видимости не создает (с функцией или без)
2) то, будет ли функция вызываться асинхронно в другом тике или нет с обсуждаемой проблеме на самом деле не имеет никакого отношения
Прототипное наследование — одна из основных особенностей языка, а использование двойного отрицания для неявного приведения к булеву это использование фич языка не по назначению, побочное свойство некоторых особенностей.
Ну, любая поддержка кода требует понимания языка, на котором он написан. Я думаю, как bolk сказал ниже, проблема в том, что качество программистов падает, и людям становится слишком лениво разбираться в тонкостях работы с языком. Порой даже попадаются индивидумы, которые могут писать селекторы с использованием jQuery, но вытащить элемент по id на нативном JS не могут. Грустно всё это…
Вот уж не знаю, по моему все довольно очевидно, такой код вполне нормально работает в си/с++
Когда я первый раз встретился с ~~x, то тоже сначала растерялся, а ведь удобно.
Смотря для каких целей.
Я предпочитаю, например, каноничное "| 0".
Спасибо)
Да это просто качество программистов падает. В языке разобраться лениво, поэтому писать можно только «не смущающими новичка конструкциями».
В данном случае я не соглашусь с собой. Не вникал в поведение Boolean без new. Был уверен что это фабрики и что поведение может в итоге ожидаться такое, которое не смогут оптимизировать роботы. Но в яндексе при сборке кода Boolean без new скорее всего и превратится в !!.. То есть скорость исполнения будет та же. У скорости ввода есть две стороны. Обычно скорость написания кода упирается не в скорость печати. Новичкам и тем кто не знаком с js понятнее запись Boolean. С другой стороны я был уверен что абсолютно все кто касается js в яндексе среди ночи смогут рассказать всё про автоматическое приведение типов.

Да и статья о другом, думаю эту тулзу можно настроить в обратную сторону, что бы она ругалась когда вместо!!! стоит Boolean :).
Интереснее причина такого кодстайла где в js после function нужен пробел. Очень похоже на портированный PEP8.
Сложнее прочитать. Не вникая в тонкости неявного приведения типов запись кажется семантически бессмысленной (двойное отрицание аргумента равносильно аргументу в булевой логике, а! булевый оператор), поведение в граничных случаях не очевидно. В общем, обычный хак. Если скорость выполнения важна, то согласно правилам хорошего тона его нужно дополнять комментарием типа // fast convert to boolean, а если не важна, то просто его не использовать.
Попробуйте для подобных сравнений jsperf использовать — очень удобно.
jsperf.com/double-negative-vs-boolean

Кстати, мне тоже больше нравится!!! х, по сравнению с Boolean(x). Читается легче.
Отличный сервис, использую. Но когда для себя замеряю попугаев — быстрее нажать F12 чем заполнять все их поля.
Ну верно, ваш код стайл просто восхитителен.
Boolean(x) === false — читаемее, хоть запись и получается длиннее.
Всякие !!x, и уж точно побитовые операции на вроде вашей +new Date() или всякие ~array.indexOf(x) стоит использовать только в больших циклах, где такая экономия на спичках имеет хоть какой-то смысл.
Это звучит как «да ну ваше наследование через цепочку прототипов, я подключу CoffeeScript и буду писать классы».
Если честно, я не понял при чем здесь прототипы и классы. Разговор вообще о другом.
Речь о том, что логическое отрицание(!) или побитовое отрицание(~) — это просто и понятно. Это есть почти во всех языках программирования. Не знать этого довольно-таки странно для программиста. Исходя из вашего поста выше, вы пишете так:

if (Boolean(arr.length) === false) {
  ...
}

вместо

if (!arr.length) {
  ...
}

верно?
Вы кажется перегибаете, любой вменяемый программист будет писать !arr.length
Тогда почему !arr.length — является «нормальным» приведением к булеву, а !!arr.length — нет?
Вменяемый вообще напишет arr.length === 0, если он хочет проверить не пустой ли массив.
Да, мне, кстати, с недавних пор тоже стало нравиться писать так. Вообще, как по мне, практика с явным определением типов — штука правильная и хорошо дисциплинирует. Но другое дело, когда мы хотим проверить параметр объекта, возможно оно false, 0 или вовсе undefined, вот тут я все же использую if (obj.param) {}
Для меня 0 и undefined — это совершенно разные вещи и в одном условии я оба сразу не проверяю. Нет кейсов.
Конечно разные. В данном случае я рассматривал 0 как упрощенный вариант false в качестве ответа от сервера. Но так же сервер и вовсе может не установить в качестве ответа ничего. А когда мы знаем, что, возможное значение — число, пусть даже 0, то это совсем другой разговор. Так что все это частные случаи.
undefined вот таким словом лучше не писать никогда. undefined — не константа, его можно от скуки переопределить. === void 0 или typeof, но при typeof производится не бесплатная операция сравнения со строкой (хотя может тут работает оптимизация глобального места где хранятся все строки). Иногда ещё 'param' in obj пригождается.
Мне всегда казалось, может быть наивно, что сравнение итога typeof оптимизировано в движке. Правда, не то чтобы я читал исходники V8 или IonMonkey.
Это в общем- то и не нужно.

Достаточно посмотреть к примеру на тот asm код который v8 генерирует.

Нет желания проверять, но почти уверен что все эти сравнение будут иметь одинаковый результат. JIT он не дурак и это его работа такие вещи оптимизировать.
Или в функциях, которые могут позвать из циклов. Или цикл может оказаться вложенным. Ещё забавно наблюдать как функцию мэппинга объявляют в цикле. Но я полностью согласен с тем что ресурсотерпящие вещи лучше писать читаемыми.
Мне кажется вы лукавите. Не знаю как там у вас, но я всегда знаю, какая функция чем может быть запущена, и окажется ли она в цикле. А если мы говорим про какие-то действительно монструозные вещи, на вроде библиотек, когда и правда не ясно, что будет делать другой разработчик с вашим кодом — то тогда уж точно разницы нет — время выполнения полезного кода будет выше, чем любая экономия.
Я говорю про функциональный подход. Когда в функции мэппинга делают ещё мэппинг. Тут уже на 1000 иттерациях может оказаться заметна разница. Библиотеки я как раз по этой причине избегаю почти все и использую microjs если есть конкретная задача, которая точно уже решена другими. Как наглядный пример такого подхода я обычно привожу конструкцию вида $('#el1').height(). Есть случаи когда достаточно сделать document.getElementById('el1').clientHeight. И вот в этом случае я прошу пройти по стеку того что делает жквери, что бы в итоге вызвать эту конструкцию. Но это важно в основном при отрисовке больших списков данных или когда мы пишем веб-приложение и весомая часть модели живёт в браузере. В случае если нам надо повесить событие на клик кнопки logout — тут действительно можно сделать это чем угодно. Но при этом событие mousemove использовать обёрнутое любой библиотекой — всё равно крайне не желательно, если мы хотим сделать плавную анимацию. Что-то я разошелся, оптимизация у меня — больная тема. :)
Boolean(x) === false
Зачем? Неужели так читаемее?
Я вам больше скажу, в таких местах даже читаемее использовать Йода-стайл на вроде false === Boolean(x)
Только не нужно кидаться тухлыми помидорами, не попробовав на практике такой подход. Это мы тут с вами снипеты в 2 строки разбираем, на реальном же коде особенности оформления уже играют большую роль в понимании.
«Йода-стайл», как вы его назвали, родился вовсе не из читаемости. Соглашение «константа слева» спасает от ситуации, когда программист пропускает одно равно и получает if (a = false) вместо if (a == false). В вашем же случае «false === Boolean(x)» это соглашение лишнее, да и вообще вся конструкция должна умереть.
Не сочтите за грубость, но вы же мне просто глаза открыли. Тем более, что про указанное вами соглашение можно не беспокоиться в рамках JS, так как if (x = 2) {} вызывает ошибку. Я же не заставляю никого писать так, как нравится мне, и как пишет команда автора из Яндекса, а у них-то наверное больше аргументов на счет явного приведения типов.
НЛО прилетело и опубликовало эту надпись здесь
Вот теперь можно констатировать, что я, кажется, сошел с ума. Я серьезно. Просто вот сейчас я не могу объяснить, как я такую ахинею мог написать, и как я так пришел к тому, к чему пришел. Стыдоба.
Дело скорее всего не в производительности, а в однозначности поведения. Чтобы код отрабатывал максимально одинаково на всех существующих известных, неизвестных и теоретически возможных платформах…
От этого спасут только тесты. Я вот был уверен что Boolean возвращает не примитив, а объект как в случае с new Boolean(false). И у меня есть уже немного опыта js, и потому я проверил и теперь знаю, а те кто делают код ревью могут не знать такой тонкости.
Проверить объект это или примитив можно присвоив свойства объекта, а потом взяв его.
Микробенчмаркинг зло. Он показывает не цифры скорости какие-то цифры. JIT браузера очень интересно оптимизирует код.
Пропустите код выше через V8 со специальными флагами посмотрите на ASM код который получится. Будете удивлены)
А нет в планах пойти дальше и не просто выводить ошибки, а делать авто форматирование кода? Пиши как хочешь, а хук на pre-commit приводит весь код к единому код стайлу.

P.S. Сам пробовал jsbeautifier и esformatter, увы оба обладают своими недостатками, последний так совсем коверкает исходный файл.
В планах этого нет. Цель проекта в том, чтобы помочь привыкнуть и с помощью CI — контролировать.
Очень жаль.
И мне тоже. Новая команда — и сразу обучение новому стилю, хотя логика от этого не меняется. В PHPStorm видел при коммите возможность переформатировать код автоматически, только вроде это к php относится и, если и к JS, то изменения типа !!x -> Boolean(x) не делает.
Открывая статью надеялся, что это не про другой JSLint, а про автоматическое форматирование кода — настроил свои правила, и при чекауте получаешь JS-код в своём любимом стиле, а при коммите он переформатируется в принятый в команде.
Зачем мне сообщение о забытом пробеле и облом коммита? Компьютер железный — пусть он и расставляет (не)нужные пробелы.
Это был бы идеальный инструмент!
WebStorm же это умеет делать. Один раз настроил правила и всё ок.
не умеет(на сколько я знаю). Он умеет форматировать код так, как вам нужно.
Но тут человек хочет получить Boolean(x), а видеть !!x, например
Тогда нужно и обратное преобразование делать (при пуллиге кода из репозитория). Иначе смысл теряется.
1. Выбрать одно лучшее IDE (мы, например, выбрали IDEA)
2. Использовать дефолтные настройки и не лезть со своим Code Style (мы просто жмем Ctrl + Alt + L, срабатывает авто-форматирование и стиль у всех один)
3. Continuous Integration.

Тут и начинаются проблемы.
Какие?
меня бы очень расстроило если бы кто-то начал мне говорить какую IDE использовать
Одну IDE использовать совсем не обязательно, есть тот же editorconfig, например.
Неужели в вашей любомй IDE нет авто-форматирования кода?
может и есть, но в комментарии, на который я отвечал, предлагали всей командой перейти на одну IDE
Ммм, да, я почему-то прочитал его как «настроить свои IDE единообразно, согласно code style команды и пользоваться авто-форматированием». Удивляюсь теперь, как так вышло.
НЛО прилетело и опубликовало эту надпись здесь
И, может быть, имеет смысл написать средство форматирования кода и использовать его повсеместно?


Дописать clang-format, например, сейчас там уже есть минимальный задел на js. Тем более, такая вещь нужна всему Яндексу.
НЛО прилетело и опубликовало эту надпись здесь
Звиняйте, не разобрался, думал скрипт для баша…
Вот смотрю я на эту форму фидбека и не могу придумать, какая там может быть нетривиальная логика внутри. Поделитесь, что она делает хоть, кроме очевидной отправки фидбека?
Рейтинг там примеру со звёздами, который можно попариться при наведении мышки красиво анимировать. Форма может увеличивается при наборе текста. Анимация при добавлении какая-нибудь.

Всегда легко посмотреть на что-нибудь вроде твиттера и хмыкнуть «ха, бложик на 140 знаков».
Так я же не хмыкаю. Наоборот, хочу постичь и приобщиться. Тем более, что фраза «нетривиальная логика» подразумевает не просто анимацию звездочек
А не было варианта просто настроить инспекции в каком-нибудь ВебШторме? Или том же Виме/Емаксе, я ими сам не пользуюсь, но раз они осиливают подсветку, значит с синтаксическими конструкциями работать в принципе можно.
Если честно, размер проекта на гитхабе поистине пугает. я бы ограничился примитивным скриптом, выдающим ошибки в типичном формате компиляторов <path>:<line>:<message>. Vim (syntactic), Emacs (compile/recompile, flymake) и ST (из коробки) прекрасно понимаю такой формат и позволяют прыгать к исходникам и выделать строки с ошибками.
Очень хорошо, что осведомили. Кабы карма Ваша не была и без того заплюсована мною — беспременно заплюсовал бы её сегодня.

А не то, видите ли, я до сих пор сидел на JSHint 2.x, потому что произошедшие в 3.x перемены (отказ от проверки стиля) ну никак не мог принять.

Конечно, теперь (когда есть отдельный стилепроверщик) — это неплохо. Есть куда убежать с JSHintовской проверки стиля.

(Хорошо бы кто-нибудь #138 пофиксил, конечно; но и без того очень, очень неплохо.)
НЛО прилетело и опубликовало эту надпись здесь
Кода довольно много, что бы пролистать, поэтому проще спросить — проводится ли семантический анализ, посроение AST? Если нет, почему недостаточно регулярных выражений, на которые способно большинство полноценных текстовых редакторов, ну или sed awk? Какого рода парсинг проводится, если не секрет?
Парсинг и построение AST с помощью esprima.
Вызывает уважение. Тогда по обмену опытом, мой сейчас любимый язык Go включает такого рода утилиту форматирования в коробочной поставке. Конфигурация не предусматривается, поскольку полагается что весь код на Go должен быть написан единообразно. Зато присутствует зачаточная возможность символьной трансформации, то есть например можно задать правило 2*x -> x+x
Классика жанра:
image
Почему бы не сделать описание правил форматирования на основе BNF?
Что-то я не понял.
Есть команда, в которой принят некий стиль. Там же есть, видимо, специально обученный человек, который высматривает пробелы и грепает восклицательные знаки. Сам расставлять пробелы он не умеет, просто рефьюзит коммит. Вы (автор статьи) написали утилиту, которая заменяет этого человека. И команде ничего не говорили… Мне это как-то дико. Или в команде тоже используется какой-то инструмент, но вы решили написать свой? Или в команде у каждого свои костыли имеются? Я работал в разных командах и что-то не могу представить вашу ситуацию.
Спрашиваю потому, что уже не первый раз рассказы о том, как работает Яндекс, вводят меня в недоумение.
В каких-то странных командах вы работали.
Процедуру код ревью себе представляете? Я, на всякий случай расскажу. Вы пишете код, ваши коллеги его смотрят, пишут замечания, вы исправляете (ну или обосновываете, что написали как надо) код в соответствии с замечаниями, отправляете на повторное ревью и т.д…
То есть не нужен специальный человек для этого.
А автору статьи было сложно привыкнуть к новому для него стилю кода, и для помощи себе он написал утилиту, которая оказалась полезной множеству людей.
То что общепринятые практики разработки ПО вводят вас в недоумение, то у меня для вас плохие новости.
Во-первых.
Мне всегда попадались люди, которые обнаруживали ошибки типа «это не заработает на python 2.3», «здесь лучше использовать другую структуру данных», «у нас принято писать комментарии на английском языке»… К пробелам и деталям типа «типов в языках со слабой типизацией» либо не привязывались, либо проверяли линтерами и прочими инструментами.
Во-вторых.
Мне попадались команды, где разработанные инструменты тут же становились общим достоянием. Макросы к редакторам, анализаторы логов, и даже форматировалки исходников. Все эти вещи разрабатывались совместно. И мне действительно трудно представить противоположную ситуацию. Почему человек вынужден делать для себя очевидный инструмент? Почему, сделав его, он не предлагает его команде? Ждёт, когда каждый сделает себе свой? Я действительно не понимаю логику такого поведения.
К пробелам и деталям типа «типов в языках со слабой типизацией» либо не привязывались, .

Ну, что не привязывались — в этом ничего хорошего нет, я сторонник строго придерживаться стайл-гайда.
либо проверяли линтерами и прочими инструментами

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

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

Статью читали? В статье явно написано, что с коллегами поделился. А теперь и со всеми желающими за пределами компании (что гораздо важнее, как мне кажется).

Вы выглядите приятным, разумным человеком, но в первом комментарии какую-то ерунду написали, честное слово.
Недавно наткнулся на ESLint. Проект позиционирует себя как модульная альтернатива jslint/jshint, где каждый может легко добавлять свои правила. К сожалению пока руки не дошли попробовать, но было бы интересно сравнить и узнать мнение тех кто пользовался
Уже много месяцев пользуюсь вашей разработкой в виде grunt-плагина grunt-jscs-checker во всех проектах. Ужасно доволен, спасибо вам огромное!
Не прочитал все коментарии, возможно уже спрашивали. Планируется ли сделать удобный интерфейс для генерации конфига jscs? Чтобы не читать весь ридми с последующей копипастой, а прощелкивать слайды, а в конце копировать конфиг?
Нет, простого интерфейса для конфигурации нет. Но если вы разработаете, мы будем счастливы его принять и развивать.
Ок, попробую набросать прототип на gh-pages в форке к вашему репу. Вопрос в том, будут ли его поддерживать, при добавлении новых правил, чтобы держать в актуальном состоянии…
Если будет удобно и понятно реализовано, то пользователи смогут контрибьютить с учетом этой страницы.
Судя по камментам мало кто читал гитхаб и мало кто понял зачем данная итилита. Печально.
У нас в компании царят формат-фашизм и граммар-нацизм, поэтому JSCS наряду с JSHint стоит на хуках в репозиториях.
Нестильный код не пройдёт!

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

Публикации