阿里云主机折上折
  • 微信号
Current Site:Index > The relationship between the Prototype pattern and the JavaScript prototype chain

The relationship between the Prototype pattern and the JavaScript prototype chain

Author:Chuan Chen 阅读数:49914人阅读 分类: JavaScript

The Prototype Pattern is a creational design pattern that creates new objects by copying existing ones, rather than through constructors. JavaScript's prototype chain mechanism inherently supports this pattern, with deep conceptual and implementation-level connections between the two. Understanding this relationship enables more efficient use of JavaScript's features for object creation and inheritance.

Core Idea of the Prototype Pattern

The essence of the Prototype Pattern lies in creating new instances by cloning existing objects, avoiding repetitive initialization processes. In traditional object-oriented languages, this typically requires implementing a Cloneable interface, whereas JavaScript builds this capability directly into its prototype chain. For example:

const carPrototype = {
  wheels: 4,
  drive() {
    console.log('Driving with ' + this.wheels + ' wheels');
  },
  clone() {
    return Object.create(this);
  }
};

const myCar = carPrototype.clone();
myCar.drive(); // Output: Driving with 4 wheels

This pattern is particularly suitable for the following scenarios:

  • When object initialization is costly (e.g., requiring complex calculations or I/O operations)
  • When the system needs to dynamically choose which class to instantiate
  • When avoiding the construction of class hierarchies is desirable

How JavaScript's Prototype Chain Works

Every JavaScript object has a __proto__ property (ES6 recommends using Object.getPrototypeOf), forming the prototype chain lookup mechanism:

function Person(name) {
  this.name = name;
}

Person.prototype.greet = function() {
  return `Hello, ${this.name}`;
};

const john = new Person('John');
console.log(john.__proto__ === Person.prototype); // true
console.log(john.__proto__.__proto__ === Object.prototype); // true

The workflow of the prototype chain:

  1. When accessing an object property, first search the instance itself
  2. If not found, search up the __proto__ chain
  3. Continue until reaching Object.prototype (the top of the prototype chain)

Intersection of the Two Prototypes

The Prototype Pattern's implementation in JavaScript directly leverages the language's built-in prototype mechanism:

// Prototype Pattern implementation
const robotPrototype = {
  beep() {
    console.log(this.sound || 'Beep beep');
  }
};

function createRobot(sound) {
  const robot = Object.create(robotPrototype);
  robot.sound = sound;
  return robot;
}

const r2d2 = createRobot('Beep boop');
r2d2.beep(); // Output: Beep boop

Differences from the traditional Prototype Pattern:

  • JavaScript handles delegation automatically via the prototype chain
  • No need to explicitly implement a cloning interface
  • Modifying the prototype affects all derived instances

Practical Application Patterns

Performance Optimization Scenarios

For scenarios requiring the creation of numerous similar objects:

// Bullet objects in a game
const bulletPrototype = {
  x: 0,
  y: 0,
  speed: 10,
  draw() {
    console.log(`Drawing bullet at (${this.x}, ${this.y})`);
  }
};

function createBullet(x, y) {
  const bullet = Object.create(bulletPrototype);
  bullet.x = x;
  bullet.y = y;
  return bullet;
}

// Create 100 bullets
const bullets = Array.from({length: 100}, (_, i) => 
  createBullet(i % 10 * 50, Math.floor(i / 10) * 50)
);

Dynamic Inheritance Implementation

Modifying inheritance relationships at runtime using the prototype chain:

const animal = {
  breathe() {
    console.log('Breathing...');
  }
};

const dog = Object.create(animal);
dog.bark = function() {
  console.log('Woof!');
};

// Modify prototype at runtime
const superDog = Object.create(dog);
superDog.fly = function() {
  console.log('Flying!');
};

superDog.breathe(); // Breathing...
superDog.bark();    // Woof!
superDog.fly();     // Flying!

Advanced Application Techniques

Prototype Property Shadowing

When prototypes and instances share property names:

const proto = { value: 42 };
const obj = Object.create(proto);

console.log(obj.value); // 42

obj.value = 100;
console.log(obj.value); // 100 (shadows the prototype property)
console.log(proto.value); // 42 (unchanged)

delete obj.value;
console.log(obj.value); // 42 (reverts to prototype property)

Prototype Chain Pollution Prevention

Preventing accidental modifications to the prototype chain:

// Safe prototype inheritance
function safeExtend(parent, child) {
  const proto = Object.create(parent.prototype || Object.prototype);
  // Copy enumerable properties
  for (const key in child) {
    if (child.hasOwnProperty(key)) {
      proto[key] = child[key];
    }
  }
  return proto;
}

function Parent() {}
Parent.prototype.method = function() {};

function Child() {}
Child.prototype = safeExtend(Parent, {
  newMethod() {}
});

Evolution in Modern JavaScript

ES6's class syntax sugar is still based on the prototype chain:

class Vehicle {
  constructor(wheels) {
    this.wheels = wheels;
  }
  drive() {
    console.log(`Driving with ${this.wheels} wheels`);
  }
}

class Car extends Vehicle {
  constructor() {
    super(4);
  }
}

console.log(Car.prototype.__proto__ === Vehicle.prototype); // true

The polyfill implementation of Object.create reveals the nature of the prototype chain:

if (!Object.create) {
  Object.create = function(proto) {
    function F() {}
    F.prototype = proto;
    return new F();
  };
}

Performance Considerations and Pitfalls

Excessively long prototype chains can degrade lookup performance:

// Create a deep prototype chain
let current = {};
for (let i = 0; i < 100; i++) {
  current = Object.create(current);
  current['level'+i] = i;
}

// Property lookup time increases with depth
console.time('deep lookup');
current.level99;
console.timeEnd('deep lookup'); // Several times slower than direct access

for...in iterates over prototype chain properties:

const obj = Object.create({ inherited: true });
obj.own = true;

for (const key in obj) {
  console.log(key); // Outputs 'own' and 'inherited'
  if (obj.hasOwnProperty(key)) {
    console.log('Own property:', key);
  }
}

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.