本文深入解析 JavaScript 的原型与继承机制,阐述对象如何通过 prototype 属性共享属性和方法。通过 Circle 类与 String 原型扩展等实例,演示构造函数、原型方法定义及属性遮蔽原理,帮助开发者优化内存使用并掌握 JS 面向对象编程核心。
JavaScript 原型与继承基础
JavaScript 中的对象通过其原型对象(prototype object)来继承属性。每个对象都拥有原型,且原型的属性在表现上等同于使用该原型作为源的对象属性。简而言之:
所有对象均从其原型继承属性。。每个对象都从其原型继承属性。
对象的 prototype 属性由构造函数(constructor function)定义。在 JavaScript 中,所有函数都具备一个 prototype 属性,该属性初始为空。随后向此属性添加的任何属性,都将被由该构造函数创建的对象所拥有。
prototype 对象与构造函数紧密关联,这使其成为存放方法及其他常量的理想位置。需要注意的是,原型中的属性并不会被复制到新创建的对象实例中,它们仅在外观上表现为对象的属性。这种机制显著减少了多个同类对象实例所占用的内存空间。
原型属性的读写与遮蔽机制
每个类仅对应一个 prototype 对象,其中附带一套属性。然而,在运行时我们可能会创建该类的多个实例。此时,如果对原型中的属性进行读写操作,会发生什么情况?
当读取属性时,JavaScript 首先检查对象本身是否拥有该属性;若未找到,则继续查找原型中是否存在。若原型中存在,则返回该值。
当尝试写入原型属性时,由于多个对象共享同一原型,直接修改原型并非最佳实践。此时,JavaScript 会在当前对象上创建一个同名的属性,并将值写入该新属性中。下次读取此属性时,JavaScript 会直接在对象自身属性中找到它,从而无需再查询原型。这种现象被称为“对象的属性掩盖或隐藏了原型的属性”(shadows or hides)。
基于上述讨论,在设计类时只需遵循一个核心原则:
在原型中仅定义方法(通常方法是不变的)、常数或常量。遵循这一原则即可避免混淆。
Circle 类原型定义示例
具体示例如下:
// Define a constructor method for our class.
// Use it to initialize properties that will be different for
// each individual Circle object.
functionCircle(x, y, r)
{
this.x = x; // The X-coordinate of the center of the circle
this.y = y; // The Y-coordinate of the center of the circle
this.r = r; // The radius of the circle
}
// Create and discard an initial Circle object.
// This forces the prototype object to be created in JavaScript 1.1.
new Circle(0,0,0);
// Define a constant: a property that will be shared by
// all circle objects. Actually, we could just use Math.PI,
// but we do it this way for the sake of instruction.
Circle.prototype.pi = 3.14159;
// Define a method to compute the circumference of the circle.
// First declare a function, then assign it to a prototype property.
// Note the use of the constant defined above.
function Circle_circumference( ) { return 2
*
this.pi *
this.r; }
Circle.prototype.circumference =Circle_circumference;
// Define another method. This time we use a function literal to define
// the function and assign it to a prototype property all in one step.
Circle.prototype.area = function( ) { return
this.pi *
this.r *
this.r; }
// The Circle class is defined.
// Now we can create an instance and invoke its methods.
var c = new Circle(0.0, 0.0, 1.0);
var a =c.area( );
var p = c.circumference( ); 内置类的原型扩展
内置类的 prototype 属性。
不仅用户自定义的类拥有 prototype,系统内置类如 String、Date 等也具备此属性。此外,我们还可以向这些内置类添加新的方法或属性。
以下代码展示了如何为所有 String 对象添加一个实用的函数:
// Returns true if the last character is c
String.prototype.endsWith = function(c) {
return (c == this.charAt(this.length-1))
} 随后,我们可以像这样调用该函数:
var message = "hello world";
message.endsWith('h') // Returns false
message.endsWith('d') // Returns true