Frontender Magazine

JavaScript-хаки для хипстеров

Писать код на JavaScript — в основном чистая радость, за исключением тех моментов, когда это не так.

Все время, пока мы пишем код, нас не покидает страх об ошибках во время исполнения кода в браузере. Конечно, это заставляет нас расти, как программистов. И, в общем-то, у нас нет других вариантов, кроме как мысленно представить себе, как будет исполняться та или иная строчка кода, пока мы пишем код.

Вот почему настолько важно, чтобы код был аккуратным, компактным и красивым. Таким, чтобы вы могли в него влюбиться. Иначе JavaScript отпугнет вас раз и навсегда.

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

Все эти сниппеты я нашел в открытых источниках (до выхода Node.js весь код JavaScript так или иначе был открытым), но я опубликую их тут так, как будто я сам все это придумал.

Хипстер-хак №1 - вызов метода

Меня действительно бесят конструкции if/else, и я нашел довольно полезный шаблон, который вызывает нужную функцию в зависимости от булевой переменной.

// Тоска
if (success) {
  obj.start();
} else {
  obj.stop();
}

// По-хипстерски
var method = (success ? 'start' : 'stop');
obj[method]();

Хипстер-хак №2 - склеивание строк

Хорошо известно, что строки притягиваются друг к другу. Рано или поздно вам придется соединить их вместе. Я совершенно не люблю склеивать их через +, в этой ситуации меня выручает метод join().

['имя', 'фамилия'].join(' '); // = 'имя фамилия';

['молоко', 'кофе', 'сахар'].join(', '); // = 'молоко, кофе, сахар'

Хипстер-хак №3 - оператор по умолчанию ||

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

// Переменная примет значение 'Без имени', если 
// myName пустая (или null, или undefined)
var name = myName || 'Без имени';


// проверим, что у нас есть объект options
var doStuff = function(options) {
  options = options || {};
  // ...
};

Хипстер-хак №4 - оператор проверки на существование &&

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

// Скучно
if (isThisAwesome) {
  alert('yes'); // не исполнится
}

// Круто!
isThisAwesome && alert('yes');

// Более безопасный вариант
var aCoolFunction = undefined;
aCoolFunction && aCoolFunction(); // не запустится и не обрушит ваш код

Хипстер-хак №5 - заглушка XXX

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

var z = 15;
doSomeMath(z, 10);
xxx // Отличная заглушка. Только я использую xxx,  
    // эту строчку проще найти, чем TODO
doSomeMoreMath(z, 15);

Хипстер-хак №6 - замерить время

Вы никогда не задумывались, что быстрее — цикл через i++ или i--? Я, честно говоря, не думал об этом. А тем, кому есть до этого хоть какое-то дело, я бы посоветовал использовать внутренние методы консоли для скорости. Эти методы позволяют находить медленные циклы или другой код, который блокирует цикл выполнения событий.

var a = [1,2,3,4,5,6,7,8,9,10];

console.time('testing_forward');
for (var i = 0; i < a.length; i++);
console.timeEnd('testing_forward');
// output: testing_forward: 0.041ms

console.time('testing_backwards');
for (var i = a.length - 1; i >= 0; i--);
console.timeEnd('testing_backwards');
// output: testing_backwards: 0.030ms

Хипстер-хак №7 - отладка

Этот прием я узнал от знакомого мне разработчика на Java. Понятия не имею, как вышло так, что он его знал, а я нет, но с тех пор я использую этот прием. Просто напишите в коде debugger.

var x = 1;
debugger; // Всё, код перестает исполняться, удачной отладки
x++;


var x = Math.random(2);
if (x > 0.5) {
  debugger; // Условная точка прерывания
}
x--;

Хипстер-хак №8 - олдскульная отладка

Мне, как разработчику, всегда было комфортнее использовать printf-отладку, а не отлаживать код построчно. Если вы разделяете мои пристрастия, то вам нужно будет предварительно «вытащить» внутренние переменные в глобальное пространство имен, для того, чтобы время от времени их просматривать. Не забудьте убрать эти данные перед тем, как делать коммит в систему контроля версий или выкладывать ваше приложение в продакшн.

var deeplyNestedFunction = function() {
  var private_object = {
    year: '2013'
  };

  // Глобальный объект для отладки
  pub = private_object;
};

// Теперь его можно вызывать из консоли
pub.year;

Хипстер-хак №9 - сверхлегкие шаблоны

Как, вы еще занимаетесь конкатенацией строк через оператор +? Ведь есть способ, который гораздо лучше подходит для добавления ваших данных в строку. Он называется шаблонизацией, вот мини-фреймворк для этого способа, размером буквально в 2,5 строчки кода.

