JS Code Golf - Fun One-Liners and Hacks

In this blog post, I'm going to share some techniques I've picked up from working on JavaScript projects where code size really matters. If you've ever participated in the JS13k competition (where you need to build a complete game in under 13 kilobytes) or worked on highly optimized frontend applications, these tricks might come in handy. Some are common sense optimizations; others might leave you scratching your head a bit!

What is code golf?

For those unfamiliar with the term, code golf is the practice of solving a programming problem using the fewest characters possible. It's like regular golf - the fewer strokes (or in this case, keystrokes), the better. While it's rarely a good approach for production code, it can be a fun challenge that sometimes teaches you clever language hacks.

Let jump right in!

Variables and Declarations

Minimizing variable declarations

Group your variable declarations to save bytes:

// Instead of:
var a = 1;
var b = 2;
var c = 3;

// Use:
var a = 1, b = 2, c = 3;

Caching frequently accessed properties

When you need to access properties multiple times, especially those with longer names or through object chains, caching them can save bytes:

// Instead of repeatedly accessing:
object.some.deep.property.method();
object.some.deep.property.method();
object.some.deep.property.value += 10;

// Cache the reference:
var p = object.some.deep.property;
p.method();
p.method();
p.value += 10;

This technique is visible in my Tiny Dungeon code where I cached references to frequently accessed objects:

var w = v.getContext("2d"); // Cache canvas context

Optimizing function calls

When dealing with functions with long names or in deeply nested objects, there are tricks to reduce repetition:

// Instead of:
Math.min(a, b);
Math.max(c, d);
Math.round(e);

// You can use:
var M = Math;
M.min(a, b);
M.max(c, d);
M.round(e);

Number Operations and Conversions

Converting to numbers quickly

Here's a neat little trick for converting a string to a number without using parseInt():

var x = "42";
var y = +x; // y is now the number 42

The unary plus operator (+) converts its operand to a number. It's shorter than parseInt() or Number() and works just as well for simple conversions.

Quick integer truncation

Instead of using Math.floor() for positive numbers, you can use the bitwise OR operator:

var x = 3.7;
var y = x | 0; // y is now 3

This technique, often called "bitwise truncation," forces the value to be converted to a 32-bit integer, effectively removing the decimal part. It's equivalent to Math.trunc() for positive numbers, but much shorter to type.

Generating random integers

Need a random integer between min and max? Here's a shorter version than using Math.floor(Math.random() * (max - min + 1)) + min:

function rand(min, max) {
  return Math.random() * (max - min + 1) | 0 + min;
}

The bitwise OR (|0) works similarly to Math.floor() for positive numbers but is more concise. In my Tiny Dungeon code, I used this pattern in the random number generator:

r: function(a, b) {
  return Math.random() * (b - a + 1) | 0 + a;
}

Numeric conversions and comparisons

JavaScript offers several shortcuts for number conversions:

// Convert to integer (alternative to parseInt):
var n = ~~"42";    // Bitwise NOT twice
var n = "42" >> 0; // Right shift by 0
var n = "42" >>> 0; // Zero-fill right shift (for positive numbers only)
var n = "42" | 0;   // Bitwise OR with 0

// Convert hex string to number:
var n = "0xFF" - 0;  // Subtracting 0 forces numeric conversion

// Compare with 0:
if (n > 0) // Normal comparison
if (n | 0) // Truthy check combined with conversion (won't work for n=0)

Tricks with Numerical Operations

There are several shortcuts for common numerical operations:

// Instead of:
var x = Math.floor(y / 2);

// You can use:
var x = y >> 1; // Divide by 2 and floor

// Instead of:
var isEven = (x % 2 === 0);

// You can use:
var isEven = !(x & 1); // Checks if the last bit is 0

// Instead of:
var x = Math.pow(2, n);

// You can use:
var x = 1 << n; // Powers of 2 using left shift

