JavaScript Concepts to Make Your Head Spin: Closures, Currying, and More 🤪

Udit Kumar's Avatar

Udit Kumar / February 08, 2022

5 min read • ––– views

Banner Image

Hold onto your hats, folks, because we're about to go on a wild ride through the land of JavaScript. During this journey, we'll cover all the important concepts you need to know to be a JavaScript master.

So, let's get started ✨!

JavaScript prototypes and prototypal inheritance

JavaScript is a prototype-based language. This means that objects in JavaScript have a hidden internal property called [[Prototype]] (as opposed to classes, which don't). This [[Prototype]] property is either null or references another object. That object is called the prototype.

When you try to access a property on an object, and if the object doesn't have that property, JavaScript will look at the object's [[Prototype]]. If the [[Prototype]] also doesn't have the property, then JavaScript will look at the [[Prototype]] of the [[Prototype]], and so on. This is called prototypal inheritance.

Let's see this in action:

js
const person = { isHuman: false, printIntroduction: function () { console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`); } }; const me = Object.create(person); me.name = 'Udit'; // "name" is a property set on "me", but not on "person" me.printIntroduction(); // expected output: "My name is Udit. Am I human? false"

In the above example, me is an object created from the person object using Object.create().

me doesn't have its own isHuman property, but since objects inherit properties from their prototypes, me.isHuman will return the isHuman property found on person.

JavaScript closures

A closure is the combination of a function and the lexical environment within which that function was declared. This environment consists of any local variables that were in-scope at the time the closure was created.

In short, 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.

Let's see this in action:

js
function makeAdder(x) { // parameter `x` is an inner variable // inner function `add()` uses `x`, so // it has a "closure" over it function add(y) { return y + x; } return add; } // `plusOne` gets a reference to the inner `add(..)` // function with `x` preset to `1` const plusOne = makeAdder(1); // `plusTen` gets a reference to the inner `add(..)` // function with `x` preset to `10` const plusTen = makeAdder(10); plusOne(3); // 4 <-- 1 + 3 plusOne(41); // 42 <-- 1 + 41 plusTen(13); // 23 <-- 10 + 13

In the above example, plusOne and plusTen are both closures. They share the same function body definition, but store different lexical environments. In plusOne, x is 1, and in plusTen, x is 10.

JavaScript this

The this keyword refers to the object it belongs to.

It has different values depending on where it is used:

  • In a method, this refers to the owner object (here method means a function that is a property of an object).
  • Alone, this refers to the global object.
  • In a function, this refers to the global object.
  • In a function, in strict mode, this is undefined.
  • In an event, this refers to the element that received the event.
  • Methods like call(), and apply() can refer this to any object which is passed as an argument.

Let's see this in action:

js
const person = { name: 'Udit', weight: 70, info: function () { console.log(`${this.name} weighs ${this.weight} kg.`); } }; person.info(); // "Udit weighs 70 kg."

In the above example, this refers to the person object that "owns" the info() method.

JavaScript Currying

Currying is a technique of evaluating function with multiple arguments, into sequence of functions with single argument.

A curried function takes multiple arguments one at a time and returns a new function that takes the next argument until all arguments are passed.

Let's see this in action:

js
function curry(fn) { return function curried(...args) { if (args.length >= fn.length) { return fn.apply(this, args); } else { return function (...args2) { return curried.apply(this, args.concat(args2)); }; } }; } function sum(a, b, c) { return a + b + c; } const curriedSum = curry(sum); console.log(curriedSum(1, 2, 3)); // 6, still callable normally console.log(curriedSum(1)(2, 3)); // 6, currying of 1st arg console.log(curriedSum(1)(2)(3)); // 6, full currying

In the above example, curry() is a higher-order function that takes a function as an argument and returns a new function. The new function is a curried version of the original function.

Conclusion

Learning JavaScript is a never-ending journey. There are so many concepts to learn and so many things to explore. I hope this blog post helped you understand some of the most important concepts in JavaScript.

That's it for this blog post. I hope you enjoyed it. If you have any questions, feel free to reach out to me on Twitter or LinkedIn. I'd love to hear from you!

• Author Github Profile