Skip to end of metadata
Go to start of metadata

Problem 1: ClojureScript functions as object methods

In any programming language with both first class functions and objects, the handling of a this parameter is a cause for complication. In the case of JavaScript the picture is particularly sticky due to langauge foibles. An additional complication, ClojureScript does not have its own notion of this independent to its JavaScript interop. Currently, there is no good way to refer to a JavaScript this aside from the (js* "this") hack. ClojureScript should more effectively handle this in a way that is safe and intuitive.

A bit about this in general

ClojureScript functions are compiled to JavaScript functions and are therefore subject to the implicit this behavior.

Given a function f defined as:

    function f(a,b) {
      print([this,a,b])
    }

Calling f directly yields:

    f(1,2)
    // [object global],1,2

note: in JavaScript strict mode this would be bound to undefined in the previous example

In this case, the this variable is implicitly bound to the global object.

However, when bound as a property on an object, f's implicit this changes:

    var o = {}
    o.m = f
    o.m(1,2)
    // [object Object],1,2

Now the value of this is the instance o.

ClojureScript and this

The problem in ClojureScript can be illustrated in the following:

    (defn f [a b] (println [(js* "this") a b])) ;;roughly equivalent to the f function above

    (def o (js* "{}"))
    (set! cljs.user.o.m f)
    ((.m o) 1 2)
    ;; [#<[object Global]> 1 2]

That is, the value for this in the body of f is bound to the global object and not the instance o.

A possible solution for ClojureScript functions as methods

One possible solution to this dilemma is to use GClosure's bind function:

    (set! cljs.user.o.m (goog.bind f o))
    ((.m o) 1 2)
    ;; [#<[object Object]> 1 2]

This will also work for multi-arity functions.

A possible syntax

    (extend-object o
      {:m f})

Would expand into a number of calls to goog.bind on the object o and then return o itself. The use of the (js* "this") could possibly be replaced with the js namespaced reference js/this.

Caveat (or bug, depending on how you look at it)

Any function attached to an object via extend-object will forever thereafter refer to said object as its this.

Problem 2: ClojureScript functions and co-opted this

Some JavaScript libraries use this as a dynamic variable bound to some special value within special contexts. For example, JQuery uses this approach:

    $('li').each(function(index) {
       alert(index + ': ' + $(this).text());
    });

In this example, this would be bound to each element in turn retrieved by the selector $('li') and processed in the each loop. techniques of this sort pull this off by explicitly passing a value for the implicit this to the target function as an argument to the call method. In other words, something (in spirit) like:

    (defn f [] (.x js/this))
    (f)
    ;=> undefined

    (.call f (js* "{x : 9}"))
    ;=> 9

This is effectively what goog.bind does, so this type of behavior should just work using ClojureScript functions.

  1. Sep 07, 2011

    In your example code, I believe ((.m o) 1 2) should be changed to (. o (m 1 2)). The former takes the value of the property m of o, a function, then calls it (akin to the js: var myfn = o.m; myfn(1 2); which will have this bound to the global object). The latter calls m as a method and works as intended.

    Some discussion on this from Rich here

    Doesn't solve that there is not a good way to refer to this.

    edit: since it's not a 0-arity fn, it could be written with the more normal syntax of (.m o 1 2), guess I didn't have enough coffee before posting