Closures-
A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment).
Credits- Edward Huang
- Lets understand concept of closures with the help of examples.
- Closures have two major advantages.
1. Memory efficient
Example 1-
- We want to build a counter function that keeps a track of counts and the count gets increased on calling the function. For that we'll need a
count
variable initialized to zero. - But we don't want it to be accessed by anyone else and alter it thus we don't want the
count
variable to be in a global scope for this very reason. - We can't declare it inside the function either because whenever the function will be called, it will create a new execution context that creates a new local scope for the function(we have learned this in previous parts of our series). Thus the
count
variable gets reinitialized to zero every time we call the function, hence we can't declare it in local/functional scope either. - We can also try to use nested functions just like this-
function add() {
let counter = 0;
function plus() {counter += 1;}
plus();
return counter;
}
But here we can't call the plus()
function from outside hence this is of no use.
- Here comes the concept of closures and self invoked functions(learned in earlier parts of the series).
A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment).
const add = (function () {
let counter = 0;
return function () {counter += 1; return counter}
})();
add();
add();
add();
- Here as you can see the function that we return from the self invoked function has a reference of a variable that is outside its local environment just like we said in closures-
with references to its surrounding state
. - These references from external environment are stored in the memory even if we lose the function outside because the particular reference is being used in the function we call.
- That's why closures are very a powerful concept.
Example 2-
Code-
const getHeavy = heavy();
console.log(getHeavy(699))
console.log(getHeavy(700))
console.log(getHeavy(701))
// we don't want to pollute global namespace
function heavy() {
const bigArray = new Array(7000).fill('hello')
return function(item) {
return bigArray[item]
}
}
- Here we are returning a function that can access the required index whenever called, without polluting our global namespace.
- Here the reference to the array
bigArray
stays in memory even though the outer function is popped from the call stack and its context is removed because of the concept of closures and we are able to use thegetHeavy
function to access required indexes from it.
Output-
"hello"
"hello"
"hello"
2. Encapsulation
- We can make variables that are not accessible in the global scope by anyone or any function.
- We can also make variables that are accessible via a function without it being in its local scope such that it gets destroyed when its execution context is popped of the call stack.
- We can make variables encapsulated and safe with the help of closures.
Example-
Code-
const getHeavy = heavy();
console.log(getHeavy(699))
console.log(getHeavy(700))
console.log(getHeavy(701))
// we don't want to pollute global namespace
function heavy() {
const bigArray = new Array(7000).fill('hello')
return function(item) {
return bigArray[item]
}
}
- The
bigArray
cannot be accessed from anywhere in the function except for the function that we return to thegetHeavy
variable. - In this way the array is encapsulated, we can access it anytime, from anywhere without it being declared in the global namespace/scope and this is property is very useful in different scenarios.
Credits- Neelesh Vishwakarma
Prototypal Inheritance-
Prototypal Inheritance is a method by which an object can inherit the properties and methods of another object.
- All JavaScript objects inherit properties and methods from a prototype.
- Date objects inherit from
Date.prototype
- Array objects inherit from
Array.prototype
- Person objects inherit from
Person.prototype
The
Object.prototype
is on the top of the prototype inheritance chain:Date objects, Array objects, and Person objects inherit from
Object.prototype
.And if we check the prototype of the
Object
then we seenull
being returned by JavaScript since Object is root element in JS.
Credits- Ryan Thelin
-
__proto__
is another keyword that can help us determine the parent/prototype of any object(even array or function) in javascript.
Let's see this with help of an example-
Example 1-
- Let's make an object for a phone that would have all the basic properties that a phone should have.
Then we would make an Object for an iPhone, that would inherit the properties from the generic phone object to specify all the basic features and then add its own specific features to the iPhone object(itself).
We also have a
isPrototypeOf()
method that checks if an object exists in another object's prototype chain.
The
hasOwnProperty()
method returns a boolean indicating whether the object has the specified property as its own property (as opposed to inheriting it).
Code-
const phone = {
calling: true,
camera: true,
touchscreen: true,
}
const iphone = {
software: "IOS",
security: "Face Unlock",
}
iphone.__proto__ = phone
console.log(iphone.calling)
console.log(phone.isPrototypeOf(iphone))
console.log(phone.hasOwnProperty(camera))
- In this example, when run
console.log(iphone.calling)
, the JS engine checks iphone's properties and looks for the keycalling
. - When we use prototypal inheritance, the properties don't get added to the child object itself. That is why, when we access a property that is not there in the child object, the JS engine continues its searches up the prototype chain in parent object's properties and returns if found.
- If not found,
undefined
is logged on the console. - This above is the reason, false is returned, when we run
console.log(phone.hasOwnProperty(camera))
because the iphone object doesn't have thecamera
property natively, instead it is inherited from the prototytpe.
Output-
true
true
false
Example 2-
-
__proto__
always returns the parent object of our current object that it inherits its properties from. - If we take an array or a function and access
__proto__
property of either one, firstly we'll see their respective objects in the output. - But if we further access the
__proto__
property of their outputs then we get the constructor object "Object" that is the base unit of arrays, functions, objects etc in JavaScript. - We cannot go any further back than the Object property. Behind that we only receive
null
.
Code-
const phone = {
calling: true,
camera: true,
touchscreen: true,
}
const iphone = {
software: "IOS",
security: "Face Unlock",
}
iphone.__proto__ = phone
console.log(iphone.__proto__) // we recieve phone object
console.log(iphone.__proto__.__proto__) // we get the base constructor object
console.log(iphone.__proto__.__proto__.__proto__) // we get null here since we cannot go further back than an Object which is base unit
Output-
-
prototype
keyword in JavaScript is always present in the parent object that holds all the properties that would be inherited down to its child. It also holds the parent object's own__proto__
property to access its parent.
Example to help understand-
Code-
const phone = {
calling: true,
camera: true,
touchscreen: true,
}
const iphone = {
software: "IOS",
security: "Face Unlock",
}
iphone.__proto__ = phone
console.log(iphone.prototype)
Traditionally, in order to get and set the
[[Prototype]]
of an object, we useObject.getPrototypeOf
andObject.setPrototypeOf
. Nowadays, in modern language, it is being set using__proto__
.One reason to use the built-in prototype object is if you'll be duplicating an object multiple times that will share common functionality. By attaching methods to the prototype, you can save on duplicating methods being created per each new instance.
__proto__
is an object in every class instance that points to the prototype it was created from.The only true difference between
prototype
and__proto__
is that the former is a property of a class constructor, while the latter is a property of a class instance.__proto__
is the actual object that is used in the lookup chain to resolve methods, etc.prototype
is the object that is used to build__proto__
.Updating the
__proto__
property is not a good practice, instead a good way to inherit properties is by usingObject.create()
.
Another way of creating a prototype chain Object.create()
ECMAScript 5 introduced a new method:
Object.create()
. Calling this method creates a new object. The prototype of this object is the first argument of the function.
Example-
Code-
const phone = {
calling: true,
camera: true,
touchscreen: true,
}
const iphone = Object.create(phone)
iphone.software= "IOS",
iphone.security= "Face Unlock"
console.log(iphone.calling)
Output-
true
Some useful articles-
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain
- https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes
Tricky example to test knowledge about prototype chain-
Code-
const multiply = function(a, b){
return a*b
}
console.log(multiply.__proto__)
console.log(Function.prototype)
console.log(multiply.__proto__.__proto__)
console.log(Object.prototype)
console.log(typeof Object)
console.log(typeof Object.prototype)
Output-
Function constructor
Function constructor
Object constructor
Object constructor
'function'
'object'
-
Object
is an inbuilt function in JavaScript. It also has a prototype of its own like all the other functions in JS. -
Object.prototype
returns an'object'
as output since the base element/parent of a function is the object constructor in JavaScript. (as we learned before)
Connect with me-
Appendix-
- Advanced JavaScript Series - Part 1: Behind the scenes (JavaScript Engine, ATS, Hidden Classes, Garbage Collection)
- Advanced JavaScript Series - Part 2: Execution Context and Call Stack
- Advanced JavaScript Series - Part 3: Weird JS behavior, Strict Mode and Hoisting, Temporal Dead Zone
- Advanced JavaScript Series - Part 4.1: Global, Function and Block Scope, Lexical vs Dynamic Scoping
- Advanced JavaScript Series - Part 4.2: Scope Chains and their working, Lexical and Variable Environments
- Advanced JavaScript Series - Part 5: IIFE & 'this' keyword in JS(tricky Eg.), call(), apply(), bind(), Currying(Functional Prog)
- Advanced JavaScript Series - Part 6.1: Everything in JS is an Object? Weird JS behaviors revealed, Primitive Non-Primitive Types
- Advanced JavaScript Series - Part 6.2: Pass by Value & Pass by Reference, Shallow & Deep Copy, Type Coercion
- Advanced JavaScript Series - Part 7: First Class Citizens & Higher Order Functions
- Advanced JavaScript Series - Part 8: The 2 Pillars~ Closures & Prototypal Inheritance
- Advanced JavaScript Series - Part 9: Constructor Functions, Object Oriented,
new
keyword
References-
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures
- https://www.geeksforgeeks.org/prototypal-inheritance-using-__proto__-in-javascript/
- https://javascript.plainenglish.io/proto-vs-prototype-in-js-140b9b9c8cd5
- https://stackoverflow.com/questions/4736910/javascript-when-to-use-prototypes
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isPrototypeOf
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty
Top comments (0)