var firstName = 'Таль';
var screenName = 'ketacode'

// Чудовищно
'Привет, меня зовут ' + firstName + ', а мой твиттер — @' + screenName;

// Отлично
var template = 'Привет, меня зовут {first-name}, а мой твиттер - @{screen-name}';
var txt = template.replace('{first-name}', firstName)
                  .replace('{screen-name}', screenName);

Как, вы все это уже знали?

Даже мой патентованный оператор XXX?! Ну что ж, готов признать, вы настоящий хипстер от JavaScript, давайте поговорим об этом в твиттере.

Если вы заметили ошибку, вы всегда можете отредактировать статью, создать issue или просто написать об этом Антону Немцеву в skype ravencry.

Tal Bereznitskey
Автор:
Tal Bereznitskey
Twitter:
@ketacode
GitHub:
berzniz
Сaйт:
http://berzniz.com/
Vlad Andersen

Комментарии (15 комментариев, если быть точным)

Автар пользователя
drugoi

А разве xxx не вызовет ReferenceError?

Автар пользователя
arteg

Советы для тех, кто не читал ни одной книги по js.

Автар пользователя
cosmiksoul

А разве xxx не вызовет ReferenceError?

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

Автар пользователя
agegorin

"xxx" становиться не нужен, когда вы начинаете пользоваться нормальными IDE. Они сами выводят список всех прописанных TODO. Ну и потом чаще необходимо чтобы все хоть как-то работало и без того что надо сделать. У меня в коде обычно рядом с TODO висит console.log() с описанием того что должно произойти.

Первый хак - так вообще только усложняет читаемость и такой код сложней модифицировать.

Остальное хорошо, да.

Автар пользователя
jemcik

если быть хипстером до конца, то как минимум первый хак должен быть таким: objsuccess ? 'start' : 'stop'; :)

Автар пользователя
SilentImp

пользоваться нормальными IDE

Ну, у меня не возникает желаения пользоваться чем то вроде Komodo IDE, который грузится чуть ли не минуту на макбуке последнего поколения. Меня полностью устраивает Sublime Text. И я не стану отказыватся от него ради возможности писать todo и заменять автоматически имена переменных и методов, если мне вздумается их переименовать.

Автар пользователя
agegorin

Ну во-первых IDE, как правило запускается один раз в день. Минута-две - мелочь в таком случае. Во-вторых я почти уверен что для Sublime Text существует подобный плагин. :) Ну и еще я бы посоветовал посмотреть в сторону WebStorm.

Автар пользователя
dydyman

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

if (success) obj.start(); else obj.stop(); // и никаких понтов

То же касается и 9 хака - если нужен быстрый код, то чем меньше высокоуровневых абстракций - тем лучше.

if ('хипстер' === 'junior') остальные советы справедливы ;)

Автар пользователя
husa

вместо var doStuff = function(options) { options = options || {}; // ... };

можно: var doStuff = function(options) { options || (options = {}); // ... };

Автар пользователя
grabus

давно было на хабре http://habrahabr.ru/post/204430/

Автар пользователя
zefirka

Ну охренеть теперь. Конечно, for (var i = 0; i < a.length; i++); работает медленнее for (var i = a.length - 1; i >= 0; i--); , но только за счет того, что в первом случае к a.length обращаются a.length-1 раз, а во-втором случае всего один раз. Инкремент или декремент - никакой разницы, нет. Автор бы знал это, если бы имел представление о реализации языка "под капотом". for (var i = 0, l=a.length; i < l; i++); - такая конструкция была бы примерно так же быстра, как и вторая.

Да и вообще, большинство "хаков" - сомнительные излишества тормозящие приложение только за счет своей хипстеровости. Зачем вводить лишние переменные и прочие эстетства? Код должен быть понятным и удобным, а не красивым и вычурным, да еще и тормозным.

Автар пользователя
JeremyWheelks

@zefirka, забыл добавить, что основная фишка в обратном проходе, это отсутствие двойной проверки условия завершения цикла, то есть должно быть так:

for (var i = a.length - 1; i; i--);

Между прочим, прекрасная оптимизация, если нас не волнует порядок обработки элементов цикла.

Автар пользователя
pokatomnik

С String.prototype.replace также чудовищно, так как он заменяет только первое вхождение строки. Тут есть 2 решения - шаблонные строки из es6 или использовать String.prototype.replace, передавая первый аргумент как регулярку.

Автар пользователя
subzey

@pokatomnik, можно ещё через split-join:

"foo bar baz".split("b").join("B"); // "foo Bar Baz"

Автар пользователя
pokatomnik

А мсье знает толк😉