Beside functions being first class citizens in JavaScript, there are plenty of other features, allowing functions to make an extra mile ride. Closures are one of them.
What is a Closure?
A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment)
Mozilla
Let's take an example:
function adder(a) {
return function(b) {
return a + b;
};
}
let add5 = adder(5);
let add10 = adder(10);
console.log(add5(2)); // 7
console.log(add10(2)); // 12
From above code, it's clear how closures work, and how it remembers the lexical scope, the function was declared within. But how this could be useful, or is it just non-practical questions in JavaScript interviews?
Don't worry, we have got plenty of applications and usage, of Closures throughout JavaScript ecosystem. Today, the usage we want to discuss is how we can optimize memory usage using Closures. Consider the code below:
function multiply(y){
let x = Math.pow(10,10);
return x* y;
}
multiply(25); //250000000000
multiply(45); //450000000000
It looks very straight forward, right? ... No, actually if you notice every time the multiply() function is called, the let x = Math.pow(10,10) is recreated and occupy certain memory, in this case quite a large memory for sure, due to the large numeric value it's generating.
Bringing in the Closure
What if we make it possible to create let x = Math.pow(10,10); only once, to the extent where it has been repetitive across the multiply() function calls, this is where the Closures come into play. Let's take a look at the below modified code:
function multiply(){
let x = Math.pow(10,10);
return function(y){
return x* y;
}
}
let multiplier= multiply();
multiplier(25); //250000000000
multiplier(45); //450000000000
Here we introduced returning an inner function, which creates a closure, and does not recreate let x = Math.pow(10,10); with each multiplier() call, and hence avoid excess memory leaks. This conveys us, by using Closures we can easily avoid costly memory jobs.
So that's it for today. Hope you have benefited from this case study, or Do share if you ever faced similar scenarios and what approaches you have followed. Stay tuned for the next JS hack!😃
Top comments (25)
Honestly, it doesn't really matter. He chose to use normal functions instead of arrow functions and there is nothing wrong with it. I'd argue that if I was a beginner, I wouldn't understand your code, so I actually prefer his way of doing things.
No need to be fancy as this website is targeted towards beginners. Although, I agree, a lot of actual senior and experienced developers are also choosing this platform but they don't really care about reading such articles. I hope you got my point.
Thanks.
I get the idea, but this uses memory, not saves it. The closure is taking up memory by its definition - the thing you are saving is the processing required for Math.pow() (or **).
Now in the vanilla version the value of Math.pow(10,10) is being stored on the stack - this memory is only in use during the execution of the function and will be immediately recycled as soon as it is exited, no need for garbage collection as the result is just a number.
@miketalbot , I agree with you to the point that it saves the processing time, but without closures, everytime we run multiply(), the line 2, let x = Math.pow(10,10); recreates in memory, where as with closures, line 2 does not recreates in memory + it saves the processing time for large jobs (as you have said above).
But Mike is right here - in terms of net memory usage, there is no difference.
The difference in terms of memory here, is not the amount of memory used, but when that memory is used and gets released. The other difference is in terms of thrashing the garbage collector - by using a closure, you avoid unnecessary thrashing, but strictly speaking, you do so by keeping this memory allocated and never releasing it.
In that sense, your optimization could be considered "worse" in terms of overall memory usage. Although it's definitely much better in terms of performance overall, the explanation for that is not that you're saving memory, which you're really not - on the contrary.
It's a good optimization, but not for the reasons you explained.
Also, with regards to this part of your explanation:
It's a good guess, but that is not how numbers are stored in JavaScript - the number type is a 64-bit floating point, no matter which number.
You can learn about number storage here:
2ality.com/2012/04/number-encoding...
But to begin with, you really shouldn't spend your time "optimizing" something this small. You should trust that the language does what's best. If you raise a value to a variable in a parent scope, it should be because the code makes more sense that way - the person reading the code will understand that this value doesn't change. Or because the value is expensive to calculate.
Speculating about saving 8 bytes of memory is not a good use of your time, unless you expect to have millions of instances - and even then, you would need to weigh the fact that those millions of instances can't be deallocated when they're not in use, against the performance overhead of calculating the value on demand.
If you do have a case that calls for memory optimizations, you should learn to use a profiler and get your facts from actual measurements - in your case here, you would have found that the extra closure you use for your "memory optimization" actually requires more memory, not less.
Performance, and sometimes even memory usage, is too complex in JavaScript for you to guess or assume anything - the execution model of JavaScript is extremely complex, and the measurements often not at all what you might intuitively expect.
This is what I came to the comments to add. 👍
My point is that a local primitive variable in a function is allocated on the stack and not in the main heap of memory (it's different if it is an object), this memory isn't "being used" it's just an offset from the current stack pointer.
The moment we "close over" a function then the function definition is converted internally into a class and the value of that variable becomes a property of the instance of that class. The stack is a super efficient way of handling primitive value storage during the execution of a function. If
x
was an object then it wouldn't be the same story, as the reference tox
would be stored on the stack but the contents ofx
would be allocated in memory and be subject to subsequent garbage collection, leading to additional processing requirements. In neither of these cases would the variable be "used" as in it wouldn't continue to take up required memory after the function exited, however as Javascript uses garbage collection it would need processing for the memory to be made available again.Consider this:
The example uses more memory indeed.
But the author also mentions that his example does not suffer from memory leaks, implying that the original one did. But there is no memory leak on the original code, memory leak means a piece of memory that it is unused and never garbage collected.
People should, at least, review what the terms they use mean before publishing an article.
Sure @lukeshiru , this would surely be more cleaner code 👍. However my attempt was to be as simple as possible, thats why tried to kept it with an old JS syntax 😊
I was about to comment about using
Math.pow
instead of just using**
. Really clean example 👌I think examples, no matter how trivial, should show code that people would actually write. Why would anyone use pow() (or **) to create the constant 1e10? And why would anyone put such a constant in a function scope rather than a more global scope, where it would be evaluated exactly once in the lifetime of the program (instead of every time the outer function is called)?
I find these questions so distracting, they totally obscure the point the article was trying to make, and the example is essentially an anti-pattern for the usage of closures.
Hi @pcockerell , I value your anguments, however keeping global values itself is an anti pattern. Second, using high exponential expressions was only meant to show jobs which could affect smooth performance. Although these patterns might not benefit in smaller code bases but would surely payoff in much larger applications.
I didn't say a global variable, but "a more global scope", e.g. a static class property or a module-level const in node. The problem with paring your example down to such a degree is that you haven't shown closures' real utility. I think at the very least the outer function should have taken a parameter, so you could show that the inner function is implicitly parametrized by the argument passed to the outer function (similar to the currying that others have mentioned).
Another way to achieve exactly the same effect would be to make multiply() a method of a class Multiply, which has a fixed instance variable "x" per your example, or having x initialized in the constructor in the more realistic example. Though admittedly this is less in the spirit of function-based Javascript and more in line with the later class-based syntactic sugar.
In any case, I still don't believe that an example of closures where the only captured variable is identical for all instances of the closure is a very useful one.
@lukeshiru I totally endorse this approach enabling multiply() to be pure function and fully reusable
Agreed! :)
Yes exactly.
The author obviously has no idea of what curried functions are and why they are useful. Looks like he just discovered what closures are and wanted to write an article about it, even without knowing what they are useful for or what the term memory leak actually means. The example code proposed by @luke 知る has way more value and shows a better understanding of the topic than the article itself.
Let's cut him some slack, he's newbie. Instead, why don't you read one of my post and participate in the discussion? I'll be really grateful.
dev.to/hasnaindev/what-is-your-go-...
I see. 🤦🏽♂️
@hasnaindev your point is valid too :) , its just 2 diff points what @lukeshiru has shared out above.
What do you think in this case with socket.io?
If I'm not wrong, every time a user connect to Websocket, will be create a new instance of
EventRouter
. If the user disconnect, the garbage collector will destroy that instance ofEventRouter
.This is neat. Never thought of this as a possibility before!
Thanks @omrisama
With the given example, Im pretty sure v8 would be capable of optimizing away the "Math.pow" to a constant..
Still waiting for the proof that it's more efficient. ^^ I might be wrong but I think the regular function gets garbage collected after usage unlike the scope of the closure.