JavaScript and TypeScript course ¡ Module 2: Functions, Arrays and Objects
The this Keyword in JavaScript
In this lesson9
Welcome back to Jurassic Park! Today we will dive deep into one of the most intriguing and sometimes confusing topics in JavaScript: the this keyword. Understanding this is crucial for effective object-oriented programming in JavaScript and for building complex systems like our park management system.
What is this?
The this keyword in JavaScript refers to the object in whose context a function is being executed. You can think of it as the "current workplace" - just like a Jurassic Park employee might work in different locations (e.g., the lab, the T-Rex enclosure, the control center), a function can be executed in different contexts.
The value of this is not determined when the function is defined, but when it is called, which can cause some confusion. The value of this depends on how the function is invoked:
- In the global context
- As an object method
- As a constructor
- Using the
call(),apply(), orbind()methods - In arrow functions
Let's look at each of these cases.
1. this in Global Context
When this is used in a regular function (not called as an object method, outside strict mode), it refers to the global object: window in the browser and global in Node.js (globalThis is the common name for both). At the top level of a browser script this is also window, but in a Node.js file it is module.exports, an empty object {}, and in ES modules it is undefined.
1// In the global scope
2console.log(this); // window in the browser, {} (module.exports) in a Node.js file
3
4// In a regular function
5function checkGlobalThis() {
6 console.log(this); // also window/global object
7}
8
9checkGlobalThis();In strict mode ("use strict";), this in a regular function will be undefined instead of the global object, which helps avoid accidentally creating global variables.
1"use strict";
2
3function strictThis() {
4 console.log(this); // undefined
5}
6
7strictThis();2. this as an Object Method
When a function is called as a method of an object, this refers to that object. This is one of the most common and intuitive uses of this in JavaScript.
1// Object representing a dinosaur
2const velociraptor = {
3 name: "Blue",
4 species: "Velociraptor",
5 age: 5,
6
7 // Method using this
8 describe: function() {
9 console.log(`This ${this.species} is named ${this.name} and is ${this.age} years old.`);
10 },
11
12 // Method that modifies dinosaur data
13 ageDinosaur: function(years) {
14 this.age += years;
15 console.log(`${this.name} is now ${this.age} years old.`);
16 }
17};
18
19// Calling methods
20velociraptor.describe(); // "This Velociraptor is named Blue and is 5 years old."
21velociraptor.ageDinosaur(2); // "Blue is now 7 years old."In the example above, this inside the describe and ageDinosaur methods refers to the velociraptor object, allowing the methods to access its properties.
Potential Pitfall: Losing Context
However, be careful - the this context can be lost if you assign a method to another variable and call it as a regular function:
1const velociraptor = {
2 name: "Blue",
3 describe: function() {
4 console.log(`Hi, I'm ${this.name}!`);
5 }
6};
7
8// Calling as a method - this refers to velociraptor
9velociraptor.describe(); // "Hi, I'm Blue!"
10
11// But when we assign the method to a variable...
12const describeDino = velociraptor.describe;
13
14// ...and call it as a regular function
15describeDino(); // "Hi, I'm undefined!"
16// (or "Hi, I'm !" if a global name variable exists)This is a common problem when passing methods as callbacks:
1const trex = {
2 name: "Rex",
3 roar: function() {
4 console.log(`${this.name} roars loudly!`);
5 }
6};
7
8// Direct call works
9trex.roar(); // "Rex roars loudly!"
10
11// But as a callback after 1 second...
12setTimeout(trex.roar, 1000); // this does not point to trex, so the name Rex will not appear
13
14// Solution (one way) is to use an arrow function or bind():
15setTimeout(() => trex.roar(), 1000); // "Rex roars loudly!"
16setTimeout(trex.roar.bind(trex), 1000); // "Rex roars loudly!"3. this in Constructors
When a function is called with the new operator (as a constructor), this refers to the newly created object. This is a fundamental mechanism for creating object instances in JavaScript.
1// Constructor function for dinosaurs
2function Dinosaur(name, species, age) {
3 // 'this' refers to the newly created object
4 this.name = name;
5 this.species = species;
6 this.age = age;
7
8 this.describe = function() {
9 console.log(`${this.name} is a ${this.species} aged ${this.age} years.`);
10 };
11}
12
13// Creating new instances
14const blue = new Dinosaur("Blue", "Velociraptor", 5);
15const rex = new Dinosaur("Rex", "Tyrannosaurus", 8);
16
17blue.describe(); // "Blue is a Velociraptor aged 5 years."
18rex.describe(); // "Rex is a Tyrannosaurus aged 8 years."
19
20// Let's modify Blue's age
21blue.age = 6;
22blue.describe(); // "Blue is a Velociraptor aged 6 years."In modern JavaScript (ES6+), instead of constructor functions we often use classes, which provide a cleaner syntax for doing the same thing:
1// ES6 class for dinosaurs
2class Dinosaur {
3 constructor(name, species, age) {
4 this.name = name;
5 this.species = species;
6 this.age = age;
7 }
8
9 // Methods are automatically added to the prototype
10 describe() {
11 console.log(`${this.name} is a ${this.species} aged ${this.age} years.`);
12 }
13
14 // Method that changes the age
15 growOlder(years = 1) {
16 this.age += years;
17 console.log(`${this.name} is now ${this.age} years old.`);
18 }
19}
20
21// Creating instances
22const trixie = new Dinosaur("Trixie", "Triceratops", 7);
23trixie.describe(); // "Trixie is a Triceratops aged 7 years."
24trixie.growOlder(3); // "Trixie is now 10 years old."4. Controlling this: call(), apply(), and bind()
JavaScript offers three methods that allow you to explicitly control the value of this when calling a function:
call()
The call() method calls a function with a specified this value and arguments passed individually:
1function examineSpecimen(testType, labNumber) {
2 console.log(`Examining ${this.species} named ${this.name}...`);
3 console.log(`Test type: ${testType}, Laboratory: ${labNumber}`);
4}
5
6const specimen = {
7 name: "Echo",
8 species: "Velociraptor",
9 age: 3
10};
11
12// Calling the function with 'this' set to specimen
13examineSpecimen.call(specimen, "DNA", "Lab-A");
14// "Examining Velociraptor named Echo..."
15// "Test type: DNA, Laboratory: Lab-A"apply()
The apply() method works similarly to call(), but arguments are passed as an array:
1// Same function
2function examineSpecimen(testType, labNumber) {
3 console.log(`Examining ${this.species} named ${this.name}...`);
4 console.log(`Test type: ${testType}, Laboratory: ${labNumber}`);
5}
6
7// Calling with arguments in an array
8examineSpecimen.apply(specimen, ["Behavioral", "Lab-B"]);
9// "Examining Velociraptor named Echo..."
10// "Test type: Behavioral, Laboratory: Lab-B"bind()
The bind() method creates a new function with a permanently assigned this value:
1const dinosaur = {
2 name: "Delta",
3 species: "Velociraptor"
4};
5
6function makeSound(sound) {
7 console.log(`${this.name} makes a sound: ${sound}!`);
8}
9
10// Creating a new function with 'this' bound to dinosaur
11const dinosaurSound = makeSound.bind(dinosaur);
12
13// Now we can call this function multiple times
14dinosaurSound("Growling"); // "Delta makes a sound: Growling!"
15dinosaurSound("Hissing"); // "Delta makes a sound: Hissing!"
16
17// We can also pre-bind arguments
18const deltaRoar = makeSound.bind(dinosaur, "ROAR");
19deltaRoar(); // "Delta makes a sound: ROAR!"Practical Example: Dinosaur Tracking System
The following example shows how different methods of controlling this can be used to create a flexible monitoring system:
1const dinoMonitoringSystem = {
2 activeDinosaurs: [],
3
4 // Register a new dinosaur
5 registerDinosaur: function(dinosaur) {
6 this.activeDinosaurs.push(dinosaur);
7 console.log(`Registered new dinosaur: ${dinosaur.name} (${dinosaur.species})`);
8 },
9
10 // Find a dinosaur by name
11 findDinosaurByName: function(name) {
12 return this.activeDinosaurs.find(dino => dino.name === name);
13 },
14
15 // Trigger an alert procedure
16 triggerAlert: function(message) {
17 console.log(`SYSTEM ALERT: ${message}`);
18
19 // Notify about all active dinosaurs
20 this.activeDinosaurs.forEach(function(dino) {
21 console.log(`- ${dino.name} (${dino.species}) in sector ${dino.sector}`);
22 });
23 }
24};
25
26// Dinosaur class with a movement method
27class Dinosaur {
28 constructor(name, species, sector) {
29 this.name = name;
30 this.species = species;
31 this.sector = sector;
32 this.status = "calm";
33 }
34
35 move(newSector, monitoringSystem) {
36 // We can pass monitoringSystem as a parameter
37 console.log(`${this.name} is moving from sector ${this.sector} to sector ${newSector}`);
38 this.sector = newSector;
39
40 // Or use call() to invoke a method with the proper context
41 const alertMessage = `Dinosaur ${this.name} entered sector ${newSector}!`;
42 monitoringSystem.triggerAlert.call(monitoringSystem, alertMessage);
43 }
44
45 // Method that changes dinosaur status
46 updateStatus(newStatus) {
47 this.status = newStatus;
48 console.log(`Dinosaur ${this.name} status changed to: ${this.status}`);
49 return this;
50 }
51}
52
53// Initialize system and dinosaurs
54const rex = new Dinosaur("Rex", "Tyrannosaurus", "A");
55const blue = new Dinosaur("Blue", "Velociraptor", "B");
56const delta = new Dinosaur("Delta", "Velociraptor", "B");
57
58// Register dinosaurs in the system
59dinoMonitoringSystem.registerDinosaur(rex);
60dinoMonitoringSystem.registerDinosaur(blue);
61dinoMonitoringSystem.registerDinosaur(delta);
62
63// Move a dinosaur
64blue.move("C", dinoMonitoringSystem);
65
66// Method chaining (thanks to return this)
67rex.updateStatus("agitated").updateStatus("aggressive");
68
69// Using bind() to create a function with predefined 'this'
70const checkRexStatus = function() {
71 console.log(`T-Rex status: ${this.status}`);
72};
73
74const boundCheckStatus = checkRexStatus.bind(rex);
75boundCheckStatus(); // "T-Rex status: aggressive"5. this in Arrow Functions
Arrow functions, introduced in ES6, handle this differently than traditional functions. They do not have their own this; instead, they "inherit" this from the surrounding lexical scope.
1const parkSystem = {
2 name: "Jurassic Park",
3 dinosaurs: ["T-Rex", "Velociraptor", "Triceratops"],
4
5 // Method using an arrow function
6 listDinosaurs: function() {
7 console.log(`${this.name} contains the following dinosaurs:`);
8
9 // Arrow function preserves 'this' from the listDinosaurs scope
10 this.dinosaurs.forEach(dino => {
11 console.log(`- ${dino} in park ${this.name}`);
12 });
13
14 // For comparison - a traditional function would lose 'this' context
15 /*
16 this.dinosaurs.forEach(function(dino) {
17 // 'this' refers to the global object or is undefined in strict mode
18 console.log(`- ${dino} in park ${this.name}`); // this.name would be undefined
19 });
20 */
21 }
22};
23
24parkSystem.listDinosaurs();
25// "Jurassic Park contains the following dinosaurs:"
26// "- T-Rex in park Jurassic Park"
27// "- Velociraptor in park Jurassic Park"
28// "- Triceratops in park Jurassic Park"Arrow functions are especially useful when you want to preserve the this context in nested functions, callbacks, or asynchronous programming.
Practical Example: Monitoring System with Timers
1class DinosaurMonitor {
2 constructor(name, species) {
3 this.name = name;
4 this.species = species;
5 this.status = "Normal";
6 this.lastUpdated = new Date();
7 this.checkInterval = null;
8 }
9
10 // Start monitoring with regular status checks
11 startMonitoring() {
12 console.log(`Starting monitoring for dinosaur: ${this.name}`);
13
14 // Arrow function preserves the 'this' context
15 this.checkInterval = setInterval(() => {
16 this.checkStatus();
17 }, 5000);
18
19 // If we used a regular function, we would lose context:
20 /*
21 this.checkInterval = setInterval(function() {
22 // 'this' here refers to the global object, not to the DinosaurMonitor instance
23 this.checkStatus(); // Error: this.checkStatus is not a function
24 }, 5000);
25 */
26 }
27
28 // Check dinosaur status
29 checkStatus() {
30 const now = new Date();
31 const timeSinceLastUpdate = (now - this.lastUpdated) / 1000; // in seconds
32
33 console.log(`Checking status of dinosaur ${this.name} (${this.species})...`);
34 console.log(`Last updated: ${timeSinceLastUpdate.toFixed(1)} seconds ago`);
35 console.log(`Status: ${this.status}`);
36
37 this.lastUpdated = now;
38
39 // Simulate status change (in a real system, sensor data would go here)
40 if (Math.random() < 0.3) {
41 const possibleStatuses = ["Normal", "Agitated", "Resting", "Feeding", "Aggressive"];
42 this.status = possibleStatuses[Math.floor(Math.random() * possibleStatuses.length)];
43 console.log(`New status: ${this.status}`);
44 }
45 }
46
47 // Stop monitoring
48 stopMonitoring() {
49 if (this.checkInterval) {
50 clearInterval(this.checkInterval);
51 this.checkInterval = null;
52 console.log(`Stopped monitoring dinosaur: ${this.name}`);
53 }
54 }
55}
56
57// Create and start a monitor
58const blueMonitor = new DinosaurMonitor("Blue", "Velociraptor");
59blueMonitor.startMonitoring();
60
61// Stop the monitor after 20 seconds
62setTimeout(() => {
63 blueMonitor.stopMonitoring();
64}, 20000);Common Pitfalls with this
The this keyword can be a source of many bugs in JavaScript. Here are some common pitfalls and how to avoid them:
1. Losing Context in Nested Functions
1const dinosaur = {
2 name: "Rex",
3 behaviors: ["hunting", "resting", "running"],
4
5 logBehaviors: function() {
6 // 'this' here refers to 'dinosaur'
7 console.log(`Dinosaur ${this.name} exhibits the following behaviors:`);
8
9 // Problem: 'this' in the forEach function refers to the global object
10 this.behaviors.forEach(function(behavior) {
11 // 'this.name' is undefined
12 console.log(`- ${this.name} likes ${behavior}`);
13 });
14 }
15};
16
17dinosaur.logBehaviors();
18// "Dinosaur Rex exhibits the following behaviors:"
19// "- undefined likes hunting"
20// "- undefined likes resting"
21// "- undefined likes running"Solutions:
- Use an arrow function:
1const dinosaur = {
2 name: "Rex",
3 behaviors: ["hunting", "resting", "running"],
4
5 logBehaviors: function() {
6 console.log(`Dinosaur ${this.name} exhibits the following behaviors:`);
7
8 // Arrow function preserves 'this' from the surrounding scope
9 this.behaviors.forEach(behavior => {
10 console.log(`- ${this.name} likes ${behavior}`);
11 });
12 }
13};- Store a reference to
thisin a variable:
1const dinosaur = {
2 name: "Rex",
3 behaviors: ["hunting", "resting", "running"],
4
5 logBehaviors: function() {
6 console.log(`Dinosaur ${this.name} exhibits the following behaviors:`);
7
8 // Store reference to 'this' in a variable 'self' or 'that'
9 const self = this;
10 this.behaviors.forEach(function(behavior) {
11 console.log(`- ${self.name} likes ${behavior}`);
12 });
13 }
14};- Use the
bind()method:
1const dinosaur = {
2 name: "Rex",
3 behaviors: ["hunting", "resting", "running"],
4
5 logBehaviors: function() {
6 console.log(`Dinosaur ${this.name} exhibits the following behaviors:`);
7
8 // Using bind() to attach 'this'
9 this.behaviors.forEach(function(behavior) {
10 console.log(`- ${this.name} likes ${behavior}`);
11 }.bind(this));
12 }
13};2. Methods as Callbacks
When an object method is passed as a callback, it loses its this context:
1class DinosaurTracker {
2 constructor(name) {
3 this.name = name;
4 this.tracked = false;
5 }
6
7 startTracking() {
8 this.tracked = true;
9 console.log(`Started tracking dinosaur ${this.name}`);
10 }
11}
12
13const blueTracker = new DinosaurTracker("Blue");
14
15// Works correctly
16blueTracker.startTracking();
17
18// Doesn't work - 'this' does not refer to blueTracker
19setTimeout(blueTracker.startTracking, 1000); // this does not point to blueTrackerSolutions:
- Use an arrow function:
1setTimeout(() => blueTracker.startTracking(), 1000);- Use
bind():
1setTimeout(blueTracker.startTracking.bind(blueTracker), 1000);Advanced Example: Park Management System
Below is a more complex example of a Jurassic Park management system that uses various aspects of the this keyword:
1// Main park management system
2const parkManagementSystem = {
3 parkName: "Jurassic Park",
4 status: "operational",
5 securityLevel: "normal",
6
7 // Park subsystems
8 subsystems: {
9 power: { status: "online", output: "100%" },
10 security: { status: "active", fences: "operational" },
11 visitorCenter: { status: "open", visitors: 120 }
12 },
13
14 // System initialization method
15 initialize: function() {
16 console.log(`Initializing park management system: ${this.parkName}`);
17
18 // Check all subsystems
19 this.checkAllSubsystems();
20
21 // Set up regular monitoring
22 this.startMonitoring();
23
24 return this;
25 },
26
27 // Check subsystem status
28 checkAllSubsystems: function() {
29 console.log("Checking status of all subsystems...");
30
31 // Using Object.entries and arrow function to preserve 'this' context
32 Object.entries(this.subsystems).forEach(([name, system]) => {
33 console.log(`- Subsystem ${name}: ${system.status}`);
34
35 // Call specific checks for each subsystem
36 this[`check${name.charAt(0).toUpperCase() + name.slice(1)}`]();
37 });
38 },
39
40 // Specific check methods for each subsystem
41 checkPower: function() {
42 const powerSystem = this.subsystems.power;
43 console.log(` Checking power: ${powerSystem.status}, Output: ${powerSystem.output}`);
44 },
45
46 checkSecurity: function() {
47 const securitySystem = this.subsystems.security;
48 console.log(` Checking security: ${securitySystem.status}, Fences: ${securitySystem.fences}`);
49 },
50
51 checkVisitorCenter: function() {
52 const visitorSystem = this.subsystems.visitorCenter;
53 console.log(` Checking visitor center: ${visitorSystem.status}, Visitors: ${visitorSystem.visitors}`);
54 },
55
56 // Start regular monitoring
57 startMonitoring: function() {
58 console.log("Starting regular park monitoring...");
59
60 // Arrow function to preserve 'this' context
61 this.monitoringInterval = setInterval(() => {
62 this.checkSecurityStatus();
63 }, 10000);
64
65 // Simulate a method call after 5 seconds
66 setTimeout(() => this.simulateSecurityAlert(), 5000);
67 },
68
69 // Check security status
70 checkSecurityStatus: function() {
71 console.log(`[MONITOR] Park status: ${this.status}, Security level: ${this.securityLevel}`);
72 },
73
74 // Simulate a security alert
75 simulateSecurityAlert: function() {
76 console.log("SIMULATION: Security breach detected in sector B!");
77
78 // Change park status and security level
79 this.status = "alert";
80 this.securityLevel = "high";
81 console.log(`Park status changed to: ${this.status}`);
82 console.log(`Security level raised to: ${this.securityLevel}`);
83
84 // Direct status check
85 this.checkSecurityStatus();
86
87 // Create an emergency response team using a constructor
88 const responseTeam = new EmergencyResponseTeam(this);
89
90 // Pass method as callback while preserving context
91 responseTeam.respondToAlert(this.resolveSecurityAlert.bind(this));
92 },
93
94 // Resolve security alert
95 resolveSecurityAlert: function(report) {
96 console.log(`Received report from response team: ${report}`);
97
98 // Restore normal status
99 this.status = "operational";
100 this.securityLevel = "normal";
101 console.log("Security alert resolved.");
102 console.log(`Park status restored to: ${this.status}`);
103
104 // Check current status
105 this.checkSecurityStatus();
106 },
107
108 // Stop monitoring
109 stopMonitoring: function() {
110 if (this.monitoringInterval) {
111 clearInterval(this.monitoringInterval);
112 console.log("Park monitoring stopped.");
113 }
114 }
115};
116
117// Constructor for the emergency response team
118function EmergencyResponseTeam(park) {
119 // 'this' refers to the new object
120 this.teamName = "Emergency Response Team";
121 this.members = ["Owen Grady", "Claire Dearing", "Dr. Wu"];
122 this.assignedPark = park;
123
124 this.respondToAlert = function(callback) {
125 console.log(`${this.teamName} responding to alert at park: ${this.assignedPark.parkName}`);
126
127 // Simulate response time
128 setTimeout(() => {
129 console.log(`${this.teamName} has secured the area and restored fences.`);
130
131 // Call the callback with a report
132 if (typeof callback === 'function') {
133 callback("Threat neutralized, fences repaired");
134 }
135 }, 3000);
136 };
137}
138
139// Initialize and start the system
140parkManagementSystem.initialize();
141
142// Stop the system after 20 seconds
143setTimeout(() => {
144 parkManagementSystem.stopMonitoring();
145}, 20000);Summary and Best Practices
The this keyword is a powerful tool in JavaScript, but it requires understanding how it behaves in different contexts:
In the global context:
thisiswindowin the browser andmodule.exports({}) in a Node.js file; in a regular function outside strict mode it is the global object (globalThis).In an object method:
thisrefers to the object that called the method.In a constructor:
thisrefers to the newly created object.In arrow functions:
thisis inherited from the surrounding lexical scope.With call/apply/bind:
thisis explicitly specified by the programmer.
Best Practices:
Use arrow functions for callbacks and nested functions to preserve the
thiscontext.Use
call(),apply(), orbind()methods when you need to explicitly control the value ofthis.Avoid creating methods in constructors; instead, add them to the prototype to avoid code duplication.
Remember that
thisis determined when the function is called, not when it is defined.Use ES6 classes instead of traditional constructors for cleaner code.
In the next lesson, we will dive deeper into arrays in JavaScript, which are a key component of managing data collections in our Jurassic Park.
Code for this lesson: index.js
1// The 'this' keyword - Jurassic Park
2console.log("'this' Laboratory in JavaScript");
3console.log("Understanding execution context\n");
4
5// ===========================================
6// 1. 'this' BASICS
7// ===========================================
8console.log("=== 1. What is 'this'? ===");
9
10// 'this' refers to the object in whose context the code is executed
11
12const dinosaur = {
13 name: "Tyrannosaurus Rex",
14 age: 8,
15
16 roar: function() {
17 console.log(`\n${this.name} roars: ROAARRR!`);
18 console.log(` Age: ${this.age} years`);
19 }
20};
21
22dinosaur.roar(); // 'this' points to the 'dinosaur' object
23
24// ===========================================
25// 2. 'this' IN DIFFERENT CONTEXTS
26// ===========================================
27console.log("\n=== 2. 'this' contexts ===");
28
29// Context 1: Object method
30const raptor = {
31 name: "Velociraptor",
32 speed: 40,
33
34 run: function() {
35 console.log(`\n${this.name} running at a speed of ${this.speed} km/h`);
36 }
37};
38
39raptor.run(); // this = raptor
40
41// Context 2: Global function (strict mode)
42function globalFunction() {
43 console.log("\nGlobal function - this:", typeof this);
44 // In strict mode: undefined
45 // Without strict mode: window (browser) or global (Node.js)
46}
47
48globalFunction();
49
50// Context 3: Constructor
51function Dinosaur(name, species) {
52 this.name = name;
53 this.species = species;
54 this.roar = function() {
55 console.log(`${this.name} (${this.species}): ROAARRR!`);
56 };
57}
58
59const trex = new Dinosaur("Rexy", "T-Rex");
60console.log("\nConstructor:");
61console.log(` Name: ${trex.name}`);
62console.log(` Species: ${trex.species}`);
63trex.roar();
64
65// ===========================================
66// 3. LOSS OF 'this' CONTEXT
67// ===========================================
68console.log("\n=== 3. Loss of 'this' context ===");
69
70const park = {
71 name: "Jurassic Park",
72 dinosaurs: ["T-Rex", "Velociraptor", "Triceratops"],
73
74 displayDinosaurs: function() {
75 console.log(`\nDinosaurs in ${this.name}:`);
76
77 // PROBLEM: forEach with a traditional function
78 // this.dinosaurs.forEach(function(dino) {
79 // console.log(` - ${dino} from ${this.name}`); // this.name = undefined!
80 // });
81
82 // SOLUTION 1: Arrow function
83 this.dinosaurs.forEach(dino => {
84 console.log(` - ${dino} from ${this.name}`);
85 });
86 }
87};
88
89park.displayDinosaurs();
90
91// SOLUTION 2: bind()
92const park2 = {
93 name: "Isla Nublar",
94 dinosaurs: ["Stegosaurus", "Brachiosaurus"],
95
96 displayDinosaurs: function() {
97 console.log(`\nDinosaurs in ${this.name}:`);
98
99 this.dinosaurs.forEach(function(dino) {
100 console.log(` - ${dino} from ${this.name}`);
101 }.bind(this)); // Binding this
102 }
103};
104
105park2.displayDinosaurs();
106
107// ===========================================
108// 4. METHODS: call(), apply(), bind()
109// ===========================================
110console.log("\n=== 4. Methods: call(), apply(), bind() ===");
111
112const dinoInfo = {
113 describe: function(habitat, diet) {
114 console.log(`\n${this.name}:`);
115 console.log(` Environment: ${habitat}`);
116 console.log(` Diet: ${diet}`);
117 }
118};
119
120const dino1 = { name: "T-Rex" };
121const dino2 = { name: "Triceratops" };
122
123// call() - a call with an explicit this and arguments
124dinoInfo.describe.call(dino1, "Tropical forest", "Carnivore");
125
126// apply() - like call(), but with the arguments in an array
127dinoInfo.describe.apply(dino2, ["Savanna", "Herbivore"]);
128
129// bind() - creates a new function with bound this
130const describeTRex = dinoInfo.describe.bind(dino1);
131describeTRex("Jungle", "Carnivore");
132
133// ===========================================
134// 5. ARROW FUNCTIONS AND 'this'
135// ===========================================
136console.log("\n=== 5. Arrow functions and 'this' ===");
137
138const enclosure = {
139 id: "A-7",
140 dinosaur: "Velociraptor",
141
142 // Traditional method
143 checkTraditional: function() {
144 console.log(`\nPaddock ${this.id} (traditional):`);
145
146 setTimeout(function() {
147 // console.log(` Dinosaur: ${this.dinosaur}`); // undefined! this has changed
148 console.log(" this.dinosaur = undefined (context lost)");
149 }, 100);
150 },
151
152 // Solution with an arrow function
153 checkArrow: function() {
154 console.log(`\nPaddock ${this.id} (arrow):`);
155
156 setTimeout(() => {
157 console.log(` Dinosaur: ${this.dinosaur}`); // Works! Arrow preserves this
158 }, 100);
159 }
160};
161
162enclosure.checkTraditional();
163
164setTimeout(() => {
165 enclosure.checkArrow();
166}, 150);
167
168// ===========================================
169// 6. 'this' IN CONSTRUCTORS AND CLASSES
170// ===========================================
171console.log("\n=== 6. 'this' in constructors and classes ===");
172
173// Constructor function
174function DinosaurPark(name) {
175 this.name = name;
176 this.dinosaurs = [];
177
178 this.addDinosaur = function(dino) {
179 this.dinosaurs.push(dino);
180 console.log(`Added ${dino} to ${this.name}`);
181 };
182
183 this.getCount = function() {
184 return this.dinosaurs.length;
185 };
186}
187
188const park1 = new DinosaurPark("Jurassic Park");
189park1.addDinosaur("T-Rex");
190park1.addDinosaur("Velociraptor");
191console.log(`Total dinosaurs: ${park1.getCount()}`);
192
193// ES6 Class
194class ModernPark {
195 constructor(name) {
196 this.name = name;
197 this.dinosaurs = [];
198 }
199
200 addDinosaur(dino) {
201 this.dinosaurs.push(dino);
202 console.log(`\nAdded ${dino} to ${this.name}`);
203 }
204
205 getCount() {
206 return this.dinosaurs.length;
207 }
208
209 displayAll() {
210 console.log(`\n${this.name} - Dinosaur list:`);
211 this.dinosaurs.forEach((dino, i) => {
212 console.log(` ${i + 1}. ${dino}`);
213 });
214 }
215}
216
217const park3 = new ModernPark("Isla Nublar");
218park3.addDinosaur("Triceratops");
219park3.addDinosaur("Stegosaurus");
220park3.addDinosaur("Brachiosaurus");
221park3.displayAll();
222console.log(`Total: ${park3.getCount()}`);
223
224// ===========================================
225// 7. PROBLEM: METHOD AS CALLBACK
226// ===========================================
227console.log("\n=== 7. Problem: method as callback ===");
228
229const securitySystem = {
230 level: "HIGH",
231
232 checkSecurity: function() {
233 console.log(`Security level: ${this.level}`);
234 }
235};
236
237// PROBLEM: this will be lost
238setTimeout(securitySystem.checkSecurity, 100); // this.level = undefined
239
240// SOLUTION 1: Arrow function wrapper
241setTimeout(() => {
242 securitySystem.checkSecurity();
243}, 150);
244
245// SOLUTION 2: bind()
246setTimeout(securitySystem.checkSecurity.bind(securitySystem), 200);
247
248// ===========================================
249// 8. COMPLEX EXAMPLE
250// ===========================================
251console.log("\n=== 8. Park management system - complex example ===");
252
253class JurassicParkManagement {
254 constructor(parkName) {
255 this.parkName = parkName;
256 this.dinosaurs = [];
257 this.visitors = 0;
258 this.status = "CLOSED";
259 }
260
261 openPark() {
262 this.status = "OPEN";
263 console.log(`\n${this.parkName} - Park open`);
264 }
265
266 closePark() {
267 this.status = "CLOSED";
268 console.log(`\n${this.parkName} - Park closed`);
269 }
270
271 addDinosaur(name, species, sector) {
272 const dino = { name, species, sector, health: 100 };
273 this.dinosaurs.push(dino);
274 console.log(`Added ${name} (${species}) to sector ${sector}`);
275 }
276
277 addVisitors(count) {
278 this.visitors += count;
279 console.log(`Added ${count} visitors. Total: ${this.visitors}`);
280 }
281
282 checkDinosaur(name) {
283 const dino = this.dinosaurs.find(d => d.name === name);
284 if (dino) {
285 console.log(`\nChecking ${name}:`);
286 console.log(` Species: ${dino.species}`);
287 console.log(` Sector: ${dino.sector}`);
288 console.log(` Health: ${dino.health}%`);
289 } else {
290 console.log(`Dinosaur not found: ${name}`);
291 }
292 }
293
294 getDailyReport() {
295 console.log(`\nDAILY REPORT - ${this.parkName}`);
296 console.log("=".repeat(50));
297 console.log(`Status: ${this.status}`);
298 console.log(`Dinosaurs: ${this.dinosaurs.length}`);
299 console.log(`Visitors: ${this.visitors}`);
300
301 if (this.dinosaurs.length > 0) {
302 console.log("\nDinosaur list:");
303 this.dinosaurs.forEach((dino, i) => {
304 console.log(` ${i + 1}. ${dino.name} (${dino.species}) - Sector ${dino.sector}`);
305 });
306 }
307 }
308
309 // Method using setTimeout with an arrow function
310 scheduleFeeding(dinoName, delay = 1000) {
311 console.log(`\nâ° Feeding scheduled for ${dinoName} in ${delay}ms`);
312
313 setTimeout(() => {
314 const dino = this.dinosaurs.find(d => d.name === dinoName);
315 if (dino) {
316 console.log(`Feeding ${dinoName} in ${this.parkName}`);
317 dino.health = Math.min(100, dino.health + 10);
318 }
319 }, delay);
320 }
321}
322
323// System demonstration
324const management = new JurassicParkManagement("Isla Nublar");
325
326management.openPark();
327management.addDinosaur("Rexy", "Tyrannosaurus Rex", "A");
328management.addDinosaur("Blue", "Velociraptor", "B");
329management.addDinosaur("Trike", "Triceratops", "C");
330
331management.addVisitors(500);
332management.addVisitors(300);
333
334management.checkDinosaur("Blue");
335management.getDailyReport();
336
337management.scheduleFeeding("Rexy", 500);
338management.scheduleFeeding("Blue", 600);
339
340setTimeout(() => {
341 console.log("\n--- After feeding ---");
342 management.checkDinosaur("Rexy");
343 management.checkDinosaur("Blue");
344
345 management.closePark();
346
347 console.log("\nCongratulations! You've mastered the 'this' keyword!");
348}, 800);Spotted a mistake in this lesson?
Check yourself
Answer the questions from this lesson. Pick an answer to see right away whether it is correct.
1. What does the 'this' keyword mean in JavaScript?
2. What does 'this' point to inside an object method in JavaScript?
3. How does 'this' behave in arrow functions?
4. In what context does 'this' point to the newly created object?