Elixir vs Javascript closures

May 29, 2017

 - Tags: ,

Closures are functions which can reference variables belonging to an outer scope. They are used often in functional-style programming. Both Javascript and Elixir support them. However, the way these languages handle closures is different. Let’s delve into the details.

Elixir Closures

In Elixir closures, when a variable belonging to an outer scope is referenced inside the closure, it is bound to the initial value of the variable (i.e when the closure is defined). If the value of the outside variable changes after the closure definition, it will not affect the closure:

outside_var = 5                           //original value of outside_var is 5
lambda = fn() -> IO.puts(outside_var) end //outside_var is referenced inside the closure, but belongs to an outer scope
outside_var = 6                           //value of outside_var is changed
lambda.()                                 //still returns 5, the original value of outside_var

example taken from the “Elixir in Action” Book

Javascript Closures

In Javascript, when a variable belonging to an outer scope is referenced inside the closure, it is bound to the variable itself, not its initial value. Contrary to Elixir, if the variable changes after the closure definition, subsequent call to the closure will reflect these changes:

var outside_var = 5;                    //original value of outside_var is 5
lambda  = function() {                 
  console.log(outside_var);             //outside_var is referenced inside the closure, but belongs to an outer scope
};
outside_var = 6;                        //value of outside_var is changed
lambda();                               //returns 6, the new value of outside_var

How to mimic Elixir closures in Javascript?

In Javascript, a common interview question is to attach event handlers to each element of a collection of DOM elements, then make each event handler print the element’s index when it is clicked. Here is a naive solution:

//$ = jQuery
$('.emit-click-event', function(el, i) { //$('.emit-click-event') is a collection of DOM elements
  $(this).on('click', function(e) {      //Event handler triggered on click event
    console.log(i);                      //Print element index
  });
});

It doesn’t work because inside each event handler i is bound to the live value of i in the outer scope. When the value of i changes because we are in the next iteration of the collection, the value of i in the event handler being defined changes, but also the value of i in the event handlers that were defined before

A work around is to wrap each event handler in a self-invoking function:

$('.emit-click-event', function(el, i) {
  (function(internalI) {                    //internalI = i for each element of the collection
    $(this).on('click', function(e) {      
      console.log(internalI);              //the index printed on each iteration isn't bound to the live value of i anymore
    });
  })(i);
});

For each element of the collection, the self-invoking function will execute, take the current value of i and assign it to internalI. Unlike before, each event handler is defined in a different context. The value of internalI inside each closure is isolated from the current value of i.

Leave a Reply