In Jurassic Park, different security zones have different access levels - some information is available park-wide, some only in a specific zone, some only in a single secure enclosure. JavaScript has a similar concept: scope determines where a variable is accessible.
Variables declared outside any function or block are in global scope - accessible from anywhere:
1// Global scope - accessible everywhere
2const parkName = 'Jurassic Park';
3let totalDinosaurs = 150;
4
5function checkPark() {
6 console.log(parkName); // "Jurassic Park" - accessible!
7 console.log(totalDinosaurs); // 150 - accessible!
8}
9
10checkPark();
11console.log(parkName); // also accessible hereVariables declared inside a function are available only within that function:
1function analyzeDanger() {
2 const dangerLevel = 'HIGH'; // only inside this function
3 let escapedCount = 3; // only inside this function
4
5 console.log(dangerLevel); // "HIGH" - accessible
6 console.log(escapedCount); // 3 - accessible
7}
8
9analyzeDanger();
10// console.log(dangerLevel); // ReferenceError: dangerLevel is not defined
11// console.log(escapedCount); // ReferenceError: escapedCount is not definedlet and const are block-scoped - accessible only within the block (inside {}) where they were declared:1if (true) {
2 const blockVar = 'I exist only in this block';
3 let anotherBlock = 'me too';
4 var oldStyle = 'I escape from the block!'; // var is NOT block-scoped!
5}
6
7// console.log(blockVar); // ReferenceError!
8// console.log(anotherBlock); // ReferenceError!
9console.log(oldStyle); // "I escape from the block!" - var ignores blocks1// var - function scope (ignores blocks)
2function varExample() {
3 for (var i = 0; i < 3; i++) {
4 var innerVar = i; // function-scoped!
5 }
6 console.log(i); // 3 - accessible! (leaked from loop)
7 console.log(innerVar); // 2 - accessible! (leaked from loop)
8}
9
10// let/const - block scope
11function letExample() {
12 for (let j = 0; j < 3; j++) {
13 let innerLet = j; // block-scoped!
14 }
15 // console.log(j); // ReferenceError!
16 // console.log(innerLet); // ReferenceError!
17}Functions "remember" the scope where they were defined - not where they're called. This is called lexical scope:
1const zone = 'Global Zone';
2
3function outerZone() {
4 const zone = 'Outer Zone';
5
6 function innerZone() {
7 const zone = 'Inner Zone';
8 console.log(zone); // "Inner Zone" - closest scope first
9 }
10
11 function anotherInner() {
12 // No local `zone` - goes up the scope chain
13 console.log(zone); // "Outer Zone" - from outerZone
14 }
15
16 innerZone();
17 anotherInner();
18}
19
20outerZone();
21console.log(zone); // "Global Zone"When a variable isn't found in the current scope, JavaScript looks in parent scopes - this is the scope chain:
1const parkSecurity = 'Maximum'; // global
2
3function securityZone() {
4 const zoneLevel = 'High'; // securityZone scope
5
6 function enclosure() {
7 const enclosureStatus = 'Locked'; // enclosure scope
8
9 // Accesses variables from all parent scopes!
10 console.log(enclosureStatus); // "Locked" - own scope
11 console.log(zoneLevel); // "High" - parent scope
12 console.log(parkSecurity); // "Maximum" - global scope
13 }
14
15 enclosure();
16 // console.log(enclosureStatus); // ReferenceError - can't go down!
17}A closure occurs when a function "remembers" variables from its outer scope, even after that outer function has finished executing:
1function createCounter(startValue = 0) {
2 let count = startValue; // this variable "lives" with the closure
3
4 return {
5 increment() { count++; return count; },
6 decrement() { count--; return count; },
7 getValue() { return count; }
8 };
9}
10
11const dinoCounter = createCounter(10);
12console.log(dinoCounter.increment()); // 11
13console.log(dinoCounter.increment()); // 12
14console.log(dinoCounter.decrement()); // 11
15console.log(dinoCounter.getValue()); // 11
16
17// Each counter has its own independent `count`!
18const raptorCounter = createCounter(5);
19console.log(raptorCounter.increment()); // 6
20console.log(dinoCounter.getValue()); // 11 - unchanged!IIFE creates a private scope to protect variables from the global namespace:
1// Park management system isolated in its own scope
2const parkSystem = (function() {
3 // Private variables - not accessible from outside
4 let _alertLevel = 0;
5 let _incidents = [];
6
7 // Private functions
8 function _logIncident(incident) {
9 _incidents.push({ ...incident, timestamp: new Date() });
10 }
11
12 // Public API
13 return {
14 raiseAlert(level, description) {
15 _alertLevel = Math.max(_alertLevel, level);
16 _logIncident({ level, description });
17 console.log(`Alert level: ${_alertLevel} - ${description}`);
18 },
19
20 getStatus() {
21 return {
22 alertLevel: _alertLevel,
23 incidentCount: _incidents.length
24 };
25 }
26 };
27})(); // Immediately invoked!
28
29parkSystem.raiseAlert(3, 'Raptor escaped');
30parkSystem.raiseAlert(5, 'T-Rex fence breach');
31console.log(parkSystem.getStatus());
32// { alertLevel: 5, incidentCount: 2 }
33
34// console.log(_alertLevel); // ReferenceError - private!Now let's better understand the differences between these three keywords:
var:undefined valuelet:const:1function demonstrateVariables() {
2 // var
3 var dinosaur = "T-Rex";
4 var dinosaur = "Velociraptor"; // OK - can redeclare
5 dinosaur = "Triceratops"; // OK - can reassign
6
7 // let
8 let park = "Jurassic Park";
9 // let park = "Dino World"; // Error - cannot redeclare
10 park = "Jurassic World"; // OK - can reassign
11
12 // const
13 const island = "Isla Nublar";
14 // const island = "Isla Sorna"; // Error - cannot redeclare
15 // island = "Another Island"; // Error - cannot reassign
16
17 // But properties of const objects CAN be modified
18 const parkSystem = {
19 mainPower: true,
20 backupGenerator: false
21 };
22
23 // This is allowed
24 parkSystem.mainPower = false;
25 parkSystem.backupGenerator = true;
26
27 console.log(parkSystem); // { mainPower: false, backupGenerator: true }
28}
29
30demonstrateVariables();Avoid global variables - just like in a real park, the more public areas there are, the harder they are to monitor and secure.
Prefer
over const
, and let
over let
- most variables should not change their values, and if they do, they should have the smallest possible scope.var
Use block scope - limit variable visibility to the smallest possible scope.
Use the module pattern - encapsulate variables and functions in modules, exposing only the necessary API.
Be aware of closures - closures are a powerful tool, but can lead to memory leaks if used improperly.
"Variable scope in JavaScript is like security clearance levels in Jurassic Park" - says Dr. Rex. "A ranger in zone A can see everything in that zone, but can also access global park information. A visitor can only see public areas. The important thing is that information doesn't flow upward - inner zones can't see outer zone details!"