-
Notifications
You must be signed in to change notification settings - Fork 67
General Javascript Golfing Tricks
A reverse for loop is shorter than a forwards for loop:
for(i=0;i<10;i++)... // before
for(i=10;i--;)...
A for loop can use a ,
instead of {}
to do multiple things inside the body:
for(i=10;i--;){thing1();thing2()} // before
for(i=10;i--;)thing1(),thing2()
You can save a further char by moving the last thing (or more) inside the for:
for(i=10;i--;)thing1(),thing2() // before
for(i=10;i--;thing2())thing1()
Do you need to loop from 0 to 3 or less? You can use for ... in
for(i=0;i<4;i++)console.log(i)
// becomes
for(i in'omg!')console.log(i)
// but looping backwards (if you can) is still shorter
for(i=4;i--;)console.log(i)
If you want to loop the values of a string:
for(p of'🦡🍄🐍')console.log(p)
Instead of two nested loops, you can often use one loop, and get the original numbers using floored division and modulus:
// Before
for(i=128;i--;)for(j=32;j--;)draw(i,j)
// After
for(k=4096;i--;)draw(k/32|0,k%32)
And if the inner loop happens to be a power of two (such as 32 === 2**5
), then you can get the flooring for free by using a bitshift for division:
// After
for(k=4096;i--;)draw(k>>5,k&31)
Usually in Javascript we use ;
to separate statements. But when golfing, we can save space by using ,
or |
to combine multiple expressions into one expression.
if(x){thing1();thing2()}
if(x)thing1(),thing2()
// Unfortunately commas won't help inside arrow functions
f=(a,b)=>thing1(a,b),thing2(b,a) // will run thing2 _outside_ the function!
// But no problem. In an arrow function, we can use | or & instead of ,
f=(a,b)=>{thing1(a,b);thing2(b,a)}
f=(a,b)=>thing1(a,b)|thing2(b,a) // These both evaluate thing2()
f=(a,b)=>thing1(a,b)&thing2(b,a) // regardless of the result of thing1()
You can use just about any existing Javascript object as a dictionary if you need to, to avoid initialising your own.
onclick=e=>{a=[],a[9]=5}
onclick=e=>{e[9]=5,a=e}
A dictionary can be used as a cheap array, but it won't give you .length
or any Array methods.
Four times
x.
can be shortened usingwith(x)...
Need to call a function multiple times, but it's a member of another object?
You can't do F=x.fillRect;F();F()
because F
won't see its context object x
.
But you can do:
x.F=x.fillRect;x.F(X,Y+7,3,3);x.F(187,Y+C(X/39)*29,5,20);x.F(0,Y-S(X/29)*22,5,20)
Alternatively, or additionally, try using the with
operator.
with(x)F(),F(),F(),F()
This is an experiment with mediocre results. It's probably more suitable for js1k.
Some of the Javascript API functions have really long names. If you are using more than ~5 of these, then these functions might help. They let us search for properties using a regexp.
/** p(object, regexp) = find key in object */
p=(o,r)=>{for(k in o)if(k.match(r))return ok}
/** P(object, regexp) = get property from object */
P=(o,r)=>o[p(r)]
f=(o,r)=>p(o,r).bind(o)
If I am using the same o
repeatedly, then I might remove the o
argument from those functions, and instead set it as a global before calling p
or P
.
So now, instead of XMLHttpRequest
, we can use p(this,/XM/)
.
Some better alternatives:
If you want to call a long-named function twice:
x[f='fillRect'](... first call ...)
x[f](... second call ...)
Example: d/7330
Another trick, for really long property names, is to generate minimised names:
for(p in x)this[p[0]+p[6]]=p
This code adds two-letter string shortcuts to the global this
, for all the functions in x
:
this['fc']='fillRect' // creates new global window.fc
this['gC']='globalComposite' // created new global window.gC
That allows us to reference the real fillRect
and globalComposite
properties like this:
x[fc](...)
x[gC]='lighter'
and all the other properties of x
too! Example: d/9134
You can not do foo&&bar=1
but you can do foo?bar=1:0
Remember that |
is greedy but %
is not. Other bitwise operations are also greedy (&
, <<
, >>
, ^
).
If you need to floor and then modulus, instead of (x|0)%y
you can use ~~x%y
You can access most Unicode chars directly from a string: "abc"[k]
But that doesn't work so well with emojis, because emojis are two characters each, so you only get half the code!
However, an iterators loops a string different. So you can use an iterator to copy emojis into an array, without losing them:
[...'🐸🌷☁️️'].map(e=>console.log(e))
or
for(e of'🐸🌷☁️️')console.log(e)
If you need to call a function many times, you can override the valueOf
of an object, and trigger the interpreter to call it without having to use any ()
s in your own code. (demo, slide)
// Before
r=Math.random;x.arc(r(),r(),r(),r(),r(),r())
// After
T.valueOf=Math.random;x.arc(T,T,T,T,T,T)
Explanation: When an object is used in an algebraic expression, before coercion its valueOf
function is called by the interpreter.