Джед Шмидт, Томас Фухс и Дастин Диаз — достаточно известные в JavaScript-коммьюнити ребята в последнее время нашли себе новую развлекуху — писать полезные штуки размером не больше одного твита, то есть 140 байт. Даже домен зарегали —
140byt.es, куда приглашаются все желающие попробовать свои силы в написании супер-компактных функций.
Естественно, в ход идут все самые изощренные способы и техники уменьшения размера исходника. У них есть вики-страничка с советами, которую я и решил перевести.
Сразу оговорюсь, что читаемость обработанного таким образом кода стремится к нулю, так что использовать эти трюки стоит только в случаях, когда размер действительно превыше всего. Например, при участии в конкурсе JS1k.
Итак.
Arguments
Используйте однобуквенные позиционные аргументы в алфавитном порядке
Поскольку аргументы функции должны быть как можно более короткими, и скорее всего будут использованы по несколько раз во время выполнения, проще рассматривать их с точки зрения позиции и именовать в алфавитном порядке, вместо того, чтобы пытаться давать им сколько-либо осмысленные имена.
function(t,d,v,i,f){...} // до
function(a,b,c,d,e){...} // после
Проверяйте наличие аргументов вместо длины
Можно использовать
in
для проверки наличия аргумента.
arguments.length>1||(cb=alert) // до
1 in arguments||(cb=alert) // после
Переменные
Используйте «лишние» аргументы вместо var
Можно сэкономить несколько байт указав лишний аргумент в функции, вместо объявления переменной с помощью
var
:
function(a){var b=1;...} // до
function(a,b){b=1;...} // после
Используйте переменные по несколько раз
setTimeout(function(){for(var i=10;i--;)... }, a) // до
setTimeout(function(){for(a=10;a--;)... }, a) // после
Используйте присваивание там, где это возможно
Поскольку оператор присваивания возвращает присваиваемое значение, можно использовать присваивание и проверку одновременно:
a=this.localStorage;if(a){...} // до
if(a=this.localStorage){...} // после
Используйте массив чтобы поменять местами переменные
Массив можно использовать как временное хранилище, чтобы не объявлять лишнюю переменную.
var a=1,b=2,c;c=a;a=b;b=c // до
var a=1,b=2;a=[b,b=a][0] // после
Используйте приведение типов при сложении
Приведение типов в JS работает весьма странно и является одним из самых распространенных источником багов. Тем не менее, его можно использовать разными интересными способами для уменьшения размеров кода.
Наример, в реализации
pubsub Джед Шмидт декрементировал переменную с отрицательным числом, а затем прибавлял ее к строке, получая что-то вида
"somestring-123"
.
После этого в другом месте использовал
.split('-')
для получения исходной строки.
Циклы
Опускайте тело цикла
Зачастую можно реализовать всю логику внутри условий и сэкономить на теле цикла.
Хороший пример этого подхода можно посмотреть в функции
timeAgo.
Используйте for вместо while
for
и
while
обычно занимают одинаково количество байт, но
for
позволяет получить больший контроль и больше возможностей для присваивания.
while(i--){...} // до
for(;i--;){...} // после
i=10;while(i--){...} // до
for(i=10;i--;){...} // после
Используйте быструю итерацию по «правдивым» массивам
Если у вас есть массив, все члены которого заведомо приводятся к true, можно использовать более короткую запись цикла:
for(a=[1,2,3,4,5],l=a.length,i=0;i<l;i++){b=a[i];...} // до
for(a=[1,2,3,4,5],i=0;b=a[i++];){...} // после
Используйте for..in
с присваиванием для получения ключей объектов
a=[];for(b in window)a.push(window[b]) // до
a=[];i=0;for(a[i++]in window); // после
Операторы
Выучите приоритет операторов
Эти знания могут помочь неплохо сэкономить на скобках.
Начать можно с изучения
этой статьи на сайте Mozilla.
Используйте ~
c indexOf
hasAnF="This sentence has an f.".indexOf("f")>=0 // до
hasAnF=~"This sentence has an f.".indexOf("f") // после
Используйте запятую для последовательного выполнения операторов вместо блока
with(document){open();write("hello");close()} // до
with(document)open(),write("hello"),close() // после
Используйте более короткие способы записи undefined
Вместо
undefined
можно использовать
[]._
или
void 0
.
Есть варианты
""._
,
1.._
и
[][0]
, но они
намного медленнее.
Удаляйте необязательные пробелы перед операторами
Иногда пробелы после операторов можно безболезненно удалить.
typeof [] // до
typeof[] // после
Числа
Используйте ~~
или 0|
вместо Math.floor
rand10=Math.floor(Math.random()*10) // до
rand10=0|Math.random()*10 // после
Используйте экспоненциальный формат для больших круглых чисел
million=1000000 // до
million=1e6 // после
Используйте побитовые сдвиги для больших бинарных чисел
color=0x100000 // до
color=1<<20 // после
Используйте 1/0
вместо Infinity
Это короче. Кроме того, делить на нуль всегда весело.
[Infinity,-Infinity] // до
[1/0,-1/0] // после
Используйте «ложность» нуля
Вместо сравнивания чисел иногда короче свести значение к нулю и проверить его истинность.
a==1||console.log("not one") // до
~-a||console.log("not one") // после
Используйте ~
чтобы изменить любое значение на единицу
В сочетании с унарным минусом это дает возможность, например, инкрементировать любую, даже еще не определенную переменную.
// i = undefined
i=i||0;i++ // до
i=-~i // после
Строки
Разбивайте строки с помощью нуля
Можно сэкономить два байта при разбиении строк методом
split
, если в качестве разделителя использовать нуль:
'alpha,bravo,charlie'.split(',') // до
'alpha0bravo0charlie'.split(0) // после
Используйте браузерный метод link
Строки в браузерах имеют не очень известный метод
link
, который создает html-ссылку.
html="<a href='"+url+"'>"+text+"</a>" // до
html=text.link(url) // после
Используйте методы replace
и exec
для итерации по строкам
Эти методы позволяют передавать функцию в качестве второго аргумента. Этим можно воспользоваться для удобной итерации по строке.
Примеры использования:
templates и
UUID.
Используйте массивы для создания простых строк
for(a="",i=32;i--;)a+=0 // до
a=Array(33).join(0) // после
Регулярные выражения
Используйте
{n}
для укорачивания рeгулярных выражений. Например
/\d{3}/
вместо
/\d\d\d/
. И наоборот
/\d\d/
вместо
/\d{2}/
.
Можно использовать
eval
вместо конструктора регулярки:
r=new RegExp("{"+p+"}","g") // до
r=eval("/{"+p+"}/g") // после
Boolean
Используйте
!
с цифрами для создания
true
и
false
.
[true,false] // до
[!0,!1] // после
Функции
Используйте именованные функции для рекурсии вместо циклов
Зачастую это получается короче, поскольку позволяет протаскивать значения через стек, без лишних переменных.
В качестве примера функция
walk.
Используйте именованные функции для хранения состояния
Если надо хранить состояние между вызовами функции, функцию можно использовать как объект и хранить данные в ее свойствах:
function(i){return function(){console.log("called "+(++i)+" times")}}(0) // до
(function a(){console.log("called "+(a.i=-~a.i)+" times")}) // после
0,function a(){console.log("called "+(a.i=-~a.i)+" times")} // еще вариант
Опускайте скобки при вызове конструктора без аргументов
now = +new Date() // до
now = +new Date // после
Опускайте ключевое слово new там, где это возможно
Некоторым конструкторам вовсе не обязательно ключевое слово
new
.
r=new Regexp(".",g) // до
r=Regexp(".",g) // после
l=new Function("x","console.log(x)") // до
l=Function("x","console.log(x)") // после
Оператор return
Когда надо вернуть что-то отличное от переменной, ставить пробел после
return
не обязательно.
return ['foo',42,'bar']; // до
return['foo',42,'bar']; // после
return {x:42,y:417}; // до
return{x:42,y:417}; // после
return .01; // до
return.01; // после
Пока все.
Вообще, рекомендую ознакомиться с плодами их творчества. В попытках уместить сложные вещи в 140 байт ребята иногда просто творят чудеса. Я думаю, даже опытный программист найдет для себя что-то новое и интересное в их коде.