We use cookies to enhance your experience on the site
CodeWorlds

Prototypes and Inheritance

In Jurassic Park, efficiently managing different dinosaur species requires understanding the similarities between them and the ability to inherit traits. JavaScript uses prototypes to implement an inheritance mechanism — it is one of the most unique and powerful aspects of the language. Understanding prototypes and prototypal inheritance is key to writing efficient, flexible JavaScript code.

What Are Prototypes?

In JavaScript, every object has an internal property called a prototype, which is a reference to another object. When we try to access a property of an object, JavaScript first checks whether the object itself has such a property. If not, it checks the object's prototype, then the prototype's prototype, and so on, forming the so-called prototype chain.

1// Creating a simple object
2const dinosaur = {
3  species: "Tyrannosaurus",
4  makesSound: true,
5  roar() {
6    return "ROOOOAR!";
7  }
8};
9
10// Creating a new object that inherits from dinosaur
11const rex = Object.create(dinosaur);
12rex.name = "Rex";
13rex.age = 8;
14
15// rex has access to its own properties
16console.log(rex.name); // "Rex"
17console.log(rex.age); // 8
18
19// rex also has access to dinosaur's properties and methods
20console.log(rex.species); // "Tyrannosaurus"
21console.log(rex.makesSound); // true
22console.log(rex.roar()); // "ROOOOAR!"
23
24// Let's check if rex has its own 'species' property (it doesn't)
25console.log(rex.hasOwnProperty('species')); // false
26
27// But it does have its own 'name' property
28console.log(rex.hasOwnProperty('name')); // true

In the example above,

rex
does not have its own
species
property, but accessing
rex.species
works because JavaScript looks for this property in the prototype of
rex
, which is the
dinosaur
object.

The Prototype Chain

Prototypes form a chain that JavaScript traverses when we try to access a property. This mechanism is called the prototype chain.

Every object in JavaScript has an internal link to its prototype (

__proto__
or via
Object.getPrototypeOf
):

1const dinosaur = {
2  breathe() { return `${this.name} breathes`; },
3  sleep() { return `${this.name} sleeps`; }
4};
5
6const rex = Object.create(dinosaur);
7rex.name = 'Rex';
8rex.species = 'T-Rex';
9
10console.log(rex.breathe()); // "Rex breathes" - from prototype!
11console.log(rex.sleep());   // "Rex sleeps" - from prototype!
12console.log(rex.hasOwnProperty('name'));    // true - own property
13console.log(rex.hasOwnProperty('breathe')); // false - inherited!
14
15// Checking prototype
16console.log(Object.getPrototypeOf(rex) === dinosaur); // true

Constructor Functions and Prototypes

Methods on the prototype are shared between all instances (more efficient than in-constructor methods):

1function Dinosaur(name, species) {
2  this.name = name;
3  this.species = species;
4  this.health = 100;
5}
6
7// Methods on prototype - shared by all instances
8Dinosaur.prototype.roar = function() {
9  return `${this.name} roars!`;
10};
11
12Dinosaur.prototype.eat = function(food) {
13  return `${this.name} eats ${food}`;
14};
15
16const rex = new Dinosaur('Rex', 'T-Rex');
17const blue = new Dinosaur('Blue', 'Velociraptor');
18
19// Both share the SAME roar function (not two copies)
20console.log(rex.roar === blue.roar); // true - same function!
21console.log(rex.roar()); // "Rex roars!"
22console.log(blue.roar()); // "Blue roars!"

In this example, the

roar
and
eat
methods are defined on the prototype of the
Dinosaur
constructor function. All instances created with
new Dinosaur()
share the same methods, which is much more efficient than defining those methods in the constructor for each instance separately.

Checking an Object's Prototype

We can check an object's prototype using several methods:

1// Using Object.getPrototypeOf()
2console.log(Object.getPrototypeOf(rex) === Dinosaur.prototype); // true
3
4// Using __proto__ (deprecated property, not recommended in production code)
5console.log(rex.__proto__ === Dinosaur.prototype); // true
6
7// Checking if an object is an instance of a constructor
8console.log(rex instanceof Dinosaur); // true
9console.log(rex instanceof Object); // true (all objects inherit from Object.prototype)

