当前位置: 首页 > news >正文

彻底搞懂 prototype 和 __proto__

一、先理清核心定义:谁拥有prototype__proto__

特性prototypeproto(隐式原型)
归属只有函数拥有(所有函数默认自带)只有对象拥有(所有对象,包括函数)
本质普通对象(Function.prototype 除外)普通对象
作用存储构造函数的「公共属性 / 方法」,供实例共享指向当前对象的「原型对象」,构建原型链
是否可直接修改可手动修改(常用)非标准属性(ES6 后推荐用 Object.getPrototypeOf),不建议直接修改

关键结论:

  • 当函数作为构造函数使用时,prototype才有用;普通函数的prototype无意义。
  • 所有对象(包括函数对象、实例对象)的__proto__,最终都会指向某个构造函数的prototype

二、prototype详解:构造函数的「公共仓库」

1. 基础用法:给构造函数挂载公共方法 / 属性
// 1. 构造函数(函数拥有prototype) function Person(name) { this.name = name; // 实例自身属性(每个实例独立) } // 2. 给Person的prototype挂载公共方法(所有实例共享) Person.prototype.sayHi = function() { console.log(`我是${this.name}`); }; Person.prototype.age = 18; // 公共属性 // 3. 创建实例 const p1 = new Person("张三"); const p2 = new Person("李四"); // 验证:所有实例共享prototype中的内容 console.log(p1.age); // 18(从Person.prototype获取) console.log(p2.age); // 18 console.log(p1.sayHi === p2.sayHi); // true(内存复用,只有一份方法) // 验证:prototype的归属(只有函数有) console.log(Person.hasOwnProperty("prototype")); // true(函数有prototype) console.log(p1.hasOwnProperty("prototype")); // false(实例对象无prototype)
2.prototype的隐藏属性:constructor

每个函数的prototype都自带一个constructor属性,指向原构造函数(可理解为「原型的反向指针」)。

// 接上面的代码 console.log(Person.prototype.constructor === Person); // true // 实例可通过__proto__访问constructor,确认自己的构造函数 console.log(p1.__proto__.constructor === Person); // true console.log(p1.constructor === Person); // true(自动沿原型链查找) // 注意:如果手动覆盖prototype,会丢失constructor,需手动恢复 Person.prototype = { // 手动覆盖后,constructor指向Object,而非Person sayHello: function() { console.log("覆盖原型"); } }; const p3 = new Person("王五"); console.log(p3.constructor === Person); // false console.log(p3.constructor === Object); // true // 修复constructor Person.prototype.constructor = Person; console.log(p3.constructor === Person); // true

三、__proto__详解:对象的「原型指针」

1. 基础逻辑:实例的__proto__指向构造函数的prototype

当用new 构造函数()创建实例时,JS 会自动给实例添加__proto__属性,指向构造函数的prototype

function Cat(name) { this.name = name; } Cat.prototype.color = "orange"; const cat1 = new Cat("橘猫"); // 核心关联:实例.__proto__ === 构造函数.prototype console.log(cat1.__proto__ === Cat.prototype); // true // 实例访问color时,自身没有 → 沿__proto__找Cat.prototype → 找到color console.log(cat1.color); // orange
2. 所有对象都有__proto__(包括函数对象)

函数本身也是对象,所以函数也有__proto__,指向Function.prototype(所有函数的原型):

// 函数的__proto__指向Function.prototype console.log(Cat.__proto__ === Function.prototype); // true console.log(Function.__proto__ === Function.prototype); // true(特殊:Function自身的__proto__指向自己的prototype) // Object是函数,它的__proto__也指向Function.prototype console.log(Object.__proto__ === Function.prototype); // true // 普通对象的__proto__指向Object.prototype const obj = {}; console.log(obj.__proto__ === Object.prototype); // true

四、原型链:prototype__proto__的联动

原型链的本质是:对象通过__proto__逐级指向父级构造函数的prototype,直到Object.prototype.__proto__ = null

可视化原型链(以cat1为例):
cat1(实例) ↓ __proto__ Cat.prototype(原型对象) ↓ __proto__ Object.prototype(顶级原型) ↓ __proto__ null(原型链终点)
代码验证原型链查找规则:
// 接上面的Cat示例 // 1. 查找cat1.toString():自身没有 → __proto__找Cat.prototype(没有)→ __proto__找Object.prototype(有) console.log(cat1.toString()); // [object Object](来自Object.prototype) // 2. 验证原型链终点 console.log(Cat.prototype.__proto__ === Object.prototype); // true console.log(Object.prototype.__proto__); // null // 3. 不存在的属性:遍历完原型链返回undefined console.log(cat1.abc); // undefined(cat1 → Cat.prototype → Object.prototype → null,均无abc)

