javascript 中的原型对象是用于实现继承和共享属性的机制,每个函数都有一个 prototype 属性指向一个对象,而该对象的 constructor 属性又指回函数本身;实例对象通过内部链接 [[prototype]](通常通过 __proto__ 访问)关联到构造函数的 prototype 对象,从而形成原型链。理解这一机制的关键在于区分构造函数、原型对象和实例之间的关系,以及属性查找时的原型链遍历过程。

如何创建并验证原型链关系?
要验证原型链关系,可以创建一个构造函数,定义其原型方法,然后实例化对象并检查 instanceof 运算符和 Object.getPrototypeOf() 的结果。例如,定义一个 Person 构造函数,设置 Person.prototype.sayHello 为一个方法,创建实例 p1 后,p1 instanceof Person 返回 true,且 Object.getPrototypeOf(p1) === Person.prototype 也成立。这证明了实例与原型对象之间的链接。

function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
return 'Hello, ' + this.name;
};
const p1 = new Person('Alice');
console.log(p1 instanceof Person); // true
console.log(Object.getPrototypeOf(p1) === Person.prototype); // true
console.log(p1.sayHello()); // Hello, Alice
上述代码中,Person 是构造函数,Person.prototype 是原型对象,p1 是实例。通过 new 操作符创建实例时,会自动将实例的 [[Prototype]] 链接到 Person.prototype。当调用 p1.sayHello() 时,JavaScript 引擎首先在 p1 自身属性中查找 sayHello,未找到则沿原型链向上查找,最终在 Person.prototype 中找到该方法并执行。
原型对象上的属性共享行为是怎样的?
原型对象上的属性被所有实例共享,修改原型上的引用类型属性会影响所有实例,而修改原始类型属性则会在实例上创建同名属性遮蔽原型属性。例如,在 Person.prototype 上添加一个数组 friends,多个实例共享该数组,向其中一个实例的 friends 添加元素会反映在所有实例中。但若在实例上直接赋值 p1.friends = ['Bob'],则 p1 拥有自己的 friends 属性,不再共享原型的数组。

function Person(name) {
this.name = name;
}
Person.prototype.friends = [];
const p1 = new Person('Alice');
const p2 = new Person('Bob');
p1.friends.push('Charlie');
console.log(p2.friends); // ['Charlie']
p1.friends = ['David'];
console.log(p2.friends); // ['Charlie']
console.log(p1.friends); // ['David']
在第一个示例中,p1.friends.push('Charlie') 修改的是原型上的 friends 数组,因为 p1 自身没有 friends 属性,所以沿原型链找到 Person.prototype.friends 并进行修改,p2 同样共享该数组,因此 p2.friends 也包含 'Charlie'。在第二个示例中,p1.friends = ['David'] 在 p1 上创建了一个新的 friends 属性,遮蔽了原型上的 friends,此时 p1.friends 指向新数组,而 p2.friends 仍指向原型上的数组,因此两者不再同步。
如果原型对象被重新赋值会发生什么?
如果构造函数的 prototype 属性被重新赋值为一个新对象,原有实例的 [[Prototype]] 仍然指向旧的原型对象,不会自动更新到新对象。新创建的实例则会指向新的原型对象。例如,先创建实例 p1,然后执行 Person.prototype = { sayGoodbye: function() {} },p1 的 [[Prototype]] 仍指向旧的原型对象,不包含 sayGoodbye 方法,而新实例 p2 的 [[Prototype]] 指向新对象,包含 sayGoodbye 方法。此外,新原型对象的 constructor 属性不再指向 Person,需要手动修复。
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
return 'Hello, ' + this.name;
};
const p1 = new Person('Alice');
Person.prototype = {
sayGoodbye: function() {
return 'Goodbye, ' + this.name;
}
};
const p2 = new Person('Bob');
console.log(p1.sayHello()); // Hello, Alice
console.log(p1.sayGoodbye); // undefined
console.log(p2.sayGoodbye()); // Goodbye, Bob
console.log(p2.constructor === Person); // false
Person.prototype.constructor = Person;
在上述代码中,p1 是在原型替换前创建的,其 [[Prototype]] 指向旧的原型对象,因此能调用 sayHello 但无法调用 sayGoodbye。p2 是在原型替换后创建的,其 [[Prototype]] 指向新对象,因此能调用 sayGoodbye 但无法调用 sayHello。新原型对象的 constructor 属性默认为 Object,通过 Person.prototype.constructor = Person 可以手动修复,确保 p2.constructor === Person 为 true。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











