JS Interview Help: Prototype, Class(ES6), IIFE, Scope, Closures, Module Pattern

Michael Bade
Oct 28, 2020 · 8 min read

Please note — this is a BRIEF explanation, meant for 2–3 minute interview answers. Please use the resources below for a deeper understanding on each topic.


When a new Object is formed, the JS engine adds a __proto__ property to the new object. This points to the prototype object of the constructor function. All JS objects take in, or inherit, properties and methods from a prototype. For instance, Dog objects inherit from Dog.prototype. String objects inherit from String.prototype.

The JS prototype property lets you add new properties to object constructors. For example, using ES2015 notation:

Ernie was my childhood dog — this post goes out to you, buddy!

The above prints “Ernie Bade is my name. I am 6 years old and my breed is Golden Retriever,” because we have added a property of about to the Dog prototype.


Classes are everywhere, especially in popular modern libraries, like React.js. Whether we realize it or not, (almost) every component utilizes class inheritance to use the React library. We can over complicate this thought process, and dive into some very deep discussion. However, for simplicity of this article, let’s use w3schools definition: JavaScript Classes are templates for JavaScript Objects.

What does this mean? Well, basically, they’re modernized prototypes. They’re “primarily syntactical sugar over JS’s existing prototype-based inheritance.”

So, taking the above Prototypal example, let’s convert it to an ES6 Class:

Above, we have the exact same Object being created. However, this time, we’re using ES6. We use a constructor method to instantiate each new instance of the Dog class, then passing in certain arguments to the class, we use those arguments in the context of the new instance itself. So, ernie is a new Dog with firstName set to “Ernie”, lastName set to “Bade”, age set to 6, and breed set to “Golden Retriever.”

There are many benefits to classes:

  • Everything is contained in one area, allowing for easy navigation of separation of concerns. Each new instance of the Dog class will have the same properties.
  • This way creates a single way to emulate classes in JS.
  • More familiar to coders who use a class-based language.

So, why did we even discuss Prototypes, if classes are more common now? Legacy code. The internet wasn’t invented in 2015… There’s a lot of legacy code that utilizes Prototypes, and it’s our job to be able to understand, and eventually, modernize that code to today’s standards. Who knows — maybe in 2025 the ES6 standard will be considered legacy!

Instantly-Invoked Function Expressions (IIFE)

An Immediately-invoked Function Expression (IIFE) is a way to execute functions immediately, as soon as they are created. The pattern is simply invoking a function expression. A function declaration is the “normal” way of creating a named function. IIFEs are great because they don’t pollute the global object, and are a simple way to isolate variable declarations.

In the above example, we have a “normal” function declaration, called tennisBalls. When the JS engine sees this function, it returns undefined. When the JS engine sees lines 5–7, it not only returns undefined, it also invokes the function immediately. Since we placed parentheses around the entire function, and added an additional set of parentheses after it, it is immediately invoked.

The main reason to use IIFEs is to obtain data privacy. Since JS’s var scopes variables to their containing function, any variables declared in the IIFE can’t be accessed by the outside world.


Simply put, scope determines the accessibility of variables. There are two basic forms of scope: Local or Global scope. JS has function scope — each function creates a new scope. So, variables that are defined inside a function are in local scope, while variables outside of a function are in the global scope. For example, what do you think will print below?

It should print “Ernie is the goodest boy.” However, since the variable, ernie, is defined within the scope of namingErnie(), when line 5 is executed, the JS engine does not have access to this variable. In order to access ernie, we need to either move this variable into global scope OR move our console.log statement inside the function.

This brings up another topic, lexical scope. In lexical scope, a variable defined outside a function can be accessible inside another function defined after the variable declaration. However, just like with local scope, the variables defined inside a function are not accessible outside that function. For example:

This will print “Ernie is the goodest boy.” because the function, namingErnie hits line 3, it will look up in scope, to find a variable named ernie. Since this variable is declared and assigned a value on line 1, namingErnie looks up its lexical scope to use that declared variable in its context.


Every function in JavaScript has a closure. From w3schools, “a closure is a function having access to the parent scope, even after the parent function has closed.” Primarily, the closure has three scope chains: it has access to its own scope (variables defined between its curly brackets), it has access to the outer function’s variables, and it has access to the global variables.

This makes one of the most complicated, but amazing features of JS. Without closures, callbacks and event handlers would be hard to implement.

A closure is created when you define a function — not when you execute it. Then, every time you execute that function, its already-defined closure gives it access to all the function scopes available around it. Let’s get into some code.

Let’s walk through this. Inside our function, we declare a timesAsked variable, which counts the number of times Ernie has asked to go on a walk. We also increment it each time we call the function (line 4). Then, we establish our canWeGo variable, which points to a simple string. That string is then console.logged the number of times Ernie has asked to go on a walk (line 6). In theory, since we are calling this function three times (lines 10–12), each declaration should print an additional ‘Can we go on a walk?’ statement. Line 10 should console.log ‘Can we go on a walk?’. Line 11 should console.log ‘Can we go on a walk?Can we go on a walk?’. Finally, Line 12 should console.log ‘Can we go on a walk?Can we go on a walk?Can we go on a walk?’ However, each time this function is called, the counter variable is reset to 0, meaning that when Ernie asks us to go for a walk on line 12, our brains have forgotten about the previous 2 times he asked (lines 10 and 11), making him very agitated and antsy.

So, how do we fix this? Closure:

This is a very similar function to the original ernieGoesOnAWalk, but this time, we use closure to solve our issue. Here, we’ve established ernieGoesOnAWalk as an IIFE (this is so that the function is invoked as soon as it’s called), and within this IIFE, we’ve declared another function, with its own closure (line 4). Inside this returned function (still line 4), we increment the timesAsked by one, and console.log our phrase. Now, every time ernieGoesOnAWalk is called, the timesAsked variable is not re-established at 0, but rather, it’s incremented to the number of times ernieGoesOnAWalk is called. Closure makes it possible for the inner function to access its parent function and have private variables. The timesAsked is protected by the scope of the anonymous function, and can only be changed using the ernieGoesOnAWalk function.

Please note… This is a VERY complicated topic, and since scope is related to closure, it’s easy to mistake closure for scope. Read, re-read, and re-re-read articles on this subject to get a firm grasp on it.

Module Pattern

From Eloquent JS: A module is a piece of program that specifies which other pieces it relies on and which functionality it provides for other modules to use (its interface). It’s easy to use and encapsulates our code. Modules can load each other and use import or export for functionality. The export keyword labels variables and functions as accessible outside of the current module. The import keyword allows the functionality from outside modules. For instance, say we have two files: ernieBarks.js and familyGreeting.js:

export function bark() {
console.log('Ruff Ruff Ruff! Squirrel?')

In the above module, we have a simple function which console.logs ‘Ruff Ruff Ruff! Squirrel?’ (Ernie gets very distracted…). By export-ing this module, we are allowing it to be used in any imported module:

import { bark } from ./ernieBarks.js

Since the bark method is imported to familyGreeting.js, it is now accessible to this module. By calling this method, ‘Ruff Ruff Ruff! Squirrel?’ is printed in our console.

Why do all of this? Since all of our code is in one logical block, our code is more maintainable and easier to update. Also, we can reuse each module as many times as we want, and don’t need to define the same functions in multiple modules. In the above example, every file I import bark will be able to call that function, and use it. Say, if Ernie is sick and can only bark ‘Ruff!’ one time, I can change the code in this module alone, and it will automatically update all imported modules as well.

//updating ernieBarks.js
export function bark() {

The Startup

Get smarter at building your thing. Join The Startup’s +750K followers.