Exploring JSF*ck

Joshua Hall

In 2010 there was an informal competition to create a system of writing JavaScript with the fewest amount of characters. From 18 to 8 to now 6 symbols we can, technically, do anything we can do with standard JavaScript. With just the !+[]() symbols and a little creativity we can do anything.

If you don’t believe my ridiculous claims, then check this out:

JSF*ck example

That’s about 20,000 characters generated with this translator. It’s actually much more doable when written by hand.

Why?

Let’s admit it, JavaScript is weird. "3" + 4 is 34, 5 + true is 6, and ('b' + 'a' + + 'a' + 'a').toLowerCase() outputs banana; this is utter chaos. Many consider this completely unacceptable and transition to TypeScript instead, but if you’re one of the few that refuses to use strongly typed languages, as I do, then taking advantage of its eccentricities is a good way to build an intuitive understanding of JavaScript’s type system.

JSF*ck is more commonly used to create hard to detect malware, but we’re obviously not going to cover that 🤷.

JavaScript Types

While most languages force you to explicitly declare your types, like const int num = 0;, JavaScript relies more on context. We have the standard booleans, true and false, but we also get a few also ‘truthy’ and ‘falsy’ values, which are symbols/variables that aren’t booleans but can be coerced and treated at booleans in the right context. 0, null, undefined, NaN, false, and empty strings are all falsy, while everything else is truthy by default.

We can manually coerce a type by wrapping it in Boolean().

console.log(Boolean(4)); // true
console.log(Boolean(0)); // false

console.log(Boolean('')); // false
console.log(Boolean(' ')); // true

Sadly, Boolean isn’t part of our six characters, so we’ll need a way around it. Instead we can use the not operator twice, !!, to force a coercion. Using it once forces it into a boolean of the opposite value while the second switches it back to its original boolean value.

console.log(!!4); // true
console.log(!!0); // false

console.log(!!'') // false
console.log(!!' ') // true

Similar to !, we can use a plus to coerce anything into a number or to concatenate with something else.

console.log(+!!4); // 1
console.log(+!!0); // 0

console.log(+!!'') // 0
console.log(+!!' ') // true

console.log(+!!4 + +!!4); // 2
console.log(+!!'' + !!''); // 0

Numbers

The only other characters you need at this point are the parenthesis, which are mostly just to assign an order to our expressions.

We don’t have access to strings or numbers but we can use empty brackets, since they resolve to true. With that we already have access to every number through addition.

console.log(!![]); // true 
console.log(+!![]); // 1

console.log(!![] + !![] + !![] + !![]); // 4

Or to be much less laborious we can wrap our number in brackets, or add brackets +[], to coerce them into strings. We can use their stringified versions concatenate them, since "4" + "2" is 42, and use parenthesis to segment them off into their own units. By using converting to numbers to add and converting to strings to concatenate we have every number in a much more readable way.

While it’s not ‘true’ JSF*ck to use variables, I’ll allow it since it makes hand writing so much more legible, sort of.

const four = !![] + !![] + !![] + !![];
const two = !![] + !![];

console.log(([four] + [two]) + ([four] + [two])); // "4242"
console.log(+([four] + [two]) + +([four] + [two])); // 84

// Pure JSF*ck
console.log(+([+!![] + +!![] + +!![] + +!![]] + [+!![] + +!![]]) + +([+!![] + +!![] + +!![] + +!![]] + [+!![] + +!![]])); // 84

Characters

Luckily for us, JavaScript allows us to treat strings like arrays, so we’re able to get a number by a particular letter’s index.

console.log('Hello World'[3]); // l

But we don’t have normal characters, so we have to get a bit creative. [][[]] will resolve to undefined, we can coerce it into a string with +[] then we can use it as an array to get a few characters. Likewise, we can use +[![]] to return NaN since it’s turning false as a string into a number.

const undef = [][[]];
console.log((undef + [])[3]); // e

const nan = +[![]];
console.log((nan + [])[2]); // N

// Pure JSF*ck
console.log(([][[]] + [])[+!![] + !![] + !![]]); // e
console.log((+[![]] + [])[+!![] + !![]]); // N

Our selection is still pretty slim, instead we can use this technique to get direct access to a numbers string representation, giving us every character. While you would normally access a method through dot syntax, like console.log('Hello world), you can also use the bracket syntax, console["log"]('Hello world').

The toString method, when used on numbers, can take the base for the string representation for that number, with 1-10 being numbers and 11-35 being the alphabet. Base 2 gives binary and base 36 will give us everything, so we’ll go with that.

Here I’m just using our last technique to get the characters needed for toString. Although I did need to cheat slightly and use String since getting a capital S turned out to be very difficult ¯\_(ツ)_/¯.

const f = (![] + [])[0]; // f from false
const i = ([][[]] + [])[5]; // i from undefined
const l = (![] + [])[2]; // l from false
const fill = f + i + l + l;
const t = (!![] + [])[0]; // t from true
const o = (!![] + [][fill])[10]; // o from function fill () { native code }
const s = (![] + [])[3]; // s from false
const S = (String + [])[9]; // S from function String() { native code }
const r = (!![] + [])[1]; // r from true
const n = ([][[]] + [])[6]; // n from undefined
const g = (+[] + String)[15]; // g from function String() { native code }

const zero = +![];
const one = +!![];
const two = !![] + !![];
const three = !![] + !![] + !![];
const thirtySix = (three + []) + (three + three) + [];

const toString = t + o + S + t + r + i + n + g;

console.log((10)["toString"](36)); // a
console.log((+((one + []) + (zero + [])))[toString](thirtySix)); // a

Functions

Now that you have the tools to build out any string and number you want, we can start writing functions. All we have to do is get any function off a string, we’ve already built out fill, and add our custom function onto its constructor. Adding parenthesis on the end will call it, since it’s an anonymous function.

For the sake of brevity, let’s pretend you already went through he work of collecting and concatenating everything for these strings.

[][fill]["constructor"]("alert('Hello World')")();

Conclusion

This may seem incredibly tedious and pointless, and it is, I personally find JSF*ck to be fun to play around with since it gets progressively more customized to you as you handcraft everything you need from the ground up.

There really aren’t very many reasons for learning this besides maybe bragging rights, doing all your work in it to ensure your job security for the next 20 years (don’t do this 😉), or just trying to impress the ladies. Hopefully this has helped you either have a better understanding of JavaScript’s weirdness or convinced you to finally learn TypeScript.

  Tweet It

🕵 Search Results

🔎 Searching...

Sponsored by #native_company# — Learn More
#native_title# #native_desc#
#native_cta#