Free email newsletter: “ES.next News

2012-09-24

A quine in JavaScript

Quoting Wikipedia:
A quine is a computer program which takes no input and produces a copy of its own source code as its only output.
@cowboy (Ben Alman) gives the following example for JavaScript:
    !function $(){console.log('!'+$+'()')}()
Why does the quine work? The above code uses several tricks.

Getting the source code

By wrapping the program in a function, we have an easy way to access its source code: In most JavaScript engines, converting a function to string returns its source.
    > function foo() { return "abc" }

    > String(foo)
    'function foo() { return "abc" }'

A function expression that refers to itself

A first version of our quine would look like this:
    function $() { console.log(String($)) }
That is a declaration for a function whose name is $ (a legal identifier in JavaScript). For reasons that will be explained later, we need the above to be a function expression. The following code works:
    var prog = function $() { console.log(String($)) };
After the assignment, there is a named function expression. Its name $ only exists inside the function and allows it to refer to itself. Calling prog gets us pretty close to a Quine:
    > prog()
    function $() { console.log(String($)) }
    undefined
After calling the function, you first see its output and then its result, undefined.

An Immediately Invoked Function Expression (IIFE)

Now we have (a close approximation of) the program in a function. But we don’t want to delay its execution, we want to execute it right away. JavaScript allows you to create a function via an expression and to call that function right away. Doing both things at the same time is called an Immediately Invoked Function Expression (IIFE, pronounced “iffy”). Incidentally, that term has also been invented by Alman. The program as an IIFE looks like this:
    > !function $() { console.log(String($)) }()
    function $() { console.log(String($)) }
    true
The unary not operator (!) tells JavaScript to parse the function as an expression. Otherwise, we wouldn’t be able to immediately-invoke [1]. true is the result of !undefined.

Adding the missing characters

We are still missing a few characters: The exclamation mark before the function expression and the parentheses after it. If we also log these, we are done:
    > !function $() { console.log('!'+$+'()') }()
    !function $() { console.log('!'+$+'()') }()
    true
Note that we solved a conundrum: Every character that we add to console.log() becomes part of the program. Which means that we need to log it, too. But if we do so explicitly, the additional characters also become part of the program, need to be logged, etc. Thankfully, referring to the source (via $) allows us to make the repetition implicitly.

Another way of writing the IIFE

You could also wrap the IIFE in parentheses:
    (function $(){console.log('('+$+'())')}())
As every character before and after the function expression shows up twice in the code, the exclamation mark saves you a total of two characters.

Reference

  1. Expressions versus statements in JavaScript

No comments: