首页/文章/八股文

深入理解 JavaScript 原型与原型链:从关系图到核心逻辑 - heshanwan - 博客园

2026-08-19
18417 分钟
...

在 JavaScript 世界里,原型(Prototype)和原型链(Prototype Chain) 是理解对象继承、属性查找机制的基石。很多开发者初学时对它们 “又爱又恨”,这篇文章将结合经典关系图,用通俗易懂的方式拆解原型与原型链的核心逻辑,帮你彻底掌握这套机制!

 

一、先搞懂几个核心概念

在分析关系图前,先明确 JavaScript 中与原型相关的关键概念,避免后续混淆:

1. 函数对象与普通对象

  • 函数对象:由 Function 构造出来的对象,本质是可执行的函数(比如 function Foo(){}、内置的 Object()Function() )。
  • 普通对象:由构造函数(如 new Foo()new Object() )创建的实例,或直接字面量 {} 创建的对象。

关键区别:函数对象有 prototype 属性(用于关联原型对象),普通对象没有 prototype,但所有对象(包括函数对象)都有 __proto__ 属性(指向自身的原型)。

2. prototype vs __proto__ vs constructor

  • prototype函数对象专属的属性,指向一个 “原型对象”。当用这个函数作为构造函数创建实例时,实例的 __proto__ 会指向该 prototype
  • __proto__所有对象(包括函数) 都有的隐式原型属性,指向当前对象的 “原型对象”,是 JavaScript 引擎实现原型链查找的关键。
  • constructor原型对象 上的属性,指向创建该原型的 “构造函数”(形成循环引用,方便实例找到构造函数)。

二、拆解关系图:原型与原型链的核心链路

结合题目中的关系图,我们从 构造函数、实例对象、原型对象 三个维度,梳理它们的关联:

1. 自定义构造函数 Foo 的链路

假设我们写了一个构造函数 function Foo() {},它的原型链路是这样的:

(1)构造函数 Foo 自身

  • Foo 是函数对象,由 Function 构造而来(Foo = new Function(...) 的简化逻辑 )。
  • 所以:
    • Foo.__proto__ → 指向 Function.prototype(函数对象的原型是 Function 的原型 )。
    • Foo.prototype → 指向 Foo 的 “原型对象”(Foo.prototype 是普通对象,默认包含 constructor: Foo )。

