脚本专家 发表于 2026-6-12 12:00:00

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

JavaScript的原型链是理解对象继承与属性查找的基础。很多开发者对prototype、__proto__以及constructor的关系容易混淆,尤其在实际编码中会遇到属性修改无效、原型链断裂等坑。本文通过具体代码实例,梳理这些核心概念,并分析常见误区。

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

先看一个标准的构造函数示例:


function Person(name, walk) {
    this.name = name;
    this.walk = walk;
    this.getName = function() {
      console.log(this.name);
    };
}

Person.eat = function() {
    console.log('eat');
};

Person.prototype.getWalk = function() {
    console.log(this.walk);
};

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本身。

所以:

console.log(person.constructor === Person); // true


构造函数本身也是对象,它的构造函数是Function。因此:

console.log(Person.constructor === Function.prototype.constructor); // true
console.log(Person.constructor === Function); // true


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

三、prototype与__proto__的本质

1. 只有函数(function)才拥有prototype属性。普通对象没有prototype。
2. prototype是构造函数的属性,它定义了由该构造函数生成的实例的公共祖先。即实例可以继承prototype上的所有属性和方法。
3. 实例的__proto__指向其构造函数的prototype。即:
   
console.log(person.__proto__ === Person.prototype); // true

4. 所有函数(包括Object、Array等内置构造函数)都是Function的实例,因此它们的__proto__都指向Function.prototype。
   
console.log(Object.__proto__ === Function.prototype); // true
   console.log(Function.__proto__ === Function.prototype); // true// Function自举

5. 普通对象是Object的实例,因此普通对象的__proto__最终指向Object.prototype(除了Object.prototype本身)。
   
console.log(Person.prototype.__proto__ === Object.prototype); // true
   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定义的属性,实际上是在实例上添加,而不是在构造函数本身上。例如:
   
function Person() {
       this.name = 15;
   }
   const p = new Person();
   console.log(p.name); // 15
   console.log(Person.name); // undefined(函数名name属性是函数自身属性,与实例无关)


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


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


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


5. 通过实例无法修改原型上的属性。例如:
   
person.name = '李四'; // 实际上是在person实例上新增了一个name属性,而非修改原型
   delete person.name;   // 只会删除实例自身的name,原型上的name依然存在

   注意:delete person.name即使person本身没有name属性,也会返回true,因为它执行了删除操作(未找到也算成功)。

六、总结

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

热心网友2 发表于 2026-6-12 12:05:00

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

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

热心网友1 发表于 2026-6-19 20:30:03

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

这篇解析太清晰了,楼主把 `prototype`、`__proto__` 和 `constructor` 的关系理得明明白白。特别是“常见坑”那部分,原型引用替换后忘记重置 `constructor` 导致链条断裂的问题,我之前就踩过,当时 debug 半天才发现是 constructor 指向了 Object。另外箭头函数不能当构造函数这点也提醒得很到位,不少人容易忽略。感谢楼主分享,已收藏。

热心网友1 发表于 2026-6-19 22:35:00

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

感谢楼主的详细解析!这篇文章把prototype、__proto__和constructor的关系梳理得非常清晰,特别是用Person构造函数一步步拆解实例与原型链的对应关系,对我理解JavaScript的继承机制帮助很大。尤其是“原型链断裂”和“constructor指向被切断”的提醒很实用,之前写代码时没注意新原型对象忘记指回constructor,结果导致instanceof判断出错,看了你的解释终于明白根源了。另外,箭头函数没有prototype这点也容易忽略,以后用箭头函数当构造函数时得留个心眼。期待你后续更多关于原型链实际应用(比如混入、寄生组合继承)的案例分享!
页: [1]
查看完整版本: JavaScript原型链核心机制:prototype、__proto__与constructor深度解析