阿里云主机折上折
  • 微信号
Current Site:Index > Prototype and prototype chain

Prototype and prototype chain

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

Prototype

Every object in JavaScript has a prototype, which is also an object. Objects inherit properties and methods from their prototype. Prototypes are the foundation of JavaScript's inheritance mechanism. Constructors point to prototype objects via the prototype property, and instance objects access their prototype via the __proto__ property.

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

Person.prototype.sayHello = function() {
  console.log(`Hello, my name is ${this.name}`);
};

const person1 = new Person('Alice');
person1.sayHello(); // Output: Hello, my name is Alice

In this example, Person is a constructor function, and its prototype property points to the prototype object. When we create the person1 instance, the instance links to Person.prototype via __proto__, allowing it to access the sayHello method.

Prototype Chain

The prototype chain is JavaScript's mechanism for implementing inheritance. When accessing a property of an object, if the object itself does not have the property, JavaScript traverses up the prototype chain until it finds the property or reaches the end of the chain (null). This chain-like structure is the prototype chain.

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

Animal.prototype.eat = function() {
  console.log(`${this.name} is eating.`);
};

function Dog(name, breed) {
  Animal.call(this, name);
  this.breed = breed;
}

// Set up the prototype chain
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.bark = function() {
  console.log(`${this.name} is barking!`);
};

const myDog = new Dog('Buddy', 'Golden Retriever');
myDog.eat(); // Output: Buddy is eating.
myDog.bark(); // Output: Buddy is barking!

Here, Dog inherits traits from Animal. The prototype of Dog.prototype is Animal.prototype, forming the prototype chain. When myDog.eat() is called, JavaScript first looks for the method on the myDog instance, then traverses up the chain to Dog.prototype, and finally finds it on Animal.prototype.

Prototype-Related Methods

JavaScript provides several important prototype-related methods:

  1. Object.getPrototypeOf(obj): Retrieves the prototype of an object.
  2. Object.setPrototypeOf(obj, prototype): Sets the prototype of an object.
  3. obj.hasOwnProperty(prop): Checks if a property belongs to the object itself (not inherited).
  4. prop in obj: Checks if a property exists on the object or its prototype chain.
const obj = {};
const parent = { x: 1 };

Object.setPrototypeOf(obj, parent);

console.log(Object.getPrototypeOf(obj) === parent); // true
console.log(obj.x); // 1 (inherited from parent)
console.log(obj.hasOwnProperty('x')); // false
console.log('x' in obj); // true

Constructors and Prototypes

The relationship between constructors, prototypes, and instances is key to understanding the prototype chain. Each constructor has a prototype property pointing to its prototype object, the prototype object has a constructor property pointing back to the constructor, and instances link to the prototype object via __proto__.

function Car(make, model) {
  this.make = make;
  this.model = model;
}

Car.prototype.displayInfo = function() {
  console.log(`This is a ${this.make} ${this.model}`);
};

const myCar = new Car('Toyota', 'Camry');

console.log(Car.prototype.constructor === Car); // true
console.log(myCar.__proto__ === Car.prototype); // true
console.log(myCar instanceof Car); // true

Pros and Cons of Prototypal Inheritance

Prototypal inheritance has unique advantages and limitations:

Advantages:

  • Memory-efficient, as shared methods are defined only once on the prototype.
  • Dynamic—modifying the prototype affects all instances immediately.
  • Simple implementation, enabling inheritance without class syntax.

Disadvantages:

  • Shared reference-type properties can cause issues.
  • Subtypes cannot pass parameters to supertypes.
  • Long prototype chains can impact performance.
function Parent() {
  this.colors = ['red', 'blue'];
}

function Child() {}

Child.prototype = new Parent();

const child1 = new Child();
child1.colors.push('green');

const child2 = new Child();
console.log(child2.colors); // ['red', 'blue', 'green'] (colors property is shared)

ES6 Classes and Prototypes

ES6 introduced the class syntax, but under the hood, it still relies on prototypal inheritance. Classes are syntactic sugar, making prototypal inheritance easier to understand and use.

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }

  speak() {
    console.log(`${this.name} barks!`);
  }
}

const d = new Dog('Mitzie', 'Poodle');
d.speak(); // Mitzie barks!

When transpiled using tools like Babel, classes are converted into prototypal inheritance code.

Prototype Pollution and Mitigation

Prototype pollution refers to malicious modifications of foundational prototypes like Object.prototype, which affect all objects. This is a common security issue.

// Malicious code
Object.prototype.isAdmin = true;

// Affected code
const user = { name: 'Alice' };
if (user.isAdmin) {
  // Code here should not execute
  console.log('You have admin privileges!');
}

Ways to mitigate prototype pollution:

  1. Use Object.create(null) to create objects without a prototype.
  2. Use Object.freeze() to freeze prototypes.
  3. Use hasOwnProperty to check properties.
// Safe approach
const safeObj = Object.create(null);
safeObj.name = 'Safe Object';
console.log(safeObj.hasOwnProperty); // undefined

// Freeze the prototype
Object.freeze(Object.prototype);
Object.prototype.isAdmin = true; // Throws an error in strict mode

Performance Considerations

Prototype chain traversal can impact performance, as longer chains increase property lookup time. Modern JavaScript engines optimize prototype lookups, but it's still important to:

  1. Avoid excessively deep prototype chains.
  2. Define frequently accessed properties directly on objects.
  3. Use composition over inheritance for code sharing.
// Poor performance due to deep inheritance chain
function A() {}
function B() {}
function C() {}
function D() {}

B.prototype = new A();
C.prototype = new B();
D.prototype = new C();

const obj = new D();
// Property lookup traverses 4 prototype levels

// Better approach: composition
const features = {
  feature1() {},
  feature2() {}
};

function MyObject() {
  Object.assign(this, features);
}

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

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

上一篇:对象枚举与迭代

下一篇:constructor属性

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 ☕.