Inheritance Between Constructors

We can implement inheritance between constructor functions by linking their prototypes:

1// Constructor function for Animal (base class)
2function Animal(name) {
3  this.name = name;
4  this.isAlive = true;
5}
6
7Animal.prototype.eat = function(food) {
8  return `${this.name} eats ${food}.`;
9};
10
11Animal.prototype.sleep = function() {
12  return `${this.name} sleeps.`;
13};
14
15// Constructor function for Dinosaur (derived class)
16function Dinosaur(name, species, diet) {
17  // Call the base class constructor
18  Animal.call(this, name);
19
20  // Add Dinosaur-specific properties
21  this.species = species;
22  this.diet = diet;
23  this.isExtinct = false;
24}
25
26// Set Dinosaur's prototype to inherit from Animal.prototype
27Dinosaur.prototype = Object.create(Animal.prototype);
28
29// Restore the correct constructor property
30Dinosaur.prototype.constructor = Dinosaur;
31
32// Add Dinosaur-specific methods
33Dinosaur.prototype.roar = function() {
34  const volume = this.diet === "carnivore" ? "loudly" : "softly";
35  return `${this.name} roars ${volume}!`;
36};
37
38// Create a Dinosaur instance
39const velociraptor = new Dinosaur("Blue", "Velociraptor", "carnivore");
40
41// We have access to Animal methods
42console.log(velociraptor.eat("meat")); // "Blue eats meat."
43console.log(velociraptor.sleep()); // "Blue sleeps."
44
45// And to Dinosaur-specific methods
46console.log(velociraptor.roar()); // "Blue roars loudly!"
47
48// Checking the prototype chain
49console.log(velociraptor instanceof Dinosaur); // true
50console.log(velociraptor instanceof Animal); // true
51console.log(velociraptor instanceof Object); // true

This pattern of implementing inheritance between constructors is often called "pseudo-classical inheritance" because it emulates class-based inheritance in a language that originally relies on prototypes.

Multi-Level Inheritance with Prototypes

1// Base type
2function Creature(name) {
3  this.name = name;
4  this.alive = true;
5}
6Creature.prototype.breathe = function() {
7  return `${this.name} breathes`;
8};
9
10// Dinosaur extends Creature
11function DinosaurOld(name, species) {
12  Creature.call(this, name); // call parent constructor
13  this.species = species;
14}
15// Set up prototype chain
16DinosaurOld.prototype = Object.create(Creature.prototype);
17DinosaurOld.prototype.constructor = DinosaurOld;
18
19DinosaurOld.prototype.roar = function() {
20  return `${this.name} roars!`;
21};
22
23// Carnivore extends Dinosaur
24function Carnivore(name, species, huntingRange) {
25  DinosaurOld.call(this, name, species);
26  this.huntingRange = huntingRange;
27}
28Carnivore.prototype = Object.create(DinosaurOld.prototype);
29Carnivore.prototype.constructor = Carnivore;
30
31Carnivore.prototype.hunt = function() {
32  return `${this.name} hunts in a ${this.huntingRange}km range!`;
33};
34
35const carnivore = new Carnivore('Rex', 'T-Rex', 5);
36console.log(carnivore.breathe()); // "Rex breathes" - from Creature
37console.log(carnivore.roar());    // "Rex roars!" - from Dinosaur
38console.log(carnivore.hunt());    // "Rex hunts in a 5km range!" - own
39
40console.log(carnivore instanceof Carnivore); // true
41console.log(carnivore instanceof DinosaurOld); // true
42console.log(carnivore instanceof Creature);  // true

ES6 Classes - Clean Syntax

Classes in ES6 are syntactic sugar over the prototype system - cleaner, but equivalent:

