Simple examples about Closure in JavaScript and Ruby

xullnn
5 min readApr 25, 2021

In this post I want to use simple code examples in both JavaScript and Ruby to show the existence of closure.

Closure is kind of an intangible concept. The definitions of it from different sources often introduce more concepts. Normally data or functions can be referenced by a given symbol(name), it’s like calling a man with his name. But what is intangible about closure is that it can not be referenced directly by a name or something, you can notice and recognize its existence but you can’t hold it by a name.

1 key elements

There’re two key elements to be noted in those examples: function and binding. Don’t worry about what is binding now, treat it just as any variable name that references “something”.

2 Code Examples in different languages

Let’s jump in some concrete examples to demonstrate the existence of closure.

  • First I’ll describe what’s the final result I want;
  • Second I’ll write tests to express the expected results, then write code to try passing the test;
  • during the process we’ll have short discussion and try to find out closure.

2.1 what I want: make a counter

I want to make a counter which is a function that will use 0 as starting point then increases by 1 each time the function is called.

  • set start point as 0
  • create the function counter
  • call counter → returns 1
  • call counter again → returns 2
  • call counter again → returns 3

2.2 Code example with Node.js + Jest:

File structure:

closure_javascript
├── counter.js
├── package.json
└── counter.test.js

package.json:

{
"scripts": {
"test": "jest"
}
}

Incorporate Jest into the project. Then run npm install .

Note: If you use other package management tool, check Jest doc to find out how to install it. And the the name of the test file should be counter.test.js in this case.

counter.test.js

const counter = require('./counter');test("each function call increases by 1", () => {
expect(counter()).toBe(1);
expect(counter()).toBe(2);
expect(counter()).toBe(3);
expect(counter()).toBe(4);
})

In our single test, we are expecting the returned value increases by 1 after each function call on counter . So we expect to get 1 after first call and get 4 after the fourth call.

counter.js

function makeCounter() {
let startingPoint = 0;
return () => { return startingPoint += 1};
};
let counter = makeCounter(); // the counter is actually created heremodule.exports = counter;

In counter.js , the counter function is not made by standard function declaration way i.e function counter() {...}; Instead it has to be made by a wrapper function makeCounter that returns a function counter.

And the local variable declared in the wrapper function is somehow “carried” by the returned function. The local variable startingPoint is bound with the returned function so that the value of the startingPoint can keep changing after the makeCounter wrapper has returned long before.

In other words, as long as we have access to the counter function made by makeCounter , we can access the startingPoint variable declared in makeCounter , but in a predefined way, because we won’t be able to alter the logic of function counter after it has been made.

This binding relationship between a function and data bound with the function during its creation(the exact creation point is when we invoke the wrapper function) is one of the key elements we mentioned — binding .

Run the test by npm run test in terminal, test result:

PASS  ./counter.test.js
✓ each function call increases by 1 (3 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.235 s
Ran all test suites.

By now we should pass the simple test.

2.3 Code example with Ruby + Minitest:

File structure:

closure_ruby
├── counter.rb
└── test_counter.rb

counter.rb

def make_counter
starting_point = 0
Proc.new { starting_point += 1 }
end

Regardless of the different syntaxes, what we do here is exactly the same as what we did when using JavaScript. The make_counter method(I think it’s ok to call it function) is a wrapper method, it returns a Proc object.

Note: A Proc object is just another form of function, the ‘function body’ is written in the code block {starting_point += 1} . And the way to invoke a Proc object is by calling call method on it — proc.call .

And also we declare a local variable starting_point inside the wrapper method, the returned proc (conceptual function) has access to that local variable. But this time we create the counter in test file, see below.

test_counter.rb

require "./counter.rb"
require "minitest/autorun"
class TestCounter < Minitest::Test
def setup
@counter = make_counter # the counter is made here
end
def test_counter
assert_equal 1, @counter.call
assert_equal 2, @counter.call
assert_equal 3, @counter.call
assert_equal 4, @counter.call
end

Here @counter is an instance variable that can be accessed by all the test methods and @counter is a proc object(conceptually a function) that can be invoked by @counter.call. Again in the test method we are expecting that the same thing to happen — every invoking causes starting_point to increase by 1 so we also get 4 after the fourth invocation.

In Ruby example, the “function” element is the proc object and the “binding” element is the local variable declared in the wrapper function. Also as long as we have access to the proc, we have access to the starting_point variable — in a predefined way.

Run the test with ruby test_counter.rb in terminal

Run options: --seed 63631# Running:.Finished in 0.000886s, 1128.6682 runs/s, 4514.6727 assertions/s.
1 runs, 4 assertions, 0 failures, 0 errors, 0 skips

In spite of the difference in syntax and terminology, we can see the manifestation of the concept of closure. A closure is just some binding/environment/data/record, whatever you call it, carried by a function, and that function is created by another wrapper function, all the variables we want to access later by the returned function can be declared in the definition body of the wrapper function.

3 Definitions

wikipedia: In programming languages, a closure, also lexical closure or function closure, is a technique for implementing lexically scoped name binding in a language with first-class functions.

mdn: A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.

O’Reilly: In Ruby, procs and lambdas are closures. The term “closure” comes from the early days of computer science; it refers to an object that is both an invocable function and a variable binding for that function. When you create a proc or a lambda, the resulting Proc object holds not just the executable block but also bindings for all the variables used by the block.

4 Summary

You can think of a function as a man, and the man’s carrying a suitcase(closure), all the things(data) in the suitcase is packaged by his mom(wrapper function), and the man is ‘created’ by his mom.

Hope these simple examples in different languages can help you better understand closure from different points of view. And it takes some time and re-acquaintances to really remember how all of this closure thing work so don’t worry if you don’t get it at the first few trials.

--

--