Strings and Arrays

String and array tricks

JavaScript has several built-in features for string and array manipulation that can be used for code golf:

// Instead of repeating a string with a loop:
var str = "";
for(var i = 0; i < 5; i++) str += "a";

// You can join an array of empty elements:
Array(6).join("a"); // "aaaaa" (one less than the size)

Array and string indexing shortcuts

JavaScript's array and string indexing provides compact ways to access characters:

// Instead of:
var first = str.charAt(0);
var last = str.charAt(str.length - 1);

// Use array-like indexing:
var first = str[0];
var last = str[str.length - 1];

Array method shortcuts

Array methods can sometimes offer shorter alternatives:

// Instead of:
var min = Math.min(a, b, c);
var max = Math.max(a, b, c);

// You can use apply with arrays:
var min = Math.min.apply(Math, [a, b, c]);
var max = Math.max.apply(Math, [a, b, c]);

Control Flow and Conditionals

Boolean coercion shortcuts

JavaScript's type coercion can be leveraged for some neat shortcuts:

// Instead of strict equality checking:
if (x === undefined || x === null) {
  // do something
}

// You can test if a condition is "truthy":
if (x == null) {
  // do something
}

The == operator will return true if x is either null or undefined, saving you some characters.

Using loose equality for conditionals

JavaScript's loose equality can sometimes be used to create very compact conditionals:

// Instead of:
if (value === 0 || value === null || value === '' || value === false) {
  // do something
}

// You can use:
if (!value) {
  // do something
}

Just be careful with this approach, as it can lead to unexpected behavior with values like 0 and ''.

Using Short-circuit evaluation

Short-circuit evaluation can replace simple if statements:

// Instead of:
if (condition) {
  doSomething();
}

// You can use:
condition && doSomething();

Similarly, for default values:

// Instead of:
if (value === undefined) {
  value = defaultValue;
}

// You can use:
value = value || defaultValue;

Ternary operator chains

For multiple conditions, chains of ternary operators can be more compact than if-else statements:

// Instead of:
var result;
if (condition1) {
  result = value1;
} else if (condition2) {
  result = value2;
} else {
  result = value3;
}

// You can use:
var result = condition1 ? value1 : condition2 ? value2 : value3;

Clever uses of switch statements

Switch statements can sometimes be more compact than if-else chains, especially when dealing with multiple discrete values:

// Instead of:
if (type === 'A') {
  return valueA;
} else if (type === 'B') {
  return valueB;
} else if (type === 'C') {
  return valueC;
} else {
  return defaultValue;
}

// You can use:
switch(type) {
  case 'A': return valueA;
  case 'B': return valueB;
  case 'C': return valueC;
  default: return defaultValue;
}

Reusing calculation side effects

Sometimes you can combine calculations with conditionals:

// Instead of:
var len = array.length;
if (len > 0) {
  // do something with non-empty array
}

// You can use:
if (array.length && /* do something */) {
  // non-empty array condition is truthy
}

This technique is commonly used in Tiny Dungeon for checking game state while simultaneously accessing values.

Function and Scope Tricks

Function expression shortcuts

When creating small utility functions, you can sometimes save space with clever tricks:

// Instead of:
function add(a, b) {
  return a + b;
}

// You can use:
var add = function(a, b) { return a + b; };

And for really short functions, you can omit braces and returns:

var square = function(x) { return x * x; };
// Can become:
var square = function(x) { return x * x }

