在 JavaScript 中,prototype
和 __proto__
都与原型链和继承机制有关,但它们的含义和作用是不同的。下面来详细讲解它们的区别。
一、prototype 和 proto 的区别
特性 | prototype |
__proto__ |
---|---|---|
作用 | 构造函数的原型对象 | 实例对象的隐式原型 |
类型 | 对象 | 对象 |
访问方式 | 构造函数.prototype | 实例对象.proto |
作用场景 | 为构造函数定义方法和属性 | 用于实例对象访问原型链 |
修改影响 | 修改后影响所有实例 | 修改后仅影响该实例及其后代 |
二、prototype 和 proto 的详细解析
1. prototype 的作用
- 每个构造函数都有一个名为
prototype
的属性,它指向一个对象。 - 这个对象是用来共享方法和属性的。
- 当通过构造函数创建对象时,实例会继承 prototype 上的方法和属性。
示例:
// 构造函数
function Person(name) {
this.name = name;
}
// 给构造函数的 prototype 添加方法
Person.prototype.sayHello = function() {
console.log("Hello, my name is " + this.name);
};
// 创建实例
const person1 = new Person("Alice");
const person2 = new Person("Bob");
person1.sayHello(); // 输出:Hello, my name is Alice
person2.sayHello(); // 输出:Hello, my name is Bob
注意:
- 通过修改
Person.prototype
,所有实例都能访问到新的方法或属性。 prototype
只有在构造函数上存在,实例本身没有prototype
属性。
2. proto 的作用
- 每个对象实例都有一个
__proto__
属性,指向创建该实例的构造函数的原型对象。 - 这个属性是隐式原型,用于查找属性和方法。
示例:
console.log(person1.__proto__ === Person.prototype); // true
console.log(person2.__proto__ === Person.prototype); // true
作用:
- 当我们访问对象的属性或方法时,JavaScript 会先查找对象本身,如果没有,则沿着**
__proto__
** 继续查找,直到null
。 - 这种链式查找机制称为原型链。
三、构造函数、实例、原型三者关系
构造函数:
Person.prototype
:原型对象。Person.prototype.constructor
:指向构造函数本身。
实例对象:
person1.__proto__
:指向构造函数的原型对象,即Person.prototype
。
关系图:
Person --> prototype --> { sayHello, constructor }
↑
__proto__
↑
person1 / person2
四、示例验证原型链
console.log(person1.__proto__ === Person.prototype); // true
console.log(Person.prototype.constructor === Person); // true
console.log(Object.getPrototypeOf(person1) === Person.prototype); // true
五、修改 prototype 和 proto 的区别
修改 prototype:影响所有实例
Person.prototype.greet = function() {
console.log("Greetings from " + this.name);
};
person1.greet(); // Greetings from Alice
person2.greet(); // Greetings from Bob
修改 proto:仅影响当前对象
const obj = {};
obj.__proto__ = {
customMethod: function() {
console.log("I am custom!");
}
};
obj.customMethod(); // I am custom!
六、在类中的表现(ES6+)
在 ES6 中,使用 class
语法定义类,原型机制依然有效:
class Animal {
speak() {
console.log("Animal speaks");
}
}
const dog = new Animal();
console.log(dog.__proto__ === Animal.prototype); // true
console.log(Animal.prototype.constructor === Animal); // true
七、为什么 proto 不推荐使用?
- 性能问题:
__proto__
是非标准特性,访问速度较慢。 - 兼容性问题: 早期版本的 IE 不支持。
- 规范性: 建议使用
Object.getPrototypeOf()
代替:console.log(Object.getPrototypeOf(person1) === Person.prototype); // true
八、总结
比较项 | prototype | proto |
---|---|---|
作用 | 构造函数的原型对象 | 实例对象的隐式原型 |
类型 | 对象 | 对象 |
修改效果 | 修改后影响所有实例 | 修改后仅影响该实例及其后代 |
使用场景 | 批量定义共享方法和属性 | 查找实例对象原型链中的属性和方法 |
推荐使用 | 是 | 否,建议使用 Object.getPrototypeOf() 代替 |
面试高频问题
prototype 和 proto 有什么区别?
prototype
是构造函数的属性,__proto__
是实例对象的属性。prototype
用于定义共享方法和属性,__proto__
用于访问原型链。
如何修改实例的原型?
- 可以直接使用
Object.setPrototypeOf(obj, newProto);
或obj.__proto__ = newProto;
,但前者性能更优。
- 可以直接使用
为什么不推荐使用 proto?
- 性能差且非标准化,推荐使用
Object.getPrototypeOf()
。
- 性能差且非标准化,推荐使用
希望这些讲解能帮你彻底搞清楚 prototype
和 __proto__
的区别!