(2)Foo 的实例(f1f2

用 new Foo() 创建实例 f1f2 时:

  • 实例的 __proto__ → 指向 Foo.prototype(构造函数的 prototype 成为实例的原型 )。
  • 所以:f1.__proto__ === Foo.prototypef2.__proto__ === Foo.prototype

(3)Foo.prototype 的链路

Foo.prototype 是普通对象,它的 __proto__ 指向谁?

  • 因为所有普通对象默认由 Object 构造,所以:Foo.prototype.__proto__ → 指向 Object.prototype(普通对象的原型链路起点 )。

2. 内置构造函数 Object 的链路

Object 是 JavaScript 内置的构造函数,用来创建普通对象(如 new Object(){} ),它的链路:

(1)构造函数 Object 自身

  • Object 是函数对象,同样由 Function 构造而来(Object = new Function(...) 逻辑 )。
  • 所以:
    • Object.__proto__ → 指向 Function.prototype(函数对象的原型统一关联 Function.prototype )。
    • Object.prototype → 指向 Object 的 “原型对象”(所有普通对象的最终原型之一 )。

(2)Object 的实例(o1o2

用 new Object() 创建实例 o1o2 时:

  • 实例的 __proto__ → 指向 Object.prototype(构造函数 Object 的 prototype 是实例原型 )。

(3)Object.prototype 的链路

Object.prototype 是 JavaScript 原型链的终点之一(最顶层普通对象原型 ),所以:Object.prototype.__proto__ → 指向 null(没有更上层的原型了 )。

3. 万物之源 Function 的链路

Function 是 JavaScript 中最特殊的构造函数,所有函数对象(包括 ObjectFooFunction 自身)都由它构造,链路非常 “递归”:

(1)构造函数 Function 自身

  • Function 是函数对象,同时它也是自身的实例(Function = new Function(...) ,自己构造自己 )。
  • 所以:
    • Function.__proto__ → 指向 Function.prototype(自己的原型指向自己的 prototype ,形成递归 )。
    • Function.prototype → 指向 Function 的 “原型对象”,它的 __proto__ 又指向 Object.prototype(因为 Function.prototype 本质是普通对象 )。

(2)Function.prototype 的链路

Function.prototype 是函数对象的原型,它的 __proto__Function.prototype.__proto__ → 指向 Object.prototype(因为 Function.prototype 是普通对象,最终归属 Object 原型链 )。

三、原型链的本质:属性查找的 “追溯链”

理解了原型关联后,原型链 的作用就清晰了:

当你访问一个对象的属性(如 f1.name )时,JavaScript 引擎会:

  1. 先在对象自身找(f1 有没有 name 属性 );
  2. 如果没找到,就顺着 __proto__ 去原型对象里找(f1.__proto__ 即 Foo.prototype 里找 );
  3. 如果还没找到,继续顺着 Foo.prototype.__proto__ 去 Object.prototype 里找;
  4. 直到找到属性,或追到 __proto__ 为 null(原型链终点 ),返回 undefined

举个例子

function Foo() {}
Foo.prototype.sayHi = function() { console.log('Hi~'); };

const f1 = new Foo();
f1.toString(); 

四、原型与原型链的实际应用

理解这套机制后,就能明白 JavaScript 中 “继承”“属性复用” 的底层逻辑,典型场景:

1. 原型继承

通过修改 __proto__ 或利用 prototype,让对象继承其他对象的属性:

const parent = { name: 'Parent' };
const child = { age: 18 };
child.__proto__ = parent; 

console.log(child.name); 

2. 构造函数复用方法

把方法挂载到 prototype 上,所有实例共享方法(节省内存):

function Person(name) {
  this.name = name;
}
Person.prototype.sayName = function() { 
  console.log(this.name); 
};

const p1 = new Person('Alice');
const p2 = new Person('Bob');
p1.sayName(); 
p2.sayName(); 

3. 理解内置对象的原型

比如数组 [] 的原型链:

const arr = [1, 2]; 

五、总结:原型与原型链的核心逻辑

  1. 所有对象(包括函数) 都有 __proto__,指向自己的原型;
  2. 函数对象 额外有 prototype,用于关联实例的 __proto__
  3. 原型链 是属性查找的链路,从自身到 __proto__ 层层追溯,直到 null
  4. Function 是 “函数对象的源头”,Object.prototype 是 “普通对象原型链的终点”,共同构成 JavaScript 对象体系的基石。

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
八股文2026-08-27
定时器按顺序播放多个音频,切后台音频会乱
本身不会补触发,但 它会被系统级冻结 ——iOS Safari 后台完全停止计时,切回前台后 只补执行一次 (不是不补,而是"缺失的中间状态补不上")。间隔短的不会出大问题, 间隔长的会出现"音频流被压缩" ——切回来后本该播 5 分钟的间隙,实际只过了 3 分钟,结果整段对不上。 核心原则 :定时器只能用来"提醒一次", 真正决定"该不该播"的是绝对时间戳 。 setTimeout 在后台会发生什么(按平台) | 平台 | 后台行为...
面试
八股文2026-08-27
线上项目白屏的原因
白屏的本质是 渲染管线某一环断了 ——可能是资源、JS、接口、路由、样式、兼容性任一环节出问题。排查按"控制台 → 网络 → DOM → 环境"四步定位。 本质(前端类比) 把网页想成一栋楼: 白屏 ≠ 一定是同一种原因——这是面试想听的层次。 六大类原因(按出现频率) | 类别 | 典型表现 | 真实案例 | | : | : | : | | ① 资源加载失败 | DOM 是空的 | 入口 JS 404、CDN 挂了、CSS 阻塞 |...
面试
八股文2026-08-27
背景图就是 1MB 大图,怎么优化
1MB 大背景图优化分 三步 :压缩体积(10 30x)、按需加载(按设备/视口/网速)、渲染期优化(GPU 合成)。背景图跟 不同——它是 CSS,不走浏览器的原生懒加载机制,得手动处理。 背景图 vs 的关键区别(先讲清楚这个) 这是面试官想听的"针对性认知"——背景图不是普通图片,不能套通用方案。 三步优化(按优先级) 第 1 步:压缩体积(最重要,立竿见影) 1MB 的来源一般是这几种 ,对应解决方案: | 原始问题 | 体积来...
面试
八股文2026-08-27
项目里很多图片和视频,怎么优化
图片视频优化分 四层 :网络层(CDN/格式)、加载层(懒加载/预加载)、渲染层(解码/缓存)、业务层(按需/降级)。面试要把这四层都讲到位才算有体系。 四层优化模型 第 1 层:网络层(省钱、省时间) 核心目标:让资源体积小、让用户拿到资源快 | 手段 | 作用 | 关键点 | | : | : | : | | 图片格式 | WebP/AVIF 比 JPEG 小 25 50% | 兼容 fallback | | 视频格式 | H.265...
面试
八股文2026-08-20
Embedding 向量模型:从语义表示到相似度计算
前言 大模型「读懂」文字靠的是 token,但 token 之间只有离散的编号关系,模型并不知道「苹果」和「李子」在语义上很近。要让程序能「理解」两段文字的相似程度,必须先把文本映射成一个 高维向量 ,再用几何方法比较。这一步就是 Embedding。 本篇基于我本地 今天的真实代码,从语义表示讲到余弦相似度,并复盘几个踩过的真实坑。 一、为什么需要 Embedding 传统关键词检索是「字面匹配」: Embedding 做的是「语义匹...
面试
八股文2026-08-19
前端面试100题
前端面试 100 题 适用方向:中高级前端 / React / Vue / Next.js / Nuxt / TypeScript / 工程化 / 实时通信 / Electron / Node.js / AI 应用前端 使用方式:优先掌握“标准回答”,再练“面试官追问”,最后把“结合你的简历怎么答”组织成自己的项目故事。 说明 “结合你的简历怎么答”只使用你简历中已经出现的项目与技术事实。 “标准回答 / 追问”属于通用前端知识总结,用...
面试

评论

请登录后发表评论

去登录
加载评论中...

目录