As a first post, I want to explain a feature in my style of programming that leaves a few people puzzled when reading my code. In JavaScript, the canonical OOP-like way to define objects is

function Class(args) {
    this.prop = args.prop
    this.method = function () { ... }
}

which defines a constructor for the “class” Class and can be invoked with

new Class(args)

This is of course a rather shallow syntactic sugar for

{
    prop: args.prop,
    method: function () { ... }
}

A downside of this approach is that this loses its context when a method is passed in a different environment:

function Obj() {
    this.value = 1
    this.method = function () {
        console.log(this.value)
    }
}

function invokeCallback(callback) {
    callback()
}

let obj = new Obj()
obj.method() // prints 1
invokeCallback(obj.method) // prints undefined

The reason is that this is not lexically but dynamically scoped, and bound by the caller upon invoking a function. This means, in the above example, this gets bound to the context where the function call happens. Running the snippet in a browser (preferably at the top level) and redefining the method as console.log(this) should reveal this. Additionally, when the function body is in strict mode, no default binding takes place. There are also many important but subtle details with implicit binding that I am not going to cover here.

To avoid this, we need a way to bind the context of every method to a lexical environment. To do so, we need closures.

Closures

A closure is a function bound to an environment. Environments are the set of free variables accessible by a function body. Simplifying, there here are two kinds of closures:

  • dynamic closures, where free variables are bound to the context of the caller
  • lexical closures, where free variables are bound to the lexical environment where the function is defined

In reality, JavaScript closures are a mix of both. this is a dynamic variable, while regular variables are bound to the lexical environment. What we need, though, does not require dynamic variables at all. Lexical closures are as powerful as fully dynamic scopes, without their pitfalls, and with improved performance as the compiler can compute the location of a value referenced by a variable at compile-time, instead of performing expensive lookup through the nested environments during runtime.

Most often, the term “closure” implicitly refers to lexical closures, so I will use this convention for the rest of the article.

A particular programming style whose foundation is based on closures–functional programming–takes great advantage of these features. The only requirement for this style of programming is support for lexical closures as values. In JavaScript, we can define and pass closures as any other value, and even attach properties to them:

function fn() {
    var x = 5
    return function () {
        console.log(x)
    }
}
fn()

this shoud print 5. We can return closures that keep valid references to the free variables they refer to, regardless of where the function is called.

We can also write factory-style closure constructors:

function printerFactory(valueToPrint) {
    return function () {
        console.log(valueToPrint)
    }
}

var print5 = printerFactory(5)
print5() // prints 5

printerFactory(6)() // prints 6
print5() // still prints 5

Closures can be created anytime with completely different environments. We can return them as values, assign them to variables, and do anything we do with other JS values.

This style of programming is essential in JavaScript, as most of the control flow is driven by events, which are asynchronous in nature, and closures are the most natural way to structure this kind of programs. In fact, closures alone are powerful enough that any arbitrary program can be written using only closure creation and calling [1].

Objects

Objects in class-based OOP are commonly defined as being instances of a class, that incapsulate functionality and abstract implementation. This is achieved in the following way:

  • classes have an internal structure which is not accessible directly by outside code;
  • classes define an interface through which outside code must interact (methods);
  • instances are newly created from the class definition, and only passed some values for initialization.

Code interacting with an instance of a class does not need to know how the functionality of a class is implemented. Only methods are allowed to interact with its internal structure; thus, methods have access to member variables as if they were defined as closures in the context of the class.

Objects as closures

We can use objects to rewrite the last code snippet:

function Printer(valueToPrint) {
    this.value = valueToPrint
    this.print = function () {
        console.log(this.value)
    }
}

var print5 = new Printer(5)
print5.print() // prints 5

new Printer(6).print() // prints 6
print5.print() // still prints 5

So far so good. There is only one issue though: as I said early in the article, we cannot pass the print method to other functions without losing this. We can wrap the method using bind:

func(print5.print.bind(print5))

which returns a closure wrapping our method in the context of the object. While this solves our problem, we would like to pass methods without additional wrapping. Furthermore, when we pass a method as callback to a function outside our control (e.g. library or builtin function), there is no guarantee that the context will be preserved at every call site.

Closures are objects

To solve this, we must understand that objects are nothing more than syntactical sugar over plain closures. We don’t need this to write fully expressive code.

Using closures, we can implement every feature of class-based OOP without the pitfalls of dynamic scoping imposed by this semantics.

First, to encapsulate state we can wrap it in a closure

function (initValue) {
    var state = initValue
}

then, we want to implement class-like methods, our interface to the local state:

function (initValue) {
    var state = initValue
    function method(x) {
        state = x
    }
}

note that we don’t use this, as state is in scope we can reference to it by name.

Finally, we return an interface that outside code will use to interact with local state:

function makeInstance(initValue) {
    var state = initValue
    function setValue(x) {
        state = x
    }
    function getValue() {
        return state
    }
    return {
        set: setValue,
        get: getValue
    }
}

What we use to represent the interface does not matter: we could also use arrays, a function with switch, or just return the method if there’s only one.

Now we can construct our object like this:

var obj = makeInstance(4)
obj.set(6)
console.log(obj.get()) // prints 6

There is also another benefit with this approach: private object properties are hidden and cannot be accessed by outside code and methods are exported explicitly, enforcing encapsulation in a stronger way than regular OO JavaScript.

Reconsidering ‘new’

Since this is a dynamically scoped variable, it is trivial to emulate the new operator in terms of closures:

function nw(constructor) {
    var obj = {}
    constructor.call(obj)
    return obj
}

nw(function () {
    this.a = 5
}) // returns { a: 5 }

We see that new is nothing more than a decorator over constructors, and that constructors are nothing more than plain functions.

Further reading

    The venerable master Qc Na was walking with his student, Anton.  Hoping to
    prompt the master into a discussion, Anton said "Master, I have heard that
    objects are a very good thing - is this true?"  Qc Na looked pityingly at
    his student and replied, "Foolish pupil - objects are merely a poor man's
    closures."

      Chastised, Anton took his leave from his master and returned to his cell,
    intent on studying closures.  He carefully read the entire "Lambda: The
    Ultimate..." series of papers and its cousins, and implemented a small
    Scheme interpreter with a closure-based object system.  He learned much, and
    looked forward to informing his master of his progress.

      On his next walk with Qc Na, Anton attempted to impress his master by
    saying "Master, I have diligently studied the matter, and now understand
    that objects are truly a poor man's closures."  Qc Na responded by hitting
    Anton with his stick, saying "When will you learn? Closures are a poor man's
    object."  At that moment, Anton became enlightened.

References

For questions, comments, and corrections contact me on Telegram
Last modified: 2017-12-28 14:44:28 +0000