导读:本期聚焦于小伙伴创作的《javascript原型链是什么?如何理解基于原型的继承机制?》,敬请观看详情。为什么在JavaScript里明明没有写继承代码,对象却能调用到父级的方法?这背后靠的是原型链在起作用。每个函数创建时都会附带一个prototype属性,它指向一个原型对象,实例通过内部指针__proto__连接到这个对象。当访问某个属性时,引擎会先查自身,再沿原型链向上找,直到Object.prototype为止。理解这套基于原型的委托机制,比死记类继承更重要,它能解释方法共享、属性遮蔽和instanceof判断逻辑。本文从内存结构、查找规则和手写继承三个角度拆开讲清。

在JavaScript中,原型链是对象之间实现属性与方法共享的核心机制。与基于类的语言不同,JavaScript采用基于原型的继承,每一个对象都有一个内部属性指向它的原型,当访问某个成员时,如果自身不存在,就会顺着这条隐式链接向上查找。这种结构不是通过复制,而是通过委托来完成复用。

javascript原型链是什么?如何理解基于原型的继承机制?

原型与原型对象的底层结构

每个函数在被创建时,都会自动获得一个prototype属性,这个属性指向一个普通对象,也就是我们常说的原型对象。构造函数通过new操作符生成实例时,实例内部会有一个隐式引用(在多数浏览器中表现为__proto__)指向构造函数的prototype。这意味着所有由同一个构造函数创建的实例,共享同一块原型内存,方法定义在原型上就能被全部实例访问,而不必在每个实例里都复制一份。

我们可以用一段简单代码观察这种关系。下面定义了一个构造函数Person,并在其原型上挂载了sayHi方法,随后创建实例验证原型指向。

function Person(name) {
  this.name = name;
}
Person.prototype.sayHi = function() {
  return 'Hi, ' + this.name;
};
var p = new Person('Tom');
console.log(p.__proto__ === Person.prototype); // true
console.log(p.sayHi()); // Hi, Tom

从内存角度看,p自身只保存了name属性,sayHi并不在p里,而是通过__proto__找到Person.prototype才拿到。如果直接在p上添加同名方法,就会形成属性遮蔽,原型上的方法不会被修改,只是被实例自身覆盖。这种结构带来了极低的内存开销,也解释了为何大规模对象系统中原型方法非常常见。

属性查找规则与链式继承逻辑

当我们读取obj.prop时,JavaScript引擎首先看obj自身是否有该属性;如果没有,就访问obj.__proto__,再看其原型;若仍没有,继续向上,直到抵达Object.prototype。一旦在Object.prototype上也找不到,就会返回undefined。这条由__proto__串联起来的路径就是原型链。它本质上是一种委托链,而非传统语言里的复制式继承。

基于这种规则,我们可以手动构造继承。一种经典写法是让子构造函数的原型指向父构造函数的实例,从而把两条原型链接起来。下面代码展示了如何用这种方式实现Student继承Person

function Student(name, grade) {
  Person.call(this, name);
  this.grade = grade;
}
Student.prototype = new Person();
Student.prototype.constructor = Student;
Student.prototype.showGrade = function() {
  return this.name + ' is in grade ' + this.grade;
};
var s = new Student('Lucy', 3);
console.log(s.sayHi()); // Hi, Lucy
console.log(s.showGrade()); // Lucy is in grade 3

上述代码中,Student.prototype变成了Person的实例,因此s在自身找不到sayHi时,会经过Student.prototype再找到Person.prototype。这种链式的查找保证了方法复用。同时要注意constructor的修正,否则s.constructor会错误地指向Person,影响后续类型判断与代码可读性。

使用instanceof判断类型时,底层就是沿着原型链检查构造函数的prototype是否出现在对象的链上。所以s instanceof Persontrue,因为Person.prototype确实在s的原型链中。理解这一点,就不会再把instanceof简单看作类关系,而明白它是原型连通性的检测。

现代语法下的原型继承与实践建议

ES6引入了class关键字,让写法更接近传统面向对象语言,但底层依然是基于原型的机制。class只是语法糖,通过extends实现的继承,在引擎中仍靠__proto__与原型对象串联。使用class能减少手动绑定原型的错误,例如不再需要显式写Student.prototype = new Person(),而是由编译器处理原型连接。

下面用class改写前面的例子,逻辑完全一致,但结构更清晰,也避免了constructor指向错乱的问题。

class Person {
  constructor(name) {
    this.name = name;
  }
  sayHi() {
    return 'Hi, ' + this.name;
  }
}
class Student extends Person {
  constructor(name, grade) {
    super(name);
    this.grade = grade;
  }
  showGrade() {
    return this.name + ' is in grade ' + this.grade;
  }
}
const s2 = new Student('Bob', 5);
console.log(s2.sayHi());
console.log(s2.showGrade());

在实际项目中,建议把共用方法放在原型或class的方法定义中,而不是在构造函数体内用this.method = function(){}的方式,否则每个实例都会创建新的函数副本,失去原型共享的意义。对于需要私有状态的数据,再放到构造函数里用this绑定。此外,当对象层级很深时,过长的原型链会带来轻微的查找成本,但比起内存浪费,这通常可以接受。

理解基于原型的继承,关键是放弃复制思维,接受委托思维。对象不是从父级拷贝成员,而是在需要时顺着链去借。掌握了原型链,你就能准确预测任意属性的来源、解释方法覆盖现象,也能在调试时快速定位为什么某个属性读到了意料之外的值。这是深入JavaScript无法绕开的一块基石。

javascriptprototype原型链修改时间:2026-08-16 10:54:13

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。