Modern JavaScript Tutorial 深度解析:构造函数 `F.prototype`、`new` 与 `constructor` 属性的完整指南

发布时间:2026/10/6 12:14:07

Modern JavaScript Tutorial 深度解析:构造函数 `F.prototype`、`new` 与 `constructor` 属性的完整指南 文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载导读在 JavaScript 中通过构造函数new F()创建对象时F.prototype属性决定了新对象的内部[[Prototype]]指向——这是语言中最古老、使用最广泛的设置原型的方式也是理解后续class语法与原型链机制的地基。本指南以 Modern JavaScript Tutorial 的 F.prototype 章节 为主体结合同章节配套习题与 构造函数与 new、原型继承 等仓库章节系统讲解F.prototype的工作时机、默认constructor属性的行为、替换原型带来的陷阱与正确修复方式并给出可直接运行的验证代码。读完本文你将能准确回答new是如何借助prototype设置继承关系的为什么覆盖Rabbit.prototype后rabbit.constructor会失效以及如何安全地利用constructor创建同类型新对象等问题。一、从[[Prototype]]说起构造函数如何参与原型继承在 原型继承章节 中我们已知每个 JavaScript 对象都有一个隐藏的内部属性[[Prototype]]它要么是null要么指向另一个对象称为原型。当读取对象上不存在的属性时JavaScript 会自动沿[[Prototype]]链向上查找这就是原型式继承。而通过构造函数创建对象时new操作符恰好会利用构造函数上的一个普通属性prototype来为新对象指定[[Prototype]]。也就是说如果F.prototype是一个对象那么new F()创建的新对象其[[Prototype]]会被设置为F.prototype所指向的对象。这里的F.prototype并不是什么特殊语法它就是挂在构造函数F上的一个名为prototype的普通属性——注意不要与内部属性[[Prototype]]混淆二者是完全不同的东西。基础示例让所有兔子继承动物let animal { eats: true }; function Rabbit(name) { this.name name; } Rabbit.prototype animal; // 关键赋值 let rabbit new Rabbit(White Rabbit); // rabbit.__proto__ animal alert( rabbit.eats ); // trueRabbit.prototype animal这句话的字面含义是当创建一个new Rabbit时把它的[[Prototype]]赋值为animal。于是rabbit自身没有eats属性但可以通过原型链从animal上读到它图中水平箭头表示prototype是一个普通属性垂直箭头表示rabbit对animal的继承关系即[[Prototype]]引用。这正是理解本节的关键心智模型构造函数通过一个普通属性把继承关系注入到每次new创建的新对象上。历史背景为什么老代码大量依赖它值得强调的是原型式继承从 JavaScript 诞生起就是语言核心特性之一。但在早期开发者并没有直接访问[[Prototype]]的途径唯一可靠的做法就是本文描述的——构造函数上的prototype属性。因此直到今天仍有大量现存脚本沿用这一模式。这正是我们有必要透彻理解它的原因。现代的Object.create、Object.setPrototypeOf等替代方案在 原型方法章节 中有专门讲解。关键时机F.prototype只在new F()调用的那一刻起作用F.prototype属性仅在调用new F时被读取用来为新对象设置[[Prototype]]。它不是一个持续生效的引用function Rabbit() {} Rabbit.prototype { eats: true }; let rabbit1 new Rabbit(); // 此时 rabbit1 的 [[Prototype]] 是 { eats: true } Rabbit.prototype { eats: false, jumps: true }; // 之后才改写 prototype let rabbit2 new Rabbit(); // rabbit2 的 [[Prototype]] 是 { eats: false, jumps: true } alert(rabbit1.eats); // true —— 旧对象保留旧的 [[Prototype]] alert(rabbit2.eats); // false —— 新对象获得新的 [[Prototype]]也就是说已创建的对象不受后续F.prototype改写的任何影响只有之后新建的对象会使用新值。这一点在后面的习题验证中还会反复出现。二、默认的F.prototype与constructor属性每个函数包括普通函数都天然拥有prototype属性即使我们完全没有提供它。默认的prototype是一个只含一个属性constructor的对象且constructor指回函数自身function Rabbit() {} /* 默认 prototype 相当于 Rabbit.prototype { constructor: Rabbit }; */可以直接验证function Rabbit() {} // 默认情况下 // Rabbit.prototype { constructor: Rabbit } alert( Rabbit.prototype.constructor Rabbit ); // true如果我们什么都不做那么所有通过new Rabbit()创建的对象都会通过[[Prototype]]继承到这个constructor属性function Rabbit() {} // 默认情况下 // Rabbit.prototype { constructor: Rabbit } let rabbit new Rabbit(); // 继承自 {constructor: Rabbit} alert(rabbit.constructor Rabbit); // true来自原型利用constructor创建同类型对象constructor属性的实用价值在于当我们手里有一个对象、却不知道它是用哪个构造函数创建时例如来自第三方库可以通过obj.constructor再造一个同类对象function Rabbit(name) { this.name name; alert(name); } let rabbit new Rabbit(White Rabbit); let rabbit2 new rabbit.constructor(Black Rabbit); // 等价于 new Rabbit(Black Rabbit)这正是 配套习题 Create an object with the same constructor 探讨的场景new obj.constructor()是否可靠完全取决于constructor属性是否仍指向正确的构造函数。只要没有覆盖过默认prototype它就必然可靠function User(name) { this.name name; } let user new User(John); let user2 new user.constructor(Pete); alert( user2.name ); // Pete成功它之所以成功是因为User.prototype.constructor User见 该习题解答。三、最大的坑JavaScript 并不保证constructor始终正确JavaScript 自身并不保证constructor值始终正确。它只是默认出现在函数的prototype里仅此而已。之后这个属性会变成什么样完全取决于我们自己。尤其是如果整体替换掉默认的prototype新的 prototype 对象里就不会再有constructor了。例如function Rabbit() {} Rabbit.prototype { jumps: true }; let rabbit new Rabbit(); alert(rabbit.constructor Rabbit); // false此时rabbit沿原型链查到的constructor是更上层Object.prototype.constructor即内置的Object而不是Rabbit。习题 Changing prototype 与解答 solution.md 里new obj.constructor()失效的完整链路正是这样的在user自身找constructor——没有沿原型链到User.prototype——也没有因为被User.prototype {}覆盖时忘记补上再往上到内置Object.prototype——这里存在Object.prototype.constructor Object最终执行的是new Object(Pete)name自然成了undefined。也就是说丢失constructor的后果是同类型对象的复制静默地变成了普通Object创建代码不报错但结果完全错误。四、两种保住constructor的正确姿势既然整块替换会丢失constructor官方教程给出了两个修复方案方案一不要整体覆盖只增删默认 prototype 的属性保留默认prototype对象其中含constructor只往上面添加/删除属性function Rabbit() {} // 不要整体覆盖 Rabbit.prototype // 只往上面添加属性 Rabbit.prototype.jumps true // 默认的 Rabbit.prototype.constructor 得以保留方案二整体覆盖时手动补回constructor如果确实需要用一个全新的对象替换prototype记得把constructor一并写进去Rabbit.prototype { jumps: true, constructor: Rabbit }; // 因为手动补上了 constructor现在它也正确两种方式都能保证rabbit.constructor Rabbit从而让new obj.constructor()这类代码继续可靠工作。五、配套习题实战改写prototype的四种情形验证仓库中 1-changing-prototype 习题从四个角度测试对F.prototype语义的理解其完整解答见 solution.md。初始代码如下function Rabbit() {} Rabbit.prototype { eats: true }; let rabbit new Rabbit(); alert( rabbit.eats ); // true情形 1整体改写Rabbit.prototype {}function Rabbit() {} Rabbit.prototype { eats: true }; let rabbit new Rabbit(); Rabbit.prototype {}; // 改写发生在 new 之后 alert( rabbit.eats ); // 答案true。Rabbit.prototype的赋值只对之后新建的对象设置[[Prototype]]对已存在的rabbit毫无影响——它的[[Prototype]]仍是旧对象{ eats: true }。情形 2修改Rabbit.prototype.eats falsefunction Rabbit() {} Rabbit.prototype { eats: true }; let rabbit new Rabbit(); Rabbit.prototype.eats false; alert( rabbit.eats ); // 答案false。对象是按引用赋值的。Rabbit.prototype指向的对象并没有被复制rabbit的[[Prototype]]与Rabbit.prototype引用的是同一个对象。因此通过任一引用修改其内容另一引用都会看到变化。情形 3delete rabbit.eatsfunction Rabbit() {} Rabbit.prototype { eats: true }; let rabbit new Rabbit(); delete rabbit.eats; alert( rabbit.eats ); // 答案true。所有delete操作都直接作用于对象自身。rabbit自己没有eats属性它只在原型上所以删除操作不产生任何效果属性仍可从原型读到。这正是 原型继承章节 强调的写/删操作不走原型原则。情形 4delete Rabbit.prototype.eatsfunction Rabbit() {} Rabbit.prototype { eats: true }; let rabbit new Rabbit(); delete Rabbit.prototype.eats; alert( rabbit.eats ); // 答案undefined。这次删除的是原型对象上的eats属性它从整个原型链中消失因此rabbit.eats再也查不到返回undefined。这四个情形共同印证了第三节的核心结论F.prototype只在new的瞬间被消费一次对象内容按引用共享读写删操作只在对象自身生效。它们是排查原型相关 bug 时最有价值的判断依据。六、总结三条必须记住的规则本节内容看似简单但包含几条极易踩坑的要点官方教程的 Summary 将其凝练为F.prototype不要与[[Prototype]]混淆会在new F()被调用时为新对象的[[Prototype]]赋值。它只在构造函数配合new使用时才有这种特殊效果。F.prototype的值必须是对象或null其他值不会生效。这也与原型继承章节中[[Prototype]]只能是对象或null的约束一脉相承。默认情况下所有函数都有F.prototype { constructor: F }因此可以通过对象的constructor属性拿到它的构造函数。但一旦整体覆盖prototype而不补回constructor这个能力就会静默丢失。最后还要强调一个易混淆点在普通对象上prototype毫无特殊之处它只是一个普通属性名let user { name: John, prototype: Bla-bla // 没有任何魔法 };只有挂在构造函数上、并经由new调用时prototype才承担起设置[[Prototype]]的职责。七、延伸阅读构造函数与 operator new理解new的完整执行流程创建空对象、绑定this、返回this与本节的直接前置知识。原型继承Prototypal inheritance[[Prototype]]、原型链查找与this语义的完整介绍。原型方法没有proto的对象现代替代方案Object.getPrototypeOf / setPrototypeOf / create与原型历史的演进脉络。原生原型Native prototypes内置对象如Object.prototype的constructor如何在实践中影响上面的查找链路。配套习题与解答Changing prototype、Create an object with the same constructor。理解F.prototype之后后续学习 class 语法 与更高级的继承模式会顺畅得多——class正是建立在这套原型机制之上的语法糖。赞分享文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载相关推荐用 new obj.constructor() 克隆同构造器对象Modern JavaScript Tutorial 的 F.prototype 与 constructor 属性详解用 new obj.constructor 克隆同构造器对象Modern JavaScript Tutorial 的 F.prototype 与 constr文档/教程前端wasm-bindgen 指南用 constructor 属性将 JavaScript 构造函数new 运算符绑定到 Rustwasm bindgen 指南用 constructor 属性将 JavaScript 构造函数 new 运算符绑定到 Rust wasm_bindgen开发工具Modern JavaScript Tutorial深入解析 new Function 动态构造函数的语法、闭包边界与压缩器安全性Modern JavaScript Tutorial深入解析 new Function 动态构造函数的语法、闭包边界与压缩器安全性 new Function文档/教程前端上一篇wxBot代码规范与最佳实践编写高质量机器人代码的完整指南下一篇MLKit Android机器学习工具包终极集成与最佳实践指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/10/6 13:04:09

