一、基本概念对比:proto 与 prototype 简明区别
| 特性 | prototype | __proto__ | |
|---|---|---|---|
| 归属 | 构造函数(Function)的一个属性 | 所有对象(包括函数对象)都有的属性 | |
| 作用 | 实例化时,作为新对象的原型对象 | 指向该对象的原型,即其构造函数的 prototype | |
| 定义 | 构造函数拥有的默认属性 | 各类对象(实例、数组、函数等)自带 | |
| 主要用途 | 提供共享属性、方法给实例 | 形成原型链中的一环,属性查找委托 | |
| 影响对象 | 所有通过该构造函数 new 出来的实例 | 当前对象的原型引用 |
二、实例演示:一步步看懂它们的关系
让我们看一段代码,逐步揭示它们在对象、函数、原型链中的真实联系:
function Animal(name) {
this.name = name;
}
// 给 Animal 的 prototype 添加方法
Animal.prototype.sayHello = function() {
console.log("Hello, I am " + this.name);
};
const cat = new Animal("Kitty");
console.log(cat.__proto__ === Animal.prototype); // true
console.log(Animal.prototype.constructor === Animal); // true
console.log(Animal.__proto__ === Function.prototype); // true
console.log(Animal.prototype.__proto__ === Object.prototype); // true
详解:这一系列等式代表什么?
cat.__proto__—— 指向构造函数的 prototype,即Animal.prototype。因此cat.sayHello()能运行,实际调用自 Animal.prototype。Animal.prototype.constructor—— 指回 Animal 构造函数自身。Animal.__proto__—— 作为一个函数对象,它的原型指向了 Function.prototype,正因如此:“万物皆对象,函数也是对象”。Animal.prototype.__proto__—— Animal.prototype 的原型是 Object.prototype,意味着所有实例最终都会沿原型链归结到 Object.prototype。
关系图结构
cat (实例对象)
|
v
cat.__proto__ --------> Animal.prototype
|
v
Animal.prototype.__proto__ --> Object.prototype
三、原型链究竟怎么查找?prototype 和 __proto__ 在其中扮演什么角色?
查找规则
当你访问一个对象的属性,比如 cat.sayHello(),JS 引擎会:
- 先在实例(cat)自身查找属性。
- 没有则顺着
cat.__proto__也就是构造函数的prototype继续找。 - 没找到就沿着
Animal.prototype.__proto__(即 Object.prototype)再查找。 - 一直到原型链顶端
Object.prototype.__proto__为 null。
思考:如果中间任一层查到属性,就会停止;否则找不到就返回 undefined。
四、深挖原理:prototype 的作用
- 函数特有属性。普通对象没有
prototype,函数才有。 - 实现继承。所有实例共享
prototype上的方法或属性。 - 被用来填充新实例的
__proto__。
function Foo() {}
const f1 = new Foo();
const f2 = new Foo();
console.log(f1.__proto__ === Foo.prototype); // true
console.log(f2.__proto__ === Foo.prototype); // true
这正是原型“共享属性”的由来。
五、慎用 __proto__,你该怎么写才标准?
__proto__曾是大多数浏览器的非标准实现,但已在 ES6 标准化。- 更推荐的方式是用
Object.getPrototypeOf(obj)和Object.setPrototypeOf(obj, proto)读写原型。
例:
Object.getPrototypeOf(cat) === Animal.prototype; // true
六、常见误区大解析
- 误区 1:可以直接 new 出一个 prototype
错!prototype 只是对象模板,不能直接实例化。只能用构造函数 new 出对象实例。 - 误区 2:以为所有对象都有 prototype
只有函数有prototype;普通对象没有。 - 误区 3:误认为 prototype 和 proto 是一回事
概念和用途都不相同,但通过实例和构造函数连接在一起,才构成原型链。
七、实际开发中你该怎么应用原型链?
-
写继承:
function Animal() {} function Dog() {} Dog.prototype = new Animal(); -
对象扩展:
const obj = { x: 1 }; const newObj = Object.create(obj); // newObj.__proto__ === obj
八、面试直击·高频经典面试题
题目:请写出以下代码的执行结果,并解释原因!
function Parent() {}
Parent.prototype.say = function() { console.log('parent'); };
function Child() {}
Child.prototype = new Parent();
const c = new Child();
c.say();
console.log(c.__proto__ === Child.prototype); // ?
console.log(Child.prototype.__proto__ === Parent.prototype); // ?
答案:
c.say()输出 'parent',因为继承自 Parent.prototype。c.__proto__ === Child.prototype为 trueChild.prototype.__proto__ === Parent.prototype为 true
剖析: 正是原型链和 prototype、proto 的精准配合!
九、总结
prototype连接构造函数和实例,使属性/方法共享和继承成为可能。__proto__连接对象和其原型,形成灵活的原型链条。
如果你觉得这篇文章有用,记得点赞、收藏、分享,关注我查看更多前端干货!