在 JavaScript 的世界里,对象的能力不是与生俱来的,
而是沿着一条看不见的链,一路“借”来的。
这条链,叫原型链;它的起点是.prototype,终点是null。
一、共享的力量:.prototype 的使命
假设我们想造很多辆车:
function Car(brand) {
this.brand = brand;
}
如果每辆车都要自己带一套“启动”“刹车”的方法,那内存开销太大了。于是 JavaScript 提供了一个共享空间:构造函数的 .prototype 属性。
Car.prototype.start = function() {
console.log(`${this.brand} 启动了!`);
};
现在,所有通过 new Car() 创建的实例,都能共享这个 start 方法:
const su7 = new Car("小米SU7");
const model3 = new Car("特斯拉");
console.log(su7.start === model3.start); // true ✅ 同一个函数
这就是 .prototype 的核心价值:一次定义,处处复用。
二、两种写法:精细添加 vs 整体重构
给原型添加方法,常见两种方式:
方式1:逐个挂载(推荐)
Car.prototype.start = function() { ... };
Car.prototype.stop = function() { ... };
- 不破坏原有结构
- 自动保留
constructor属性
方式2:整体赋值
Car.prototype = {
start() { ... },
stop() { ... }
};
- 看起来更整洁,但会丢失默认的
constructor - 因为
{}是一个全新对象,没有指向Car的constructor
✅ 修复方案:
Car.prototype = {
constructor: Car, // 手动补回
start() { ... },
stop() { ... }
};
小建议:日常开发优先用“逐个添加”,批量重构时记得补
constructor。
三、实例的“隐形纽带”:.__proto__
当你执行 const su7 = new Car("小米SU7"),JavaScript 会悄悄做一件事:
让
su7的__proto__指向Car.prototype
验证一下:
console.log(su7.__proto__ === Car.prototype); // true
这意味着:
su7自己没有start?- 没关系,JS 会自动去
su7.__proto__(即Car.prototype)里找。
这条“隐形纽带”,就是实例访问共享方法的桥梁。
⚠️ 注意:
__proto__是非标准属性,仅用于调试或理解。生产环境请用Object.getPrototypeOf(su7)。
四、标准 API 与回溯之锚:getPrototypeOf 和 constructor
更规范的方式:Object.getPrototypeOf()
Object.getPrototypeOf(su7) === Car.prototype; // true
这是 ECMAScript 标准方法,比 __proto__ 更可靠。
回溯源头:constructor
每个原型对象默认有一个 constructor 属性,指回它的构造函数:
console.log(Car.prototype.constructor === Car); // true
console.log(su7.constructor === Car); // true(通过原型链继承)
用途举例:
- 动态创建同类型对象:
new obj.constructor("新品牌") - 类型判断(需谨慎,因可被篡改)
五、为什么连 .toString() 都能用?
即使你没写任何方法,也能调用:
console.log(su7.toString()); // "[object Object]"
秘密在于:所有对象的原型链最终都通向 Object.prototype。
查找路径如下:
su7
→ su7.__proto__ = Car.prototype
→ Car.prototype.__proto__ = Object.prototype
→ 找到 Object.prototype.toString()
所以,toString、hasOwnProperty、valueOf 等方法,其实是“祖传”的。
你也可以覆盖它,让输出更友好:
Car.prototype.toString = function() {
return `🚗 ${this.brand}`;
};
console.log(String(su7)); // "🚗 小米SU7"
六、终点:null 为一切画上句号
原型链不会无限延伸。当我们继续往上走:
console.log(Object.prototype.__proto__); // null
null 表示“此处再无原型”。它是整个原型系统的终止符。
- 当 JS 查找属性一路走到
null仍未找到,就返回undefined - 这保证了查找过程必定终止,不会死循环
正如万物生于有,有生于无;
JavaScript 的对象能力,始于委托,终于null。
七、 图形快速理解总结
这张图可以帮助我们理解js原型的原理
✅ 结语:原型不是魔法,而是设计
.prototype 提供共享,__proto__ 建立链接,Object.prototype 提供基础能力,null 守住边界——
这四者共同构成了 JavaScript 灵活而强大的原型系统。
理解它,你不仅能写出更高效的代码,还能看懂 Vue、React 等框架底层的继承逻辑。
下次有人问:“JS 有类吗?”
你可以笑着说: “我们有原型,比类更自由。”