Prototype and prototype chain
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:
Object.getPrototypeOf(obj)
: Retrieves the prototype of an object.Object.setPrototypeOf(obj, prototype)
: Sets the prototype of an object.obj.hasOwnProperty(prop)
: Checks if a property belongs to the object itself (not inherited).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:
- Use
Object.create(null)
to create objects without a prototype. - Use
Object.freeze()
to freeze prototypes. - 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:
- Avoid excessively deep prototype chains.
- Define frequently accessed properties directly on objects.
- 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属性