在JavaScript中,prototype
不是一个实例对象可以直接访问的属性,这就是为什么你在尝试打印 person1.prototype
时会得到 undefined
。实际上,每个函数对象都有一个 prototype
属性,该属性是一个指向原型对象的指针,而这个原型对象用于实现基于原型的继承与属性共享。
prototype 为何是不能被实例直接访问的?
- 当你创建一个实例(如
person1
)时,它内部有一个指向其构造函数(这里是 Person
)的 prototype
对象的链接(通常被称为 __proto__
,尽管这不是ECMAScript标准的一部分,而是某些JavaScript引擎实现的一个属性,用于访问内部 [[Prototype]]
链接)。这个链接是自动创建的,但你不能直接通过实例来访问或修改 prototype
属性,因为该属性是定义在函数对象(构造函数)上的。
prototype 的意义是什么?
- 属性共享:
prototype
允许你在所有实例之间共享方法和属性。这意味着,一旦你在原型上定义了一个方法或属性,所有的实例都可以访问它,而不需要在每个实例上单独定义。这大大节省了内存。 - 继承:基于原型的继承是JavaScript的核心特性之一。通过原型链,你可以实现复杂的继承关系,允许一个对象继承另一个对象的属性和方法。
- 灵活性:虽然JavaScript的原型继承系统可能初看起来有些混乱,但它提供了极高的灵活性。你可以动态地修改原型,进而影响到所有基于该原型的实例。
示例说明
在你的例子中,Person.prototype.sayHello
是在 Person
构造函数的原型上定义的一个方法。所有通过 new Person(...)
创建的实例都会继承这个方法,但它们并不直接拥有 prototype
属性。相反,它们通过内部的 [[Prototype]]
链接(在大多数环境中通过 __proto__
访问)来访问原型链上的方法和属性。
如何查看原型链
如果你想要查看实例的原型链,可以使用 Object.getPrototypeOf()
方法或 __proto__
(尽管不推荐使用后者,因为它不是标准属性):
console.log(Object.getPrototypeOf(person1) === Person.prototype); // true
console.log(person1.__proto__ === Person.prototype); // 大多数环境中也是 true,但不推荐
这样,你就可以看到 person1
的原型确实是 Person.prototype
,从而验证了原型链的存在和它的工作方式。
需要用
Object.getPrototypeOf(person1)
另外 console.log(person1) 里也是可以找到它的 prototype 上的属性的,只是不会直接显示子在 person1 下,而是在他的 <prototype> 下面(firefox,不同的浏览器/IDE 这个显示的“属性”名会有不同,因为这实际上不是一个“属性”,不是可以直接看到的)