OOPS-1
Agenda¶
Topics to cover in Javascript Oops
- This keyword
- Constructor functions
- Classes & Classical Inheritance
- Prototype & Prototypal Inheritance
- Call ,Apply & Bind Methods
- Memory of objects (Reference Datatypes)
We will try to cover most of these topics in today's sessions and the remaining in the next.
It is going to be a bit challenging, advanced, but very interesting session covering topics that are asked very frequently in interviews.
So let's start.
This Keyword¶
Certainly! When covering Object-Oriented Programming (OOP) in JavaScript, the this keyword is a crucial concept to understand. Here's a breakdown of topics you can cover regarding the this keyword in JavaScript and its relevance to OOP:
-
Introduction to
thiskeyword: -
Explain that
thisis a special keyword in JavaScript that refers to the current execution context or the current object. -
Mention that its value depends on how a function is called, rather than where it is defined.
-
Global Context:
-
Describe that in the global context (outside of any function),
thisrefers to the global object (e.g.,windowin browsers,globalin Node.js). -
Function Context:
-
Explain that within a function, the value of
thiscan change based on how the function is invoked. -
Discuss function invocation methods:
- Regular function invocation:
thisusually refers to the global object (in non-strict mode) orundefined(in strict mode). - Method invocation:
thisrefers to the object that the method is called on. - Constructor invocation:
thisrefers to the instance being created by the constructor function. call()andapply()methods: Explicitly setthisfor a function call.- Arrow functions:
thisretains the value of the enclosing lexical context.
- Regular function invocation:
-
Object-Oriented Programming (OOP) and
this: -
Explain how
thisis used in the context of OOP to refer to the instance of an object. -
Describe how methods within an object can access other properties and methods using
this. -
Common
thisPitfalls: -
Describe issues that developers often face with
this:- Losing
thiscontext in nested functions. - Using
thisin callbacks without proper binding. - Caching
thisin a variable to ensure proper reference in nested scopes.
- Losing
-
Solving
thisIssues: -
Introduce solutions to common
thisproblems:- Using
.bind(),.call(), or.apply()to explicitly setthis. - Storing a reference to the outer
thisin a variable to use within nested functions. - Using arrow functions to preserve the lexical scope's
this.
- Using
-
Examples and Demonstrations:
-
Provide code examples for each type of function invocation and how
thisbehaves in each case. -
Show scenarios where
thisis used within object methods to access object properties. -
Best Practices:
-
Emphasize the importance of understanding the context in which
thisis used. - Encourage using arrow functions in methods when you want to retain the outer scope's
this. -
Suggest using modern ES6 features like classes to manage
thismore effectively. -
Browser vs. Node.js:
- Mention the differences in the global object (
windowvs.global) and how they affectthisbehavior.
this keyword in Node.js in non-strict mode¶
Certainly, let's break down the behavior of the this keyword in Node.js in non-strict mode for each of the scenarios you mentioned. After that, I'll provide a summary in tabular form.
Assumption: fn is a function defined globally, and obj is an object containing a method fn.
// Scenario 1: Console.log(this)
console.log("Scenario 1:");
console.log(this); // Output: {}
// Scenario 2: Console.log(this) -> fn = global object
console.log("Scenario 2:");
function fnGlobal() {
console.log(this === global); // true
}
fnGlobal();
// Scenario 3: this -> obj -> fn = object itself
console.log("Scenario 3:");
var obj = {
fn: function () {
console.log(this === obj); // true
}
};
obj.fn();
// Scenario 4: this -> obj -> fn -> fn = global object
console.log("Scenario 4:");
var obj2 = {
fn: function () {
console.log(this === obj2); // true
var nestedFn = function () {
console.log(this === global); // true
};
nestedFn();
}
};
obj2.fn();
Now, let's summarize these scenarios in a tabular form:
| Scenario | Code | Output | Explanation |
|---|---|---|---|
| 1 | console.log(this); |
{} |
In global context, this refers to the global object. |
| 2 | function fnGlobal() {...}fnGlobal(); |
true (inside the function) |
In a regular function, this refers to the global object. |
| 3 | obj.fn = function() {...}obj.fn(); |
true (inside the method) |
Inside an object method, this refers to the object itself. |
| 4 | obj2.fn = function() {...}obj2.fn(); |
true (inside the method)true (inside nested function) |
Inside a nested function, this reverts to the global object. |
In scenarios 3 and 4, this refers to the object containing the method when that method is directly invoked. However, when a nested function is defined within the method and invoked within it, this inside the nested function refers to the global object (global in Node.js).
Understanding these behaviors helps in writing clean and predictable code, especially when dealing with methods and nested functions within objects.
this keyword in Browser in non-strict mode¶
Certainly, let's break down the behavior of the this keyword in Browser in non-strict mode for each of the scenarios you mentioned. After that, I'll provide a summary in tabular form.
Assumption: fn is a function defined globally, and obj is an object containing a method fn.
Sure, let's break down each scenario and then summarize them in a tabular form.
Scenario 1: console.log(this)
console.log(this); // Window Object
In this scenario, if you directly execute console.log(this) in the global context of a web browser (not within any function or object), the output will be the Window object. This is because this refers to the global object, which is the Window object in a browser environment.
Scenario 2: console.log(this) inside a function
function exampleFunction() {
console.log(this);
}
exampleFunction(); // Window Object
In this case, when you call exampleFunction(), it's being invoked as a regular function. The this inside the function still refers to the global object (Window in the browser).
Scenario 3: this inside an object method
var obj = {
prop: 'I am a property',
method: function() {
console.log(this.prop);
}
};
obj.method(); // "I am a property"
In this scenario, obj is an object containing a method named method. When you call obj.method(), the this inside the method refers to the obj itself. Therefore, this.prop accesses the prop property of the obj object.
Scenario 4: this inside nested functions
var obj = {
prop: 'I am a property',
method: function() {
var nestedFunction = function() {
console.log(this.prop);
};
nestedFunction();
}
};
obj.method(); // undefined
Here, within the nestedFunction, this refers to the global object (Window in the browser). This is because the function nestedFunction is not a method of obj. As a result, this.prop will be undefined since the global object doesn't have a prop property.
Now, let's summarize these scenarios in a tabular form:
| Scenario | this Value |
Explanation |
|---|---|---|
console.log(this) |
Window Object | Global context, this refers to the global object (Window in browser). |
console.log(this) |
Window Object | Inside a regular function, this still refers to the global object. |
this in object method |
obj (object itself) |
Inside a method, this refers to the object on which the method is invoked. |
this in nested function |
Window Object | Inside a nested function, this refers to the global object. |
Understanding these scenarios is important for grasping how this behaves in different contexts within a browser environment.
this keyword in Node.js in strict mode¶
Certainly, let's break down the behavior of the this keyword in Node.js in strict mode for each of the scenarios you mentioned. After that, I'll provide a summary in tabular form.
Assumption: fn is a function defined globally, and obj is an object containing a method fn. Here's what each scenario seems to be referring to:
console.log(this)(Scenario 1):
"use strict";
console.log(this); // Outputs an empty object ({})
In strict mode, when you log this in the global context, it will be an empty object {}. This is because strict mode prevents the default binding of this to the global object.
console.log(this)with Undefined Function (Scenario 2):
"use strict";
function myFunction() {
console.log(this);
}
myFunction(); // Outputs undefined
In strict mode, when you call a function without specifying its context (this), it's set to undefined. This is different from non-strict mode, where it would point to the global object.
thisInside an Object Method (Scenario 3):
"use strict";
var obj = {
prop: "I'm a property",
method: function() {
console.log(this.prop);
}
};
obj.method(); // Outputs "I'm a property"
When a method is invoked on an object, this within the method refers to the object itself. So, this.prop accesses the prop property of the obj object.
thisInside Nested Object Methods (Scenario 4):
"use strict";
var outerObj = {
innerObj: {
method: function() {
console.log(this);
}
}
};
outerObj.innerObj.method(); // Outputs the inner object
Inside nested object methods, this refers to the closest containing object. In this case, it points to innerObj when the method is invoked.
Now, let's summarize these scenarios in tabular form:
| Scenario | Example Code | this Value |
Explanation |
|---|---|---|---|
| 1 | console.log(this); |
{} |
In strict mode, this is an empty object in the global context. |
| 2 | myFunction(); |
undefined |
Calling a function without context results in undefined. |
| 3 | obj.method(); |
obj object reference |
this in an object method points to the object itself. |
| 4 | outerObj.innerObj.method(); |
innerObj object reference |
In nested methods, this refers to the closest containing object. |
Understanding these scenarios and how this behaves in different contexts is crucial for writing reliable and maintainable code.
this keyword in Browser in strict mode¶
Certainly, let's break down the behavior of the this keyword in Browser in strict mode for each of the scenarios you mentioned. After that, I'll provide a summary in tabular form.
Assumption: fn is a function defined globally, and obj is an object containing a method fn. Assuming you have the following setup in a browser environment:
"use strict";
// Scenario 2
console.log("Scenario 2:");
console.log(this); // Output: undefined
// Scenario 3
var obj = {
fn: function () {
console.log("Scenario 3:");
console.log(this); // Output: obj
},
};
// Scenario 4
var fn = obj.fn;
// Scenario 1
console.log("Scenario 1:");
console.log(this); // Output: window
// Scenario 4 (contd.)
console.log("Scenario 4:");
fn(); // Output: undefined
Now, let's summarize these scenarios in a tabular form:
| Scenario | Code | this Value |
Explanation |
|---|---|---|---|
| 1 | console.log(this); |
window object |
In the global context, this refers to the global object (window in browsers). |
| 2 | "use strict"; console.log(this); |
undefined |
In strict mode, this in the global context is undefined. |
| 3 | obj.fn(); |
obj object |
When calling a method (fn) of an object (obj), this refers to the object itself (obj). |
| 4 | var fn = obj.fn; fn(); |
undefined |
When a method (fn) is assigned to a variable (fn) and called, this is undefined. |
In summary:
- In the global context, outside of any function,
thisrefers to the global object (windowin browsers). - In strict mode, in the global context,
thisisundefined. - When a function is a method of an object,
thiswithin the function refers to the object itself. - If a method is assigned to a variable and then invoked,
thisinside the method will beundefined.
Remember, the behavior of this can be a bit tricky, especially when dealing with nested functions, callbacks, and different contexts. Understanding these scenarios helps you write more predictable and maintainable code in JavaScript.
Constructor Functions¶
In JavaScript, constructor functions are used to create and initialize objects. They serve as templates for creating objects with similar properties and methods. Constructor functions are typically written with an initial capital letter to distinguish them from regular functions.
A constructor function defines the structure of an object by setting its properties and methods using the this keyword. When you create an object using a constructor function with the new keyword, it creates a new instance of that object type and binds the instance to the this keyword within the constructor function.
Car Company Example:
Imagine you're building a car manufacturing company. Each car will have properties like the make, model, year, and color. You can create a constructor function called Car to represent a car object.
Coding Example:
Here's how you can implement the car company example using a constructor function:
// Constructor function for Car
function Car(make, model, year, color) {
this.make = make;
this.model = model;
this.year = year;
this.color = color;
this.start = function() {
console.log(`Starting the ${this.make} ${this.model}`);
};
}
// Creating car instances
const car1 = new Car("Toyota", "Camry", 2022, "Blue");
const car2 = new Car("Ford", "Mustang", 2023, "Red");
// Using the car instances
console.log(car1); // Car { make: 'Toyota', model: 'Camry', year: 2022, color: 'Blue' }
console.log(car2); // Car { make: 'Ford', model: 'Mustang', year: 2023, color: 'Red' }
car1.start(); // Starting the Toyota Camry
car2.start(); // Starting the Ford Mustang
In this example:
- The
Carconstructor function defines the properties (make,model,year,color) and thestartmethod for a car object. - Instances of cars (
car1andcar2) are created using the constructor function. - The
startmethod is accessed and invoked on each car instance.
By using constructor functions, you can create multiple car instances with the same structure but different property values. This follows the fundamental concept of OOP, where you create objects based on blueprints (constructor functions) that define their structure and behavior.
Constructor function with this example¶
// Constructor function for Car
function Car(nameParam, colorParam, topSpeedParam) {
// Using 'this' to refer to instance-specific properties
this.name = nameParam;
this.color = colorParam;
this.topSpeed = topSpeedParam;
// Using 'this' to refer to instance-specific method
this.drive = function() {
console.log(`I am driving ${this.name}`);
};
}
// Creating car instances using 'new'
let car1 = new Car("Ferrari", "Red", '1000km/hr');
let car2 = new Car("8", 'white', '600km/hr');
// Using the car instances
car1.drive(); // Output: I am driving Ferrari
car2.drive(); // Output: I am driving 8
Explanation of this usage:
-
In the constructor function (
Car):- When creating a new instance using the constructor with
new, thethiskeyword refers to the newly created instance. - Properties (
name,color,topSpeed) are assigned to the instance usingthisfollowed by the property name. For example,this.namerefers to thenameproperty of the specific instance being created. - The
drivemethod is assigned to each instance withthis.drive. Inside thedrivemethod,this.namerefers to thenameproperty of the instance that is calling the method. This allows each instance to have its own unique behavior.
- When creating a new instance using the constructor with
-
When creating car instances:
let car1 = new Car("Ferrari", "Red", '1000km/hr');creates an instance ofCarnamedcar1. Within the constructor,this.namebecomes "Ferrari" forcar1.let car2 = new Car("8", 'white', '600km/hr');creates another instance namedcar2. Inside the constructor,this.namebecomes "8" forcar2.
-
Using the car instances:
- Calling the
drivemethod oncar1(car1.drive()) prints "I am driving Ferrari". Within the method,this.namerefers to thenameproperty ofcar1. - Calling the
drivemethod oncar2(car2.drive()) prints "I am driving 8". Within the method,this.namerefers to thenameproperty ofcar2.
- Calling the
The this keyword is essential in JavaScript's OOP to distinguish properties and methods of each individual instance when using constructor functions to create objects.