Exploring the Power of JavaScript Lambda Expressions

André Ericson
September 11, 2023

Lambda expressions are present in most of modern programming languages (Python, Ruby, Java...). They are simply expressions that create functions. This is really important for a programming language to support first-class functions which basically means passing functions as arguments to other functions or assigning them to variables.

In JavaScript pre-ES6 we have function expressions which give us an anonymous function (a function without a name).

var anon = function (a, b) { return a + b };

In ES6 we have arrow functions with a more flexible syntax that has some bonus features and gotchas.

// we could write the above example as:
var anon = (a, b) => a + b;
// or
var anon = (a, b) => { return a + b };
// if we only have one parameter we can loose the parentheses
var anon = a => a;
// and without parameters
var () => {} // noop // this looks pretty nice when you change something like:
[1,2,3,4].filter(function (value) {return value % 2 === 0});
// to:
[1,2,3,4].filter(value => value % 2 === 0);

One of the major advantages of arrow functions is that it does not have it's own this value. It's this is lexically bound to the enclosing scope. This mean we can kiss goodbye to this horrible pattern:

class Logger { dumpData(data) { var _this = this; // this dumps data to a file and get the name of the file via a callback dump(data, function (outputFile) { _this.latestLog = outputFile; }); }
}
// using arrow functions
class Logger { dumpData(data) { dump(data, outputFile => this.latestLog = outputFile); }
}

However there are some gotchas you should be aware of:

  • This should be pretty obvious, but because this is lexically bound you can't change it; call() and apply() will not be able to provide another value for this.
  • There is no arguments variable:
(function () {console.log(arguments)})(1, 2); // will output [1, 2] (() => console.log(arguments))(1, 2); // will raise ReferenceError: arguments is not defined

  • Be careful when returning object literals
(() => {foo: 1})() // this will return undefined. 'foo: 1' is interpreted as a statement composed of a label and the literal 1 // the correct way should be wrapping it with parenthesis
(() => ({foo: 1}))() // returns Object {foo: 1}

Remember this is all ES6 and not supported by all browsers but you can always use Babel.

In the ever-evolving landscape of web development, JavaScript stands as an indispensable tool for crafting dynamic and interactive web applications. Its versatility knows no bounds, offering developers a myriad of techniques to streamline code and enhance functionality. One such technique that has gained immense popularity in recent years is the use of JavaScript lambda expressions, also called "arrow functions".

These concise, anonymous functions are not only a powerful addition to the JavaScript toolkit but also an essential concept for modern web developers. This is why in this comprehensive blog post, I delve deep into the world of JavaScript lambda expressions. I'll explore their syntax, and showcase practical examples to illustrate how they can be harnessed to write more concise and expressive code.

So, let's harness the full potential of JavaScript lambdas and uncover how they can empower your web development endeavors.

What Are JavaScript Lambda Expressions?

Lambda expressions, often referred to as arrow functions, are a fundamental feature in modern JavaScript. They serve as concise and anonymous functions, capable of creating function expressions without the need for the traditional "function" keyword.

The Syntax of Arrow Functions

Understanding the syntax of arrow functions is crucial for harnessing their power. Let's take a closer look at the syntax. In JavaScript, you can write an arrow function like this:

 const anon = (a, b) => a + b;

An arrow function is concise and convenient, providing a shorter syntax for anonymous functions. Here's another example of similar functionality:

const anon = (a, b) => { 
   return a + b;
};

When you have a single parameter, you can even omit the parentheses:

const anon = a => a * 50;

And if you have no parameters, it would look like this:

const anon = () => {}; // noop

This simplified syntax makes your code more readable and elegant. You can even transform code to utilize arrow functions for a more expressive style. For instance, you can transform this:

// Array [ 2, 4 ]
[1, 2, 3, 4].filter(function (value) { 
    return value % 2 === 0;
});

Into this:

[1, 2, 3, 4].filter(value => value % 2 === 0);

Advantages of JavaScript Lambda Expressions

Arrow functions offer several advantages in terms of code readability, conciseness, and ease of use.

One of the key advantages is that lambda functions help in writing cleaner code and improves code maintainability. Another advantage is the simplified syntax of arrow functions. They allow for more concise and expressive code compared to traditional function expressions. By eliminating the need for the [.code-inline]function[.code-inline] keyword and providing a shorter syntax for declaring functions, arrow functions result in less code verbosity and improved readability.

Arrow functions also have a lexical binding of [.code-inline]this[.code-inline], meaning they capture the value of [.code-inline]this[.code-inline] from the surrounding context. 

Consider this example:

class Logger { 
    dumpData(data) { 
        dump(data, outputFile => { 
            this.latestLog = outputFile; 
        }); 
    } 
}

In the above code, we can directly access the [.code-inline]this[.code-inline] value of the surrounding [.code-inline]Logger[.code-inline] class using an arrow function, improving readability and reducing error-prone code.

This eliminates the need for workarounds (check the example below) like saving the [.code-inline]this[.code-inline] value in a separate variable to access it inside a callback function, resulting in cleaner and more intuitive code.

class Logger {
    dumpData(data) {
        let that = this;
        dump(data, function outputFile() {
            that.latestLog = outputFile;
        });
    } 
}

Additionally, arrow functions do not have their own [.code-inline]arguments[.code-inline] object. This can be an advantage in certain scenarios as it allows for more predictable behavior and encourages the use of rest parameters or the spread operator to manage function arguments.

Important Considerations with JavaScript Lambda Expressions

While arrow functions offer remarkable benefits, they also come with a few considerations:

1. Arrow functions do not allow the manipulation of the [.code-inline]this[.code-inline] value. Unlike traditional functions, you cannot change the [.code-inline]this[.code-inline] value using methods like [.code-inline]call()[.code-inline] or [.code-inline]apply()[.code-inline]. It's essential to keep this in mind when working with arrow functions to ensure the proper context is maintained.

2. Unlike traditional functions, arrow functions do not have access to the [.code-inline]arguments[.code-inline] variable. If you rely heavily on [.code-inline]arguments[.code-inline] within your functions, you should be aware that arrow functions won't have this feature.

(function () {console.log(arguments)})(1, 2); // will output [1, 2] 

(() => console.log(arguments))(1, 2); // will raise ReferenceError: arguments is not defined

3. Lastly, be careful when returning object literals

(() => {foo: 1})() // this will return undefined. 'foo: 1' is interpreted as a statement composed of a label and the literal 1

// the correct way should be wrapping it with parenthesis

(() => ({foo: 1}))() // returns Object {foo: 1}

JavaScript lambda expressions empower developers to write cleaner, more concise code, freeing up your time to focus on solving complex problems. By embracing the power of arrow functions, you can enhance your JavaScript skills and elevate your coding projects to the next level.

If you're interested in improving the reliability and maintainability of your JavaScript projects, check out this informative blog post on adding types with TypeScript.