一、先理清核心定义:谁拥有prototype和__proto__?
| 特性 | prototype | proto(隐式原型) |
|---|---|---|
| 归属 | 只有函数拥有(所有函数默认自带) | 只有对象拥有(所有对象,包括函数) |
| 本质 | 普通对象(Function.prototype 除外) | 普通对象 |
| 作用 | 存储构造函数的「公共属性 / 方法」,供实例共享 | 指向当前对象的「原型对象」,构建原型链 |
| 是否可直接修改 | 可手动修改(常用) | 非标准属性(ES6 后推荐用 Object.getPrototypeOf),不建议直接修改 |
关键结论:
- 当函数作为构造函数使用时,
prototype才有用;普通函数的prototype无意义。- 所有对象(包括函数对象、实例对象)的
__proto__,最终都会指向某个构造函数的prototype。
二、prototype详解:构造函数的「公共仓库」
1. 基础用法:给构造函数挂载公共方法 / 属性
// 1. 构造函数(函数拥有prototype) function Person(name) { this.name = name; // 实例自身属性(每个实例独立) } // 2. 给Person的prototype挂载公共方法(所有实例共享) Person.prototype.sayHi = function() { console.log(`我是${this.name}`); }; Person.prototype.age = 18; // 公共属性 // 3. 创建实例 const p1 = new Person("张三"); const p2 = new Person("李四"); // 验证:所有实例共享prototype中的内容 console.log(p1.age); // 18(从Person.prototype获取) console.log(p2.age); // 18 console.log(p1.sayHi === p2.sayHi); // true(内存复用,只有一份方法) // 验证:prototype的归属(只有函数有) console.log(Person.hasOwnProperty("prototype")); // true(函数有prototype) console.log(p1.hasOwnProperty("prototype")); // false(实例对象无prototype)2.prototype的隐藏属性:constructor
每个函数的prototype都自带一个constructor属性,指向原构造函数(可理解为「原型的反向指针」)。
// 接上面的代码 console.log(Person.prototype.constructor === Person); // true // 实例可通过__proto__访问constructor,确认自己的构造函数 console.log(p1.__proto__.constructor === Person); // true console.log(p1.constructor === Person); // true(自动沿原型链查找) // 注意:如果手动覆盖prototype,会丢失constructor,需手动恢复 Person.prototype = { // 手动覆盖后,constructor指向Object,而非Person sayHello: function() { console.log("覆盖原型"); } }; const p3 = new Person("王五"); console.log(p3.constructor === Person); // false console.log(p3.constructor === Object); // true // 修复constructor Person.prototype.constructor = Person; console.log(p3.constructor === Person); // true三、__proto__详解:对象的「原型指针」
1. 基础逻辑:实例的__proto__指向构造函数的prototype
当用new 构造函数()创建实例时,JS 会自动给实例添加__proto__属性,指向构造函数的prototype:
function Cat(name) { this.name = name; } Cat.prototype.color = "orange"; const cat1 = new Cat("橘猫"); // 核心关联:实例.__proto__ === 构造函数.prototype console.log(cat1.__proto__ === Cat.prototype); // true // 实例访问color时,自身没有 → 沿__proto__找Cat.prototype → 找到color console.log(cat1.color); // orange2. 所有对象都有__proto__(包括函数对象)
函数本身也是对象,所以函数也有__proto__,指向Function.prototype(所有函数的原型):
// 函数的__proto__指向Function.prototype console.log(Cat.__proto__ === Function.prototype); // true console.log(Function.__proto__ === Function.prototype); // true(特殊:Function自身的__proto__指向自己的prototype) // Object是函数,它的__proto__也指向Function.prototype console.log(Object.__proto__ === Function.prototype); // true // 普通对象的__proto__指向Object.prototype const obj = {}; console.log(obj.__proto__ === Object.prototype); // true四、原型链:prototype和__proto__的联动
原型链的本质是:对象通过__proto__逐级指向父级构造函数的prototype,直到Object.prototype.__proto__ = null。
可视化原型链(以cat1为例):
cat1(实例) ↓ __proto__ Cat.prototype(原型对象) ↓ __proto__ Object.prototype(顶级原型) ↓ __proto__ null(原型链终点)代码验证原型链查找规则:
// 接上面的Cat示例 // 1. 查找cat1.toString():自身没有 → __proto__找Cat.prototype(没有)→ __proto__找Object.prototype(有) console.log(cat1.toString()); // [object Object](来自Object.prototype) // 2. 验证原型链终点 console.log(Cat.prototype.__proto__ === Object.prototype); // true console.log(Object.prototype.__proto__); // null // 3. 不存在的属性:遍历完原型链返回undefined console.log(cat1.abc); // undefined(cat1 → Cat.prototype → Object.prototype → null,均无abc)五、常见避坑点 & 最佳实践
1. 不要直接修改__proto__
__proto__是 ES6 之前的非标准属性,虽然主流浏览器支持,但修改性能差(会破坏原型链优化)。推荐用标准 API:
const obj = {}; // 替代 __proto__ 读取 console.log(Object.getPrototypeOf(obj) === Object.prototype); // true // 替代 __proto__ 修改(不推荐频繁修改) const protoObj = { x: 10 }; Object.setPrototypeOf(obj, protoObj); console.log(obj.x); // 102. 区分「实例属性」和「原型属性」
function Dog(name) { this.name = name; } Dog.prototype.age = 2; const dog1 = new Dog("旺财"); dog1.age = 3; // 给实例自身添加age,覆盖原型的age console.log(dog1.age); // 3(自身属性优先) // 删除自身属性后,恢复读取原型属性 delete dog1.age; console.log(dog1.age); // 2(从原型读取)3.in与hasOwnProperty的区别(原型链影响)
// 接上面的Dog示例 console.log("age" in dog1); // true(in检查整个原型链) console.log(dog1.hasOwnProperty("age")); // false(自身无age) console.log(dog1.hasOwnProperty("name")); // true(自身有name)六、核心总结
prototype:函数的「公共仓库」,只有构造函数的prototype有实际意义,存储实例共享的属性 / 方法,自带constructor指向原函数。__proto__:对象的「原型指针」,所有对象都有,指向创建该对象的构造函数的prototype,是原型链的核心链路。- 联动关系:
实例.__proto__ === 构造函数.prototype,原型链通过__proto__串联多个prototype,直到null。 - 查找规则:访问对象属性 / 方法时,先查自身 → 沿
__proto__查原型 → 直到Object.prototype→ 仍无则返回undefined。