电气互联系统有功-无功协同优化:碳约束下的建模与求解

1. “碳中和”目标下的电气互联系统:为什么非做有功-无功协同不可 先说一个我在做这类项目时最深的体会:很多做电力系统优化的同学,把重点全放在有功调度上,无功这块要么忽略,要么用固定的功率因数折算一下。但在“碳中…

2026/10/6 13:04:09

基于SpringBoot+Vue的短链接流量分析与可视化系统实战

做短链接流量分析这个事,我一开始是被临时拉去救火的。业务方要做一场裂变活动,投放了一堆带参数链接,结果后台只能看到打开人数,来源渠道、设备分布、时段趋势全是一团黑。市面上的第三方统计平台要么收费贵,要么数据…

2026/10/6 13:04:09

华三交换机恢复出厂:命令行、BootROM与硬件复位全攻略

这是很典型的网络运维活儿。华三交换机恢复出厂,听起来就是个reset的事,但实际工作中,因为场景不同(密码丢了、设备要退网、配置乱了要清盘、二手设备要重写),你需要的“恢复出厂”方式完全不一样。我这几年…

2026/10/6 13:04:09

基于JSP+Servlet+MySQL的智能小区物业管理系统实战解析

很多人做JavaWeb课程设计或毕业设计时,第一反应就是去网上找个Spring Boot Vue的模板,改改数据库交差完事。但真到了答辩或复试,一问事务怎么控制的,一问Servlet生命周期,直接卡壳。我前阵子带着一个学弟把这个“基于…

