Closures (or arrow functions, aka lambdas) don't cause memory leaks
Can someone to confirm or infirm if the local variables (here
el) can't be cleared by the garbage collector? Or, are modern browsers capable to detect they are unused in the closure?
Yes, modern JavaScript engines are able to detect variables from parent scopes that are visible from a closure but unused. I found a way to prove that.
Step 1: the closure uses a variable of 10 MB
I used this code in Chromium:
class Abc {
constructor() {
let arr = new Uint8Array(1024*1024*10) // 10 MB
let el = document.getElementById("my-btn")
if (el)
el.addEventListener("click", ev => this.onClick(ev, arr))
}
onClick(ev) {
console.log("Clicked!", ev.target)
}
}
new Abc()
Notice the variable arr of type Uint8Array. It is a typed array with a size of 10 megabytes. In this first version, the variable arr is used in the closure.
Then, in the developer tools of Chromium, tab "Profiles", I take a Heap Snapshot:

After ordering by decreasing size, the first row is: "system / JSArrayBufferData" with a size of 10 MB. It is our variable arr.
Step 2: the variable of 10 MB is visible but unused in the closure
Now I just remove the arr parameter in this line of code:
el.addEventListener("click", ev => this.onClick(ev))
Then, a second snapshot:

The first row has vanished.
This experience confirms that the garbage collector is capable to clean variables from parent scopes that are visible but unused in active closures.
About Function.prototype.bind
I quote the Google JavaScript Style Guide, section on arrow functions:
Never call
f.bind(this)orgoog.bind(f, this)(and avoid writingconst self = this). All of these can be expressed more clearly and less error-prone with an arrow function. This is particularly useful for callbacks, which sometimes pass unexpected additional arguments.
Google clearly recommends to use lambdas rather than Function.prototype.bind.
Related:
- Why is bind slower than a closure?
- A benchmark
- Arrow functions vs. bind() from Dr. Axel Rauschmayer
Answer from Paleo on Stack Overflow
Is there a difference between arrow function in javascript and lambda functions in other languages like python?
design patterns - Why does JavaScript code usually use lambdas instead of named functions? - Stack Overflow
functional programming - What is the difference between a function and a lambda? - Software Engineering Stack Exchange
javascript - Does use of anonymous functions affect performance? - Stack Overflow
Why is a Python Lambda wildly slower than a Javascript Lambda with the same functionality? - Stack Overflow
Why does JavaScript use so many lambda functions?
Why use the lambda function?
What does the => mean in JavaScript?
Videos
Wikipedia says "In computer programming, an anonymous function (function literal, lambda abstraction, lambda function, lambda expression or block) is a function definition that is not bound to an identifier."
This makes things relatively easy. For your purposes, "lambda function" and "anonymous function" are effectively synonymous. Therefore, everything you express via the => syntax is a lambda function/anonymous function, and everything you define with the function syntax isn't.
A callback is simply code that is passed to other code to be called at some later time. As you've seen, you can use both named and unnamed functions as callbacks.
The important thing to remember is that "callback" is a role that a function takes on in a specific context. It's entirely possible to call a function as a normal function and also use it as a callback elsewhere.
Callback
A Callback function is any function passed as a parameter to another function to be executed when some condition occurs. In your example, when the Promise returned by fetch is fulfilled.
A callback may be anonymous or named, or defined using function or () => {}.
That is, I would define a callback as 'a function that is called else where in your application'. By that definition, the lambda functions passed into an Array.prototype method are callback functions.
Yup! The first parameter to Array.prototype.forEach is even named callbackFn.
Anonymous vs lambda
In software engineering in general, a lambda function and an anonymous function are the same thing. Here is the definition of anonymous function from the C2 wiki.
In a programming language, an unnamed function object (also: "function literal").
Example (in PseudoCode): "lambda(x,y){ x>y }" is an anonymous function object representing the function that tells whether its first argument is greater than its second argument.
A lambda function is understood to be the same thing because of lambda calculus, which involves anonymous functions, and because the lambda keyword is often used in specific language constructs implementing support for anonymous functions.
When we drill down a bit into Javascript specifically, there are two language constructs that implement anonymous functions.
The first one is an anonymous function expression
function() { console.log("Doing stuff") }
The second is an arrow function expression
() => console.log("Doing stuff")
While these provide language support for making anonymous functions, you can still assign names to the result.
const myFunction = function() { console.log("Doing stuff") }
In other languages, such as Java and C#, lambda function refers to a syntax similar to arrow functions. While Javascript doesn't really have a language construct with that name, arrow functions would probably spring to mind for many people because of the similarity.
In conclusion, anonymous functions and lambda functions can be said to be the same thing from a software engineering perspective, but they can also refer to specific language constructs which are not equivalent.
The code below
function printCurrentValue(value) {
console.log("the value is: " + value)
}
Is then not an anonymous function, nor a lambda function. But if it had been
const printCurrentValue = function(value) {
console.log("the value is: " + value)
}
Then it's still not an anonymous function, but you could say it's defined using an anonymous function expression.
As for
fetch('/user')
.then((res) => res.json())
.then((json) => console.log(json));
(res) => res.json() is
- Anonymous
- A callback
- An arrow function
And you could say it's a lambda function, both referring to it being anonymous and referring to it being an arrow function.
The word "lambda" or "lambda expressions" most often refers to anonymous functions. So in that sense a lambda is a kind of function, but not every function is a lambda (i.e. named functions aren't usually referred to as lambdas). Depending on the language, anonymous functions are often implemented differently than named functions (particularly in languages where anonymous functions are closures and named functions are not), so referring to them with different terms can make sense.
The difference between scheme's lambda keyword and Javascript's function keyword is that the latter can be used to create both anonymous functions and named functions while the former only creates anonymous functions (and you'd use define to create named functions).
The lambda calculus is a minimal programming language/mathematical model of computation, which uses functions as its only "data structure". In the lamdba calculus the lambda-symbol is used to create (anonymous) functions. This is where the usage of the term "lambda" in other languages comes from.
A lambda is simply an anonymous function - a function with no name.
The performance problem here is the cost of creating a new function object at each iteration of the loop and not the fact that you use an anonymous function:
for (var i = 0; i < 1000; ++i) {
myObjects[i].onMyEvent = function() {
// do something
};
}
You are creating a thousand distinct function objects even though they have the same body of code and no binding to the lexical scope (closure). The following seems faster, on the other hand, because it simply assigns the same function reference to the array elements throughout the loop:
function myEventHandler() {
// do something
}
for (var i = 0; i < 1000; ++i) {
myObjects[i].onMyEvent = myEventHandler;
}
If you were to create the anonymous function before entering the loop, then only assign references to it to the array elements while inside the loop, you will find that there is no performance or semantic difference whatsoever when compared to the named function version:
var handler = function() {
// do something
};
for (var i = 0; i < 1000; ++i) {
myObjects[i].onMyEvent = handler;
}
In short, there is no observable performance cost to using anonymous over named functions.
As an aside, it may appear from above that there is no difference between:
function myEventHandler() { /* ... */ }
and:
var myEventHandler = function() { /* ... */ }
The former is a function declaration whereas the latter is a variable assignment to an anonymous function. Although they may appear to have the same effect, JavaScript does treat them slightly differently. To understand the difference, I recommend reading, “JavaScript function declaration ambiguity”.
The actual execution time for any approach is largely going to be dictated by the browser's implementation of the compiler and runtime. For a complete comparison of modern browser performance, visit the JS Perf site
Here's my test code:
var dummyVar;
function test1() {
for (var i = 0; i < 1000000; ++i) {
dummyVar = myFunc;
}
}
function test2() {
for (var i = 0; i < 1000000; ++i) {
dummyVar = function() {
var x = 0;
x++;
};
}
}
function myFunc() {
var x = 0;
x++;
}
document.onclick = function() {
var start = new Date();
test1();
var mid = new Date();
test2();
var end = new Date();
alert ("Test 1: " + (mid - start) + "\n Test 2: " + (end - mid));
}
The results:
Test 1: 142ms
Test 2: 1983ms
It appears that the JS engine doesn't recognise that it's the same function in Test2 and compiles it each time.