In JavaScript, semicolons are automatically inserted in many cases, so for code golf you can often omit them (though I don't recommend this for production code).

Function argument tricks

The arguments object offers some interesting opportunities for code golf:

// Instead of:
function sum(a, b, c) {
  return a + b + c;
}

// You can handle any number of arguments:
function sum() {
  var t = 0, i = arguments.length;
  while(i--) t += arguments[i];
  return t;
}

Avoiding unnecessary function wrappers

When setting up event callbacks or timers, you can often avoid creating additional function wrappers:

// Instead of:
setInterval(function() { 
  doSomething(); 
}, 1000);

// If doSomething doesn't need parameters, you can use:
setInterval(doSomething, 1000);

Self-invoking functions for scope isolation

Self-invoking functions (also called Immediately Invoked Function Expressions or IIFEs) can be used to create private scopes and avoid polluting the global namespace:

// Instead of creating global variables:
var game = {};
game.init = function() { /* ... */ };
game.start = function() { /* ... */ };
game.init();

// You can contain everything in a self-invoking function:
(function(){
  var privateVar = 'hidden';
  
  function init() { /* ... */ }
  function start() { /* ... */ }
  
  init();
})();

This pattern appears in my Tiny Dungeon code to wrap the entire game, preventing namespace pollution and potentially reducing variable name lengths inside the private scope.

Clever scope tricks

JavaScript's scope and variable hoisting can be leveraged for smaller code:

// Instead of:
function getCount() {
  var count = 0;
  return function() {
    return ++count;
  };
}
var counter = getCount();

// You can use:
var counter = (function(i) {
  return function() { return ++i; };
})(0);

Advanced Tricks

Using Comma Operators

The comma operator evaluates multiple expressions and returns the value of the last one. This can be useful for packing multiple operations in a single statement:

// Instead of:
x = 10;
y = 20;
z = x + y;

// You can use:
z = (x = 10, y = 20, x + y);

I used this technique extensively in Tiny Dungeon for compact initialization code.

Chaining function calls instead of variables

Instead of storing intermediate results in variables, chain function calls when possible:

// Instead of:
var data = getData();
var processed = processData(data);
return finalizeData(processed);

// Chain the calls:
return finalizeData(processData(getData()));

Reusing object keys and properties

When working with objects, if you have repeated keys, you can use variables to define them once:

// Instead of:
var obj = {
  longPropertyName1: value1,
  longPropertyName2: value2,
  longPropertyName3: value3
};

// Define the key once and reuse it:
var k = 'longPropertyName';
var obj = {};
obj[k + '1'] = value1;
obj[k + '2'] = value2;
obj[k + '3'] = value3;

In Tiny Dungeon, this technique helped reduce repeated strings.

Optimizing loops

When iterating through arrays or objects, choose the most concise loop type for your needs:

// For loops with a cached length are compact and efficient
for(var i=0, n=array.length; i<n; i++) {
  // Using a cached length value is both faster and can be more concise
}

In some cases, a reverse loop can be even more compact:

for(var i=array.length; i--;) {
  // The loop runs from (length-1) down to 0
}

Profile your code

Once you've finished your compact code, you need to profile it. It's important to benchmark your code on all of the browsers you're targeting. Modern browsers compile and optimize your code, but the different JS engines do it in slightly different ways, so the function that runs lightning fast in one browser may perform sluggishly on another.

When to use (and not use) these techniques

The techniques I've shared are primarily useful in specific contexts like:

  1. JS13k and similar competitions where every byte counts
  2. Environments with extreme size constraints
  3. Learning exercises to understand JavaScript better

For day-to-day code, prioritize:

  • Readability
  • Maintainability
  • Proper error handling
  • Good documentation

Remember that minifiers will often optimize many of these patterns automatically, so focusing on writing clear, maintainable code is usually the better approach for production work.

Final thoughts

Code golf is a fun exercise that can push your understanding of JavaScript to new levels. The tricks and techniques you learn might occasionally come in handy in your regular coding, but more importantly, they deepen your knowledge of how the language works under the hood.

If you're interested in diving deeper, I recommend looking at actual JS13k game entries to see how developers push the limits of what's possible in just a few kilobytes. You'll find some incredibly creative optimization techniques that go far beyond what I've covered here.

If you spot any errors or have your own favorite code golf tricks to share, please leave a comment below :)