JavaScript原型模式详解:内存优化与原型链机制

作者:木棉

时间:2026-10-21

浏览:70

本文深入解析JavaScript中的prototype属性与原型模式。通过对比构造函数与原型对象的内存分配,阐述如何利用原型共享方法以优化性能。同时详细讲解属性搜索的原型链机制、不可变性规则、同名覆盖现象以及重写原型链导致的断链问题,帮助开发者掌握对象继承的核心原理。

文章目录

为了直观理解原型概念,我们可以将JavaScript的对象系统比作一个公共池塘。这个池塘代表原型对象,其中存放的鱼象征着原型中共享的属性和方法,所有实例均可访问。而存放在家中的鱼则代表构造函数中定义的属性和方法,属于实例私有。对象实例则是这些资源的使用者,通过原型链访问共享资源或拥有私有数据。

上述比喻旨在建立对prototype的直观认知,接下来将通过具体代码深入剖析其工作机制。

理解prototype与原型模式

一、理解prototype

在JavaScript中,每个函数都拥有一个prototype属性,该属性是一个指向对象的指针,用于定义实例共享的行为和状态。

原型模式是一种常用的对象创建模式,其核心思想是将实例私有的属性和方法定义在构造函数中,而将可共享的属性和方法定义在原型对象(即prototype指向的对象)中。以下代码展示了如何使用原型模式创建对象:


 function person(name, age) {
 this.name = name;
 this.age = age;
}
person.prototype = {
 sayName: function() {
 console.log(this.name); 
 }
};
var p1 = new person("Wind", 20);
p1.sayName(); // "Wind"
var p2 = new person("Nic", 20);
p2.sayName(); // Nic

在上述代码中,name和age属性被定义在构造函数内,因此每个实例(如p1和p2)在内存中都会拥有各自独立的name和age副本。然而,sayName方法被定义在原型中,这意味着所有实例共享同一个函数引用,调用时执行的是同一份代码,从而节省了内存空间。

如果我们将sayName方法直接定义在构造函数内部,而非原型中,情况会有何不同?

此时,每个实例(p1和p2)都会在各自的内存空间中创建一份独立的sayName函数副本。这种做法会导致内存资源的浪费,因为多个实例重复存储了相同的功能代码。因此,使用prototype的主要优势之一是提高代码复用率并减少内存占用。

在理解原型对象的基础上,还需要掌握属性查找的机制。当代码尝试读取对象的某个属性时,JavaScript引擎会执行一次搜索,目标是指定的属性名。

搜索路径遵循特定的顺序:首先检查对象实例本身,若未找到则继续查找原型对象,接着是对象所继承的父类对象,再追溯至父类对象的原型,依此类推,直到到达原型链的末端。

prototype的使用注意事项

二、prototype的注意点

1. 不可变性:尽管原型是共享的,但通过对象实例无法直接重写原型中的值(对于基本类型而言)。这可以通俗地理解为:只有“父级”(构造函数或原型本身)能统一修改共享值,而“子级”(实例)不能直接改变基本类型的共享值。不过,对于引用类型(如数组),实例可以修改其内部状态,但这并不改变原型对象的引用。

基本类型示例如下:


function person() {}
person.prototype = { 
 num: 0
}; 
var p1 = new person(); 
var p2 = new person();
p1.num++;
p2.num; // 0

非基本类型(引用类型)示例如下:


function person() {}
person.prototype = {
 num: [1,2,3]
}; 
var p1 = new person(); 
var p2 = new person();
p1.num[2] = 8; 
p2.num; // [1, 2, 8] 改变了

2. 同名覆盖性:如果在实例中定义了一个与原型属性同名的属性,该属性会被创建在对象实例中,并屏蔽(覆盖)原型中的同名属性。此时,访问该属性将返回实例中的值,而非原型的值。


function person(name) {
 this.name = name; 
}
person.prototype = {
 age: 18 9 };
var p1 = new person("Wind");
var p2 = new person("Nic");
p1.age = 20;
p1.age; // 20
p2.age; // 18

3. 使用对象字面量创建原型方法会切断原有的原型链。


function person(name) {
 this.name = name; 
}
person.prototype = { 
 sayName: function() {
 console.log(this.name); 
 }
};
var p1 = new person("Wind");
person.prototype = {
 age = 20
}; 
p1.sayName(); // error

这是因为将prototype指针指向了一个全新的对象,从而切断了构造函数与原有原型对象的联系。此时,之前创建的实例(如p1)仍然指向旧的原型对象,因此无法访问新原型中定义的方法。那么,如果尝试调用新原型的属性会发生什么?

p1.age; // error

基于此现象可以推测,原本包含sayName的旧原型对象可能已被内存回收,而p1的prototype指针仍指向该已失效的旧对象,导致调用时抛出错误(error)。

总结

三、总结

prototype的正确用法是:构造函数模型用于定义每个实例独有的属性,而原型模型用于定义所有实例共享的方法和非私有属性。

本文涵盖了JavaScript原型机制的核心内容,希望能为读者的学习和工作提供有益参考。感谢阅读与支持。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章