
JavaScript类与原型继承的本质
在javascript中,es6的class语法提供了一种更清晰、更面向对象的方式来定义构造函数和它们相关的原型方法。然而,这仅仅是现有原型继承模式之上的一层抽象(或称“语法糖”)。
考虑一个典型的类定义,其中包含一个方法:
class Foo {
barMethod() {
console.log("This is a prototype method.");
}
}
// 实际上,这大致等同于:
// function Foo() {}
// Foo.prototype.barMethod = function() { console.log("This is a prototype method."); };当我们创建一个Foo的实例时,barMethod可以通过原型链被访问到:
const instance1 = new Foo(); instance1.barMethod(); // 输出: This is a prototype method. console.log(Foo.prototype.barMethod === instance1.barMethod); // 输出: true
这表明barMethod是定义在Foo.prototype上的,所有Foo的实例都共享同一个barMethod函数。
公共实例字段的特殊性
然而,当我们在类中声明一个“公共实例字段”(Public Instance Field)时,其行为与原型方法截然不同。公共实例字段允许我们在类定义内部直接声明并初始化实例属性,而无需在constructor中显式使用this。
立即学习“Java免费学习笔记(深入)”;
例如:
class FooWithField {
barField = "Hello World";
}初学者可能会误以为barField也会像barMethod一样被添加到FooWithField.prototype上。但实际情况并非如此:
const instance2 = new FooWithField(); console.log(instance2.barField); // 输出: Hello World console.log(FooWithField.prototype.barField); // 输出: undefined
从上述结果可以看出,barField并不存在于FooWithField.prototype上。那么,它究竟在哪里呢?
公共实例字段的内部机制
公共实例字段的声明,实际上是JavaScript引擎在实例化类时,将该字段直接添加到每个新创建的实例对象上。其内部机制等价于在类的constructor(构造函数)中,使用this关键字对属性进行赋值。
也就是说,以下两种写法在功能上是等价的:
使用公共实例字段:
class FooWithField {
barField = "Hello World";
}使用构造函数显式赋值:
class FooWithConstructor {
constructor() {
this.barField = "Hello World";
}
}无论哪种方式,barField都是实例自身的属性,而不是原型链上的属性。这意味着每个FooWithField或FooWithConstructor的实例都会拥有自己独立的barField副本。
const instA = new FooWithField(); const instB = new FooWithField(); console.log(instA.barField); // Hello World console.log(instB.barField); // Hello World instA.barField = "Modified A"; console.log(instA.barField); // Modified A console.log(instB.barField); // Hello World (instB的barField不受影响)
总结与注意事项
-
原型方法 vs. 实例字段:
- 类方法(如 barMethod()):定义在类的原型 (Foo.prototype) 上,所有实例共享同一个方法引用。
- 公共实例字段(如 barField = "value"):直接定义在每个实例对象上,每个实例拥有自己的独立副本。
- 内部实现: 公共实例字段是构造函数中 this.propertyName = value 的语法糖。
- 访问方式: 实例字段只能通过实例对象(如 instance.barField)访问,无法通过类构造函数本身或其原型(Foo.barField 或 Foo.prototype.barField)直接访问。
- 适用场景: 当你需要为每个实例提供独立的、可变的状态时,公共实例字段非常有用。而当功能逻辑或共享行为需要被所有实例共享时,应使用原型方法。
理解公共实例字段与原型方法的这种区别,对于编写高效且可维护的JavaScript类至关至要。它有助于避免对属性查找机制的误解,并能更准确地设计类的结构和行为。










