查看: 169|回复: 1

JavaScript原型链核心机制:prototype、__proto__与constructor深度解析

[复制链接]
发表于 3 小时前 | 显示全部楼层 |阅读模式
JavaScript的原型链是理解对象继承与属性查找的基础。很多开发者对prototype、__proto__以及constructor的关系容易混淆,尤其在实际编码中会遇到属性修改无效、原型链断裂等坑。本文通过具体代码实例,梳理这些核心概念,并分析常见误区。

一、构造函数与实例的基本关系

先看一个标准的构造函数示例:
  1. function Person(name, walk) {
  2.     this.name = name;
  3.     this.walk = walk;
  4.     this.getName = function() {
  5.         console.log(this.name);
  6.     };
  7. }
  8. Person.eat = function() {
  9.     console.log('eat');
  10. };
  11. Person.prototype.getWalk = function() {
  12.     console.log(this.walk);
  13. };
  14. const person = new Person('张三', '顺拐');
复制代码

这里,Person是一个构造函数,person是由new Person创建的实例。关键点在于:
- 构造函数Person本身是一个函数对象,它也有自己的原型链。
- 通过Person创建的实例person,其原型(__proto__)指向Person.prototype。
- 实例可以访问原型上的属性和方法,但自身并不拥有它们。

二、constructor属性的追溯

实例本身没有constructor属性,但当访问person.constructor时,JavaScript会沿着原型链向上查找,直到在Person.prototype上找到constructor。而Person.prototype.constructor默认指向Person本身。

所以:
  1. console.log(person.constructor === Person); // true
复制代码

构造函数本身也是对象,它的构造函数是Function。因此:
  1. console.log(Person.constructor === Function.prototype.constructor); // true
  2. console.log(Person.constructor === Function); // true
复制代码

注意,箭头函数没有prototype属性,因此不能作为构造函数使用,但仍然有__proto__,其原型指向Function.prototype。

三、prototype与__proto__的本质

1. 只有函数(function)才拥有prototype属性。普通对象没有prototype。
2. prototype是构造函数的属性,它定义了由该构造函数生成的实例的公共祖先。即实例可以继承prototype上的所有属性和方法。
3. 实例的__proto__指向其构造函数的prototype。即:
  
  1. console.log(person.__proto__ === Person.prototype); // true
复制代码
4. 所有函数(包括Object、Array等内置构造函数)都是Function的实例,因此它们的__proto__都指向Function.prototype。
  
  1. console.log(Object.__proto__ === Function.prototype); // true
  2.    console.log(Function.__proto__ === Function.prototype); // true  // Function自举
复制代码
5. 普通对象是Object的实例,因此普通对象的__proto__最终指向Object.prototype(除了Object.prototype本身)。
  
  1. console.log(Person.prototype.__proto__ === Object.prototype); // true
  2.    console.log(Function.prototype.__proto__ === Object.prototype); // true
复制代码
6. 为了避免循环,Object.prototype.__proto__指向null,这是原型链的终点。

四、完整的原型链路径

- 对于实例person:person -> Person.prototype -> Object.prototype -> null
- 对于构造函数Person:Person -> Function.prototype -> Object.prototype -> null
- 对于Function本身:Function -> Function.prototype -> Object.prototype -> null

理解这个链条后,属性查找就清晰了:当访问person.name时,优先找person自身的name属性;如果没有,则沿着__proto__向上查找直到null。

五、常见坑与实例属性/原型属性的区别

1. 在构造函数中使用this.xxx定义的属性,实际上是在实例上添加,而不是在构造函数本身上。例如:
  
  1. function Person() {
  2.        this.name = 15;
  3.    }
  4.    const p = new Person();
  5.    console.log(p.name); // 15
  6.    console.log(Person.name); // undefined(函数名name属性是函数自身属性,与实例无关)
复制代码

2. 如果同时定义了原型属性和实例属性,实例属性会遮盖原型属性:
  
  1. Person.prototype.name = 20;
  2.    console.log(p.name); // 15(实例属性优先)
复制代码

3. 固定值的属性建议直接放在原型上,避免每个实例重复创建:
  
  1. Person.prototype.name = '默认名字';
  2.    // 如果构造函数里没有this.name,则实例将继承这个值
复制代码

4. 修改Person.prototype的引用(如赋值为一个新对象)时,需注意实例的原型链条是否被切断:
   - 如果实例是在修改前创建的,实例的__proto__仍然指向旧的原型对象,无法访问新原型上的属性。
   - 如果实例是在修改后创建的,则__proto__指向新的原型对象。
   - 同时,新原型对象如果没有显式将constructor指回Person,则person.constructor会沿着新原型的__proto__继续查找,最终可能指向Object,造成原型链关系紊乱。
  
  1. Person.prototype = {
  2.        constructor: Person, // 必须手动修正,否则constructor指向Object
  3.        // 其他方法
  4.    };
复制代码

5. 通过实例无法修改原型上的属性。例如:
  
  1. person.name = '李四'; // 实际上是在person实例上新增了一个name属性,而非修改原型
  2.    delete person.name;   // 只会删除实例自身的name,原型上的name依然存在
复制代码
   注意:delete person.name即使person本身没有name属性,也会返回true,因为它执行了删除操作(未找到也算成功)。

六、总结

JavaScript原型链是隐式继承的实现基础。掌握constructor、prototype、__proto__这三者的关系,可以避免很多属性覆盖、原型断裂的陷阱。在实际开发中,建议优先将共享方法定义在prototype上,而将实例特有的属性定义在构造函数内部。当需要替换原型对象时,务必重新设置constructor指向。理解原型链也有助于深入理解JavaScript的继承模式(如组合继承、寄生组合继承等)。
回复

使用道具 举报

发表于 3 小时前 | 显示全部楼层

Re: JavaScript原型链核心机制:prototype、__proto__与constructor深度解析

感谢楼主的深度解析!这篇文章把 prototype、__proto__ 和 constructor 的关系讲得非常透彻,尤其是用代码一步步拆解原型链路径和常见坑,对我这种容易混淆概念的人帮助很大。之前一直没想通为什么修改 Person.prototype 的引用会导致旧实例的原型链断裂,看完你举的例子一下就明白了。另外,你强调箭头函数没有 prototype 所以不能做构造函数,这点也很关键,以前经常忽略。 想请教一个问题:当 Person.prototype 被整体替换成新对象后,如果新对象没有显式设置 constructor,instanceof 判断是否也会受影响?还是说 instanceof 只检查原型链上的对象而不依赖 constructor?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-6-12 15:10 , Processed in 0.029146 second(s), 17 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部