1class Creature {
2  constructor(name) {
3    this.name = name;
4    this.alive = true;
5  }
6
7  breathe() {
8    return `${this.name} breathes`;
9  }
10
11  toString() {
12    return `[Creature: ${this.name}]`;
13  }
14}
15
16class Dinosaur extends Creature {
17  constructor(name, species) {
18    super(name); // calls Creature constructor
19    this.species = species;
20    this.health = 100;
21  }
22
23  roar() {
24    return `${this.name} (${this.species}) roars!`;
25  }
26
27  // Override parent method
28  toString() {
29    return `[Dinosaur: ${this.name} (${this.species})]`;
30  }
31}
32
33class Carnivore extends Dinosaur {
34  constructor(name, species, huntingRange) {
35    super(name, species);
36    this.huntingRange = huntingRange;
37    this.diet = 'carnivore';
38  }
39
40  hunt(prey) {
41    return `${this.name} hunts ${prey} in ${this.huntingRange}km range!`;
42  }
43}
44
45class Herbivore extends Dinosaur {
46  constructor(name, species, favoriteFood) {
47    super(name, species);
48    this.favoriteFood = favoriteFood;
49    this.diet = 'herbivore';
50  }
51
52  graze() {
53    return `${this.name} grazes on ${this.favoriteFood}`;
54  }
55}
56
57class Tyrannosaurus extends Carnivore {
58  constructor(name) {
59    super(name, 'T-Rex', 5);
60    this.biteForce = 57000; // Newtons
61  }
62
63  stomp() {
64    return `${this.name} stomps the ground - everything shakes!`;
65  }
66}
67
68class Velociraptor extends Carnivore {
69  constructor(name) {
70    super(name, 'Velociraptor', 3);
71    this.intelligence = 'HIGH';
72    this.packHunter = true;
73  }
74
75  coordinateAttack(packMembers) {
76    return `${this.name} coordinates attack with: ${packMembers.join(', ')}`;
77  }
78}
79
80class Triceratops extends Herbivore {
81  constructor(name) {
82    super(name, 'Triceratops', 'ferns');
83    this.hornLength = 1; // meters
84  }
85
86  chargeAttack() {
87    return `${this.name} charges with ${this.hornLength}m horns!`;
88  }
89}
90
91class Brachiosaurus extends Herbivore {
92  constructor(name) {
93    super(name, 'Brachiosaurus', 'treetops');
94    this.neckLength = 9; // meters
95  }
96
97  eatTrees() {
98    return `${this.name} reaches ${this.neckLength}m up to eat from treetops`;
99  }
100}
101
102// Full Jurassic Park simulation
103const rex = new Tyrannosaurus('Rex');
104const blue = new Velociraptor('Blue');
105const delta = new Velociraptor('Delta');
106const trike = new Triceratops('Trike');
107const brachie = new Brachiosaurus('Brachie');
108
109console.log(rex.breathe());  // "Rex breathes" - from Creature
110console.log(rex.roar());     // "Rex (T-Rex) roars!" - from Dinosaur
111console.log(rex.hunt('Trike')); // "Rex hunts Trike in 5km range!" - from Carnivore
112console.log(rex.stomp());       // own method
113
114console.log(blue.coordinateAttack(['Delta', 'Echo', 'Charlie']));
115
116// instanceof chain
117console.log(rex instanceof Tyrannosaurus); // true
118console.log(rex instanceof Carnivore);     // true
119console.log(rex instanceof Dinosaur);      // true
120console.log(rex instanceof Creature);      // true

Mixins

JavaScript doesn't support multiple inheritance, but mixins let you mix in functionality from multiple sources:

1// Mixin - a set of methods to add to a class
2const SwimMixin = {
3  swim(speed) {
4    return `${this.name} swims at ${speed} km/h!`;
5  },
6  dive(depth) {
7    return `${this.name} dives to ${depth} meters!`;
8  }
9};
10
11const FlyMixin = {
12  fly(altitude) {
13    return `${this.name} flies at ${altitude}m altitude!`;
14  },
15  land() {
16    return `${this.name} lands!`;
17  }
18};
19
20// Apply mixins to a class
21class Pteranodon extends Dinosaur {
22  constructor(name) {
23    super(name, 'Pteranodon');
24    this.wingspan = 7; // meters
25  }
26}
27
28// Apply mixins
29Object.assign(Pteranodon.prototype, FlyMixin, SwimMixin);
30
31const ptero = new Pteranodon('Ptero');
32console.log(ptero.fly(200));   // "Ptero flies at 200m altitude!"
33console.log(ptero.dive(10));   // "Ptero dives to 10 meters!"
34console.log(ptero.roar());     // "Ptero (Pteranodon) roars!" - from Dinosaur