五、常见避坑点 & 最佳实践

1. 不要直接修改__proto__

__proto__是 ES6 之前的非标准属性,虽然主流浏览器支持,但修改性能差(会破坏原型链优化)。推荐用标准 API:

const obj = {}; // 替代 __proto__ 读取 console.log(Object.getPrototypeOf(obj) === Object.prototype); // true // 替代 __proto__ 修改(不推荐频繁修改) const protoObj = { x: 10 }; Object.setPrototypeOf(obj, protoObj); console.log(obj.x); // 10
2. 区分「实例属性」和「原型属性」
function Dog(name) { this.name = name; } Dog.prototype.age = 2; const dog1 = new Dog("旺财"); dog1.age = 3; // 给实例自身添加age,覆盖原型的age console.log(dog1.age); // 3(自身属性优先) // 删除自身属性后,恢复读取原型属性 delete dog1.age; console.log(dog1.age); // 2(从原型读取)
3.inhasOwnProperty的区别(原型链影响)
// 接上面的Dog示例 console.log("age" in dog1); // true(in检查整个原型链) console.log(dog1.hasOwnProperty("age")); // false(自身无age) console.log(dog1.hasOwnProperty("name")); // true(自身有name)

六、核心总结

  1. prototype:函数的「公共仓库」,只有构造函数的prototype有实际意义,存储实例共享的属性 / 方法,自带constructor指向原函数。
  2. __proto__:对象的「原型指针」,所有对象都有,指向创建该对象的构造函数的prototype,是原型链的核心链路。
  3. 联动关系实例.__proto__ === 构造函数.prototype,原型链通过__proto__串联多个prototype,直到null
  4. 查找规则:访问对象属性 / 方法时,先查自身 → 沿__proto__查原型 → 直到Object.prototype→ 仍无则返回undefined
http://www.cnnetsun.cn/news/93086.html

相关文章:

  • FreeMove终极指南:Windows文件迁移的革命性解决方案
  • FeHelper全能工具箱:前端开发效率提升终极指南
  • QQ空间历史说说完整备份指南:永久珍藏你的数字记忆
  • 十大MCP Server方案,让DevOps步入智能新时代
  • VUE3:深入浅出探究pinia、provide\inject在多层组件页面是怎么使用的
  • Molecular Operating Environment (MOE) 完整安装与配置指南
  • LobeChat能否用于生成直播话术?电商主播提词器
  • 终极C++网络编程:构建高性能HTTP服务的完整指南
  • 显卡驱动终极清理指南:一键彻底解决兼容性问题
  • Zotero GPT:用AI重新定义文献管理效率
  • LobeChat与LangChain结合应用:打造复杂AI工作流
  • 突破60帧束缚:原神性能优化工具深度解析
  • 云计算作业—-V L AN实验
  • 当连锁巡检“听懂人话”:VLM技术下的智能运营新场景
  • 责任链模式(Chain of Responsibility):实现事件或请求的逐级处理与传递
  • SMUDebugTool深度探索:解锁AMD Ryzen系统的隐藏性能
  • JavaScript 中的单例模式:利用闭包、IIFE 或 ES Modules 实现线程安全的单例
  • CORS 机制中的预检请求(Preflight Request):为什么 OPTIONS 请求总是先于复杂请求发送?
  • Google Drive文件下载终极指南:简单快速解决下载难题
  • 面对一个新领域,如何快速摸清门道?试试“一键生成”研究地图
  • 终极指南:5步实现全球付费内容免费阅读
  • GBase 8s数据库SYSTIMESTAMP表达式介绍(上)
  • 从“秒级”到“毫秒级”:金仓如何让InfluxDB的“时序神话”黯然失色?
  • zotero-style插件深度解析:从零打造高效文献管理生态
  • 5倍推理加速:Axolotl缓存策略如何终结重复计算瓶颈
  • LobeChat能否部署在华为云弹性云服务器?国产化替代实践
  • LobeChat备份与恢复策略:防止重要对话丢失
  • Access Token 生命周期管理:详细设计 Token 的获取、缓存、续期和过期处理机制
  • 客户群 ID 与业务 ID 映射:设计高性能数据库表结构,实现 ChatID 与内部业务标签的快速关联
  • 代码随想录算法训练营Day48 | 108.冗余连接、109.冗余连接II