2026/10/6 13:04:09

数据结构课设核心:用C语言实现迷宫求解的栈与队列本质

简介:本资源是面向高校计算机专业本科生的数据结构课程设计实践项目,聚焦经典图搜索问题——老鼠走迷宫的C完整实现,旨在帮助学习者深入理解栈、队列、图遍历等核心数据结构与DFS算法的实际应用。压缩包共27个文件,包含可直接运行…

2026/10/6 12:59:09

机器学习驱动的自动音乐生成优化:从符号建模到可控采样

简介:基于机器学习的自动音乐生成软件,核心采用长短期记忆网络模型,代替常见的简单循环神经网络与WaveNet方案,在最少人为干预下生成一段短曲并播放,缓解同质化问题。资源面向深度学习与音乐生成交叉方向的学习者&…

2026/10/5 6:32:56

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起:为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高,很多人第一次听到会以为是某个新模型的名字,其实它更像是一种思路——把Jev模型的能力当作底座,通过Agent的方式去接管浏览器&#xf…

2026/10/6 4:01:51

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同":多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西,大概率会有一种感觉:单个 Agent 能做的事情,其实很快就摸到天花板了。你给它一个提示词,挂几个工…

2026/10/5 17:38:27

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/6 0:03:23

MR25H40CDF+STM32F031C6工业级高可靠数据存储方案

1. 项目概述:为什么在工业现场非得用 MR25H40CDF 配 STM32F031C6 做数据存储?在工厂产线的 PLC 控制柜里、在风电变流器的散热片背面、在矿井监测终端的金属外壳下,你经常能看到一块指甲盖大小的黑色芯片——它既不是 Flash,也不是…

2026/10/6 0:03:23

MRAM+STM32工业断电数据保全实战指南

1. 项目概述:为什么在工业现场非得用 MR25H40CDF 配 STM32F031C6 做数据存储?在工厂产线的PLC柜里、在野外无人值守的环境监测终端里、在高速运转的包装机控制板上,你经常能看到一块指甲盖大小的黑色芯片,旁边贴着“MR25H40CDF”丝…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