Object.prototype - Universal Methods

All objects inherit from

Object.prototype
:

1const dino = { name: 'Rex', species: 'T-Rex' };
2
3// Methods from Object.prototype
4console.log(dino.hasOwnProperty('name'));    // true
5console.log(dino.hasOwnProperty('breathe')); // false
6
7console.log(dino.toString()); // "[object Object]" - can be overridden
8
9// Object static methods
10console.log(Object.keys(dino));    // ['name', 'species']
11console.log(Object.values(dino));  // ['Rex', 'T-Rex']
12console.log(Object.entries(dino)); // [['name', 'Rex'], ['species', 'T-Rex']]
13
14const copy = Object.assign({}, dino, { health: 100 });
15const spread = { ...dino, health: 100 };

Prototypal Inheritance in Practice: Jurassic Park Dinosaur System

Below is a more complex example of a dinosaur management system using prototypal inheritance:

1// Base class for all creatures in the park
2class Creature {
3  constructor(name, age) {
4    this.name = name;
5    this.age = age;
6    this.health = 100;
7    this.alive = true;
8  }
9
10  eat(food) {
11    this.health = Math.min(100, this.health + 10);
12    return `${this.name} eats ${food}. Health: ${this.health}%`;
13  }
14
15  sleep() {
16    this.health = Math.min(100, this.health + 5);
17    return `${this.name} sleeps. Health: ${this.health}%`;
18  }
19
20  damage(amount) {
21    this.health = Math.max(0, this.health - amount);
22    if (this.health === 0 && this.alive) {
23      this.alive = false;
24      return `${this.name} is dead!`;
25    }
26    return `${this.name} takes ${amount} damage. Remaining health: ${this.health}%`;
27  }
28}
29
30// Class for dinosaurs, inheriting from Creature
31class Dinosaur extends Creature {
32  constructor(name, species, age, diet) {
33    super(name, age);
34    this.species = species;
35    this.diet = diet;
36    this.containmentLevel = this.calculateContainmentLevel();
37  }
38
39  calculateContainmentLevel() {
40    if (this.diet === "carnivore") {
41      return this.age > 5 ? "Maximum" : "High";
42    } else {
43      return this.age > 10 ? "Medium" : "Standard";
44    }
45  }
46
47  roar() {
48    const volume = this.diet === "carnivore" ? "loudly" : "softly";
49    return `${this.name} roars ${volume}!`;
50  }
51
52  getStatus() {
53    return `
54Dinosaur: ${this.name} (${this.species})
55Age: ${this.age} years
56Diet: ${this.diet === "carnivore" ? "Carnivore" : "Herbivore"}
57Health: ${this.health}%
58Status: ${this.alive ? "Alive" : "Dead"}
59Containment level: ${this.containmentLevel}
60`;
61  }
62}
63
64// Class for carnivorous dinosaurs (predators)
65class Carnivore extends Dinosaur {
66  constructor(name, species, age, attackPower) {
67    super(name, species, age, "carnivore");
68    this.attackPower = attackPower;
69    this.huntingSuccess = 0.7; // 70% chance of successful hunt
70  }
71
72  hunt(prey) {
73    if (!this.alive) return `${this.name} is dead, cannot hunt.`;
74    if (!prey.alive) return `${prey.name} is already dead.`;
75
76    const huntSuccess = Math.random() < this.huntingSuccess;
77
78    if (huntSuccess) {
79      const damage = Math.floor(this.attackPower * (0.7 + Math.random() * 0.6));
80      const result = prey.damage(damage);
81      this.eat("meat");
82      return `${this.name} successfully hunts ${prey.name}! ${result}`;
83    } else {
84      return `${this.name} tries to hunt ${prey.name}, but fails.`;
85    }
86  }
87
88  biteAttack(target) {
89    if (!this.alive) return `${this.name} is dead, cannot attack.`;
90    if (!target.alive) return `${target.name} is already dead.`;
91
92    const damage = Math.floor(this.attackPower * 1.2);
93    const result = target.damage(damage);
94    return `${this.name} bites ${target.name}! ${result}`;
95  }
96}
97
98// Class for herbivorous dinosaurs
99class Herbivore extends Dinosaur {
100  constructor(name, species, age, defenseRating) {
101    super(name, species, age, "herbivore");
102    this.defenseRating = defenseRating;
103    this.fleeSuccess = 0.6; // 60% chance of successful escape
104  }
105
106  grazeVegetation() {
107    if (!this.alive) return `${this.name} is dead, cannot eat.`;
108
109    this.health = Math.min(100, this.health + 15);
110    return `${this.name} grazes on plants. Health rises to ${this.health}%.`;
111  }
112
113  flee(predator) {
114    if (!this.alive) return `${this.name} is dead, cannot flee.`;
115
116    const escapeSuccess = Math.random() < this.fleeSuccess;
117
118    if (escapeSuccess) {
119      this.health = Math.max(0, this.health - 5);
120      return `${this.name} successfully flees from ${predator.name}!`;
121    } else {
122      return `${this.name} tries to flee from ${predator.name}, but fails.`;
123    }
124  }
125
126  // Defensive method — reduces incoming damage
127  damage(amount) {
128    const reducedDamage = Math.max(0, amount - this.defenseRating);
129    return super.damage(reducedDamage);
130  }
131}
132
133// Specific dinosaur species
134class Tyrannosaurus extends Carnivore {
135  constructor(name, age) {
136    super(name, "Tyrannosaurus Rex", age, 35);
137    this.intimidationFactor = 9;
138  }
139
140  intimidate(target) {
141    return `${this.name} lets out a deafening roar that terrifies ${target.name}!`;
142  }
143
144  roar() {
145    return `${this.name} lets out a mighty, deafening roar that echoes for miles!`;
146  }
147}
148
149class Velociraptor extends Carnivore {
150  constructor(name, age) {
151    super(name, "Velociraptor", age, 20);
152    this.speed = 70; // km/h
153    this.intelligence = 9;
154    this.packHunting = true;
155  }
156
157  coordinateAttack(targets) {
158    if (!Array.isArray(targets) || targets.length < 2) {
159      return `${this.name} needs at least two targets to coordinate an attack.`;
160    }
161
162    let results = `${this.name} coordinates a pack attack on ${targets.map(t => t.name).join(', ')}!
163`;
164    targets.forEach(target => {
165      const damage = Math.floor(this.attackPower * 1.5);
166      results += target.damage(damage) + '\n';
167    });
168
169    return results;
170  }
171
172  ambush(target) {
173    const damage = Math.floor(this.attackPower * 2);
174    const result = target.damage(damage);
175    return `${this.name} ambushes ${target.name}! ${result}`;
176  }
177}
178
179class Triceratops extends Herbivore {
180  constructor(name, age) {
181    super(name, "Triceratops", age, 25);
182    this.hornStrength = 30;
183  }
184
185  chargeAttack(target) {
186    const damage = this.hornStrength + Math.floor(Math.random() * 10);
187    const result = target.damage(damage);
188    return `${this.name} charges at ${target.name} with its horns! ${result}`;
189  }
190
191  defendFromAttack(attacker) {
192    const counterDamage = Math.floor(this.hornStrength * 0.5);
193    const result = attacker.damage(counterDamage);
194    return `${this.name} defends itself from ${attacker.name} using its horns! ${result}`;
195  }
196}
197
198class Brachiosaurus extends Herbivore {
199  constructor(name, age) {
200    super(name, "Brachiosaurus", age, 15);
201    this.height = 13; // meters
202    this.tailLength = 9; // meters
203  }
204
205  reachHighVegetation() {
206    this.health = Math.min(100, this.health + 20);
207    return `${this.name} reaches up with its long neck to eat leaves from tall trees. Health rises to ${this.health}%.`;
208  }
209
210  tailWhip(target) {
211    const damage = Math.floor(this.tailLength * 2);
212    const result = target.damage(damage);
213    return `${this.name} strikes ${target.name} with its massive tail! ${result}`;
214  }
215}
216
217// Jurassic Park simulation
218function runJurassicParkSimulation() {
219  console.log("=== JURASSIC PARK SIMULATION ===
220");
221
222  // Creating dinosaurs
223  const rex = new Tyrannosaurus("Rex", 8);
224  const blue = new Velociraptor("Blue", 4);
225  const delta = new Velociraptor("Delta", 4);
226  const trixie = new Triceratops("Trixie", 12);
227  const bronty = new Brachiosaurus("Bronty", 25);
228
229  // Display dinosaur info
230  console.log(rex.getStatus());
231  console.log(blue.getStatus());
232  console.log(trixie.getStatus());
233  console.log(bronty.getStatus());
234
235  console.log("
236=== DINOSAUR INTERACTIONS ===
237");
238
239  console.log(rex.roar());
240  console.log(blue.roar());
241  console.log(trixie.grazeVegetation());
242  console.log(bronty.reachHighVegetation());
243
244  console.log("
245=== BATTLE: REX VS TRIXIE ===
246");
247
248  console.log(rex.intimidate(trixie));
249  console.log(trixie.flee(rex));
250  console.log(rex.hunt(trixie));
251  console.log(trixie.defendFromAttack(rex));
252  console.log(rex.biteAttack(trixie));
253
254  console.log("
255=== VELOCIRAPTOR PACK HUNT ===
256");
257
258  console.log(blue.coordinateAttack([trixie, bronty]));
259  console.log(delta.ambush(bronty));
260  console.log(bronty.tailWhip(delta));
261
262  console.log("
263=== DINOSAUR STATUS AFTER INTERACTIONS ===
264");
265
266  console.log(rex.getStatus());
267  console.log(blue.getStatus());
268  console.log(delta.getStatus());
269  console.log(trixie.getStatus());
270  console.log(bronty.getStatus());
271}
272
273// Run the simulation
274runJurassicParkSimulation();

Prototypal vs Classical Inheritance

Prototypal inheritance in JavaScript differs from classical inheritance in other OOP languages:

  1. Dynamic - prototypes can be modified at runtime, whereas classes are usually static
  2. Prototype chain - properties are looked up through the prototype chain, while in classical inheritance methods are resolved through the class hierarchy
  3. No classes (before ES6) - JavaScript traditionally had no formal class concept, only constructor functions and prototypes
  4. Single inheritance - JavaScript supports only single inheritance (each object has only one prototype), while some languages support multiple inheritance

Best Practices with Prototypes

  1. Understand the mechanism - understanding how the prototype chain works is essential for effective inheritance usage
  2. Use classes (ES6+) - for most use cases, class syntax is more readable and easier to maintain
  3. Avoid modifying built-in prototypes - modifying prototypes of built-in objects (e.g., Object.prototype, Array.prototype) can lead to unexpected conflicts
  4. Use Object.create() for inheritance - when using the traditional approach, Object.create() is a clean way to implement prototypal inheritance
  5. Composition over inheritance - sometimes it's better to use composition (combining objects) rather than deep inheritance to avoid hierarchy problems

Deno and Bun - Modern Alternatives to Node.js

Just as Jurassic Park experimented with different cloning methods, the JavaScript world is also evolving, offering new server-side runtimes. Besides Node.js, which has been the de facto standard for years, interesting alternatives have emerged: Deno and Bun.

Deno - A Safer JavaScript Future

Origins of Deno

Deno was created by Ryan Dahl - the same developer who created Node.js! In 2018, during a presentation titled "10 Things I Regret About Node.js," Ryan presented his vision of a better runtime for JavaScript and TypeScript. It's like Dr. Hammond in Jurassic Park, who after initial failures tried to create a more perfect, safer solution.

Key Differences Between Deno and Node.js

1. Native TypeScript

Deno supports TypeScript out of the box - no extra configuration or transpilation needed:

1// dinosaur.ts
2interface Dinosaur {
3  name: string;
4  species: string;
5  age: number;
6  dangerous: boolean;
7}
8
9function createDinosaur(name: string, species: string): Dinosaur {
10  return {
11    name,
12    species,
13    age: 0,
14    dangerous: species === "Tyrannosaurus Rex"
15  };
16}
17
18const rex = createDinosaur("Rexy", "Tyrannosaurus Rex");
19console.log(rex);

You run it directly:

1deno run dinosaur.ts

2. Permission System

Deno's biggest innovation is its permission system. In Node.js, every script has full access to the filesystem, network, and environment variables. Deno requires explicit permissions - like the security systems in Jurassic Park that control access to different zones:

1# No permissions - script can't do anything
2deno run script.ts
3
4# With network access
5deno run --allow-net script.ts
6
7# With file access in a specific directory
8deno run --allow-read=/data --allow-write=/data script.ts
9
10# With environment variable access
11deno run --allow-env script.ts
12
13# All permissions (use carefully!)
14deno run --allow-all script.ts

Example with the park security system:

1// security-monitor.ts
2// This script monitors security status and writes logs
3
4// Attempting to read sensors without permissions
5try {
6  const sensors = await fetch("http://park-sensors.internal/status");
7  const data = await sensors.json();
8  console.log("Sensor status:", data);
9} catch (error) {
10  console.error("No network permission:", error.message);
11}
12
13// Attempting to write logs without permissions
14try {
15  await Deno.writeTextFile("./logs/security.log", "System operational
16");
17} catch (error) {
18  console.error("No write permission:", error.message);
19}

Running:

1# This will fail — no permissions
2deno run security-monitor.ts
3
4# This will work
5deno run --allow-net --allow-write=./logs security-monitor.ts

3. No node_modules

Deno doesn't use

node_modules
. Instead, it imports modules directly from URLs and caches them locally:

1// Import from URL
2import { serve } from "https://deno.land/std@0.200.0/http/server.ts";
3
4// HTTP server serving dinosaur data
5const handler = (req: Request): Response => {
6  const dinosaurs = [
7    { name: "Rexy", species: "T-Rex" },
8    { name: "Blue", species: "Velociraptor" }
9  ];
10
11  return new Response(JSON.stringify(dinosaurs), {
12    headers: { "content-type": "application/json" }
13  });
14};
15
16serve(handler, { port: 8000 });

4. Standard Library

Deno has a well-designed standard library, similar to Go or Python. You don't need to install dozens of packages for basic operations:

1// Working with files
2import { readLines } from "https://deno.land/std@0.200.0/io/mod.ts";
3
4// Reading dinosaur logs line by line
5const file = await Deno.open("dinosaur-logs.txt");
6for await (const line of readLines(file)) {
7  console.log("Log entry:", line);
8}
9file.close();
10
11// Working with UUID
12import { v4 } from "https://deno.land/std@0.200.0/uuid/mod.ts";
13
14const dinosaurId = v4.generate();
15console.log("New dinosaur ID:", dinosaurId);

5. Built-in Tools

Deno has built-in tools that would require additional packages in Node.js:

1# Code formatting
2deno fmt script.ts
3
4# Linting
5deno lint script.ts
6
7# Tests
8deno test tests/
9
10# Bundling
11deno bundle main.ts output.js
12
13# Documentation
14deno doc module.ts

When to Consider Using Deno?

Use Deno when:

  • Starting a new project without legacy Node.js code
  • Security is a priority (permission system)
  • You want TypeScript without extra configuration
  • You prefer a simpler architecture without node_modules
  • Building CLI tools or automation scripts

Stick with Node.js when:

  • You have a large existing Node.js project
  • You need specific npm packages not compatible with Deno
  • Working in a team that only knows Node.js
  • Using frameworks tightly coupled with Node.js

Bun - The Fastest JavaScript Runtime

Bun is another modern alternative focused on performance. Written in Zig instead of C++ (like Node.js and Deno), it offers impressive speed:

1// Bun supports TypeScript natively
2Bun.serve({
3  port: 3000,
4  fetch(req) {
5    return new Response("Welcome to the Jurassic Park management system!");
6  }
7});

Key Features of Bun:

1. Blazing Speed

  • App startup: ~3x faster than Node.js
  • Package installation: ~100x faster than npm
  • Bundling: ~10x faster than webpack

2. Node.js Compatibility

  • Supports npm modules
  • Has built-in Node.js-compatible APIs
  • Easier migration than Deno

3. Built-in Tools

1# Bundler
2bun build ./index.ts --outdir ./dist
3
4# Test runner
5bun test
6
7# Package manager (super fast!)
8bun install
9bun add lodash

4. Built-in SQLite

1import { Database } from "bun:sqlite";
2
3const db = new Database("dinosaurs.db");
4
5// Create table
6db.run(`
7  CREATE TABLE IF NOT EXISTS dinosaurs (
8    id INTEGER PRIMARY KEY,
9    name TEXT,
10    species TEXT,
11    age INTEGER
12  )
13`);
14
15// Insert data
16const insert = db.prepare("INSERT INTO dinosaurs (name, species, age) VALUES (?, ?, ?)");
17insert.run("Rexy", "T-Rex", 8);
18insert.run("Blue", "Velociraptor", 4);
19
20// Queries
21const dinosaurs = db.query("SELECT * FROM dinosaurs").all();
22console.log(dinosaurs);

Comparison: Node.js vs Deno vs Bun

| Feature | Node.js | Deno | Bun | |---------|---------|------|-----| | TypeScript | Requires config | Native | Native | | Package manager | npm/yarn/pnpm | Built-in | Super fast | | Permission system | None | Yes | None | | npm compatibility | 100% | ~Partial | High | | Performance | Standard | Good | Excellent | | Ecosystem | Huge | Growing | Young | | Stability | Production | Stable | Beta/Dev |

Practical Example - Jurassic Park API in Each Runtime

Node.js + Express:

1// Requires: npm install express
2const express = require('express');
3const app = express();
4
5app.get('/dinosaurs', (req, res) => {
6  res.json([
7    { name: "Rexy", species: "T-Rex" },
8    { name: "Blue", species: "Velociraptor" }
9  ]);
10});
11
12app.listen(3000, () => console.log('Server running on port 3000'));

Deno:

1// No installation needed - everything from URL
2import { serve } from "https://deno.land/std@0.200.0/http/server.ts";
3
4serve((req) => {
5  const dinosaurs = [
6    { name: "Rexy", species: "T-Rex" },
7    { name: "Blue", species: "Velociraptor" }
8  ];
9
10  return new Response(JSON.stringify(dinosaurs), {
11    headers: { "content-type": "application/json" }
12  });
13}, { port: 3000 });
14
15console.log("Server running on port 3000");

Bun:

1// Built-in API, super fast
2Bun.serve({
3  port: 3000,
4  fetch(req) {
5    const dinosaurs = [
6      { name: "Rexy", species: "T-Rex" },
7      { name: "Blue", species: "Velociraptor" }
8    ];
9
10    return new Response(JSON.stringify(dinosaurs), {
11      headers: { "content-type": "application/json" }
12    });
13  }
14});
15
16console.log("Server running on port 3000");

Alternatives Summary

Just as Jurassic Park experimented with different methods to find the best solutions, the JavaScript world offers various runtimes, each with its own strengths:

  • Node.js - proven, stable, with a massive ecosystem
  • Deno - secure, modern, with native TypeScript
  • Bun - blazingly fast, compatible, with built-in tools

The choice depends on your needs, but knowing the alternatives makes you better prepared for the future of JavaScript!

Summary

Prototypes and prototypal inheritance are fundamental concepts in JavaScript. Even when using modern class syntax (ES6+), it's important to understand the underlying prototype mechanism to fully leverage the language's capabilities. In our Jurassic Park, prototypal inheritance helps us model different types of dinosaurs and their behaviors in a consistent, efficient way.

The main concepts we covered:

  1. Prototypes - the mechanism through which objects can inherit properties and methods from other objects
  2. Prototype chain - a sequence of objects linked through their prototypes that JavaScript traverses when looking up properties
  3. Constructor functions and prototypes - the traditional way of implementing inheritance in JavaScript
  4. ES6+ classes - modern syntax for inheritance, built on the prototype mechanism
  5. Mixins - a way to combine functionality from different sources

Understanding prototypes and inheritance in JavaScript is key to writing efficient, well-organized code that can model complex relationships between objects, just like those in our virtual Jurassic Park.

"Prototypes and inheritance in JavaScript are like the genetic code in Jurassic Park" - says Dr. Rex. "A Tyrannosaurus inherits traits from Carnivore, which inherits from Dinosaur, which inherits from Creature. Each level adds specialized capabilities. ES6 classes are like modern genetic engineering - the same DNA, but cleaner tools!"

Go to CodeWorlds