Джед Шмидт, Томас Фухс и Дастин Диаз — достаточно известные в JavaScript-коммьюнити ребята в последнее время нашли себе новую развлекуху — писать полезные штуки размером не больше одного твита, то есть 140 байт. Даже домен зарегали — 140byt.es, куда приглашаются все желающие попробовать свои силы в написании супер-компактных функций.
Естественно, в ход идут все самые изощренные способы и техники уменьшения размера исходника. У них есть вики-страничка с советами, которую я и решил перевести.
Сразу оговорюсь, что читаемость обработанного таким образом кода стремится к нулю, так что использовать эти трюки стоит только в случаях, когда размер действительно превыше всего. Например, при участии в конкурсе JS1k.
Итак.
Поскольку аргументы функции должны быть как можно более короткими, и скорее всего будут использованы по несколько раз во время выполнения, проще рассматривать их с точки зрения позиции и именовать в алфавитном порядке, вместо того, чтобы пытаться давать им сколько-либо осмысленные имена.
Можно использовать
Используйте «лишние» аргументы вместо
Можно сэкономить несколько байт указав лишний аргумент в функции, вместо объявления переменной с помощью
Поскольку оператор присваивания возвращает присваиваемое значение, можно использовать присваивание и проверку одновременно:
Массив можно использовать как временное хранилище, чтобы не объявлять лишнюю переменную.
Приведение типов в JS работает весьма странно и является одним из самых распространенных источником багов. Тем не менее, его можно использовать разными интересными способами для уменьшения размеров кода.
Наример, в реализации pubsub Джед Шмидт декрементировал переменную с отрицательным числом, а затем прибавлял ее к строке, получая что-то вида
После этого в другом месте использовал
Зачастую можно реализовать всю логику внутри условий и сэкономить на теле цикла.
Хороший пример этого подхода можно посмотреть в функции timeAgo.
Если у вас есть массив, все члены которого заведомо приводятся к true, можно использовать более короткую запись цикла:
Используйте
Эти знания могут помочь неплохо сэкономить на скобках.
Начать можно с изучения этой статьи на сайте Mozilla.
Используйте
Используйте более короткие способы записи
Вместо
Есть варианты
Иногда пробелы после операторов можно безболезненно удалить.
Используйте
Используйте
Это короче. Кроме того, делить на нуль всегда весело.
Вместо сравнивания чисел иногда короче свести значение к нулю и проверить его истинность.
Используйте
В сочетании с унарным минусом это дает возможность, например, инкрементировать любую, даже еще не определенную переменную.
Можно сэкономить два байта при разбиении строк методом
Используйте браузерный метод
Строки в браузерах имеют не очень известный метод
Используйте методы
Эти методы позволяют передавать функцию в качестве второго аргумента. Этим можно воспользоваться для удобной итерации по строке.
Примеры использования: templates и UUID.
Используйте
Можно использовать
Используйте
Зачастую это получается короче, поскольку позволяет протаскивать значения через стек, без лишних переменных.
В качестве примера функция walk.
Если надо хранить состояние между вызовами функции, функцию можно использовать как объект и хранить данные в ее свойствах:
Некоторым конструкторам вовсе не обязательно ключевое слово
Когда надо вернуть что-то отличное от переменной, ставить пробел после
Пока все.
Вообще, рекомендую ознакомиться с плодами их творчества. В попытках уместить сложные вещи в 140 байт ребята иногда просто творят чудеса. Я думаю, даже опытный программист найдет для себя что-то новое и интересное в их коде.
Естественно, в ход идут все самые изощренные способы и техники уменьшения размера исходника. У них есть вики-страничка с советами, которую я и решил перевести.
Сразу оговорюсь, что читаемость обработанного таким образом кода стремится к нулю, так что использовать эти трюки стоит только в случаях, когда размер действительно превыше всего. Например, при участии в конкурсе 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 байт ребята иногда просто творят чудеса. Я думаю, даже опытный программист найдет для себя что-то новое и интересное в их коде.