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

原型与原型链、原型属性学习笔记

一、核心概念

在JavaScript中,原型(Prototype)是实现继承的核心机制,所有对象(除null外)都有一个内置的原型对象,用于共享属性和方法,避免重复创建,提升代码复用性。原型链则是原型层层关联形成的链条,解决了“对象如何查找属性/方法”的问题。

关键前提:函数也是对象,函数除了自身的属性和方法,还自带一个特殊的原型属性;而普通对象(由函数创建)的原型,指向其构造函数的原型对象。

二、原型(Prototype)详解

2.1 什么是原型

原型是一个对象,每个函数(构造函数)都有一个prototype属性(原型属性),这个属性指向的就是该函数的“原型对象”。当用这个构造函数创建实例时,实例会自动关联到这个原型对象,实例可以访问原型对象上的所有属性和方法。

简单理解:原型对象是“模板”,实例是“模板复制出来的产物”,模板上的内容,所有产物都能共用。

2.2 原型的分类(易混点区分)

  • 函数的原型属性(prototype):仅函数拥有,指向“原型对象”,作用是为其创建的实例提供共享属性/方法。 示例:function Person() {}Person.prototype就是Person函数的原型属性,指向Person的原型对象。

  • 实例的原型(__proto__):所有对象(除null)都拥有,是一个内置属性(非标准属性,规范中用Object.getPrototypeOf()获取),指向其构造函数的原型对象。 示例:let p = new Person()p.__proto__ === Person.prototype(结果为true)。

  • 原型对象的构造器(constructor):每个原型对象都有一个constructor属性,指向其对应的构造函数。 示例:Person.prototype.constructor === Person(结果为true),实例可以通过p.constructor间接访问到构造函数。

2.3 原型的核心作用

  1. 属性/方法共享:将所有实例共有的属性(如物种、通用方法)放在原型对象上,所有实例无需单独拥有,节省内存。 示例:给Person原型添加sayHi方法,所有Person实例都能调用,且只创建一次。

  2. 实现继承:子构造函数的原型指向父构造函数的实例,让子实例能访问父构造函数的原型属性/方法(后续原型链会详细说明)。

三、原型链(Prototype Chain)详解

3.1 什么是原型链

当访问一个对象的属性/方法时,JavaScript会先在对象自身查找;如果找不到,就会去它的原型对象(__proto__指向的对象)中查找;如果原型对象中也找不到,就去原型对象的原型中查找,以此类推,直到找到属性/方法,或找到原型链的终点(null),这个层层查找的链条,就是原型链。

核心逻辑:实例.__proto__ → 构造函数.prototype → 构造函数.prototype.__proto__ → ... → null

3.2 原型链的终点

所有原型链的最终终点都是null,因为Object.prototype.__proto__ === null(Object是JavaScript中所有对象的顶层构造函数)。

示例:Person实例p的原型链 p → Person.prototype → Object.prototype → null

所以,p可以访问Object.prototype上的方法(如toString()hasOwnProperty()),这就是原型链的作用——实现跨构造函数的属性/方法继承。

3.3 原型链的常见场景(实例)

// 1. 定义构造函数 function Person(name) { this.name = name; // 实例自身属性 } // 2. 给Person原型添加共享方法 Person.prototype.sayHi = function() { console.log(`Hi, ${this.name}`); }; // 3. 创建实例 let p1 = new Person("张三"); let p2 = new Person("李四"); // 访问实例自身属性(name):直接找到 console.log(p1.name); // 张三 // 访问共享方法(sayHi):自身没有,去Person.prototype查找 p1.sayHi(); // Hi, 张三 // 访问Object原型上的方法(toString):Person.prototype没有,去Object.prototype查找 console.log(p1.toString()); // [object Object] // 查找不存在的属性:遍历原型链到null,返回undefined console.log(p1.age); // undefined

四、原型属性相关问题(易混、易错点)

4.1 易混点1:prototype vs __proto__

区别维度

prototype

__proto__

拥有者

仅函数(构造函数)拥有

所有对象(除null)都拥有

作用

为其创建的实例提供共享属性/方法

指向自身的原型对象,构成原型链

关系

实例.__proto__ === 构造函数.prototype

原型对象.__proto__ 指向更高层原型

4.2 易错点1:修改原型属性 vs 给实例添加属性

当给实例添加一个与原型对象同名的属性时,不会修改原型对象的属性,只会在实例自身添加该属性,优先访问实例自身的属性(原型链查找的优先级)。

// 延续上面的Person构造函数 Person.prototype.age = 18; // 原型上的age属性 let p1 = new Person("张三"); console.log(p1.age); // 18(自身没有,访问原型) // 给实例添加同名属性 p1.age = 20; console.log(p1.age); // 20(访问自身属性) console.log(Person.prototype.age); // 18(原型属性未被修改) // 删除实例自身的age属性,会重新访问原型的age delete p1.age; console.log(p1.age); // 18

4.3 易错点2:直接修改原型对象 vs 替换原型对象

  • 直接修改原型对象:在原有原型对象上添加/修改属性,不影响原型链,已创建的实例和后续创建的实例都能访问到。 示例:Person.prototype.gender = "男",p1(已创建)也能访问p1.gender。

  • 替换原型对象:用一个新对象替换原有的prototype,会断裂原型链,已创建的实例仍指向原来的原型对象,只有后续创建的实例指向新原型。

  • // 替换原型对象 Person.prototype = { gender: "男", sayHello: function() { console.log("Hello"); } }; let p3 = new Person("王五"); let p1 = new Person("张三"); // 之前创建的实例 console.log(p3.gender); // 男(新原型的属性) console.log(p1.gender); // undefined(原原型没有gender,新原型与p1无关) console.log(p1.sayHi()); // 正常执行(原原型的方法) console.log(p3.sayHi()); // 报错(新原型没有sayHi方法)

4.4 易错点3:null的原型

null没有原型(null.__proto__会报错),它是原型链的终点,也是唯一没有原型的对象。

注意:Object.create(null)创建的对象,其原型也是null,无法访问Object.prototype上的任何方法(如toString)。

http://www.jsqmd.com/news/603641/

相关文章:

  • STM32定时器级联功能实战:如何构建64位定时器
  • python boto3
  • Win11Debloat:轻松打造极速、纯净Windows 11的终极指南
  • 4大维度掌握AI音乐源分离:Demucs的技术突破与实践指南
  • 告别理论推导!用《有源滤波器的快速实用设计》手把手搞定1kHz带通滤波器(附Multisim仿真)
  • Kubernetes网络入门003篇【20260407】
  • 2026执医考试备考优质机构最新推荐_零基础、在职高效通过首选 - 医考机构品牌测评专家
  • npm国内镜像加速之使用 nrm 工具(灵活切换,适合多环境)
  • Linux新手必看:fdisk磁盘分区从入门到精通(含常见问题解决)
  • 19米LS型螺旋输送机设计【说明书+CAD图纸+开题报告+外文翻译】
  • 为什么92%的Python MCP项目在CI/CD阶段突然报错?揭秘被官方文档隐藏的4个环境依赖雷区
  • BallonsTranslator:基于深度学习的智能漫画翻译与排版解决方案
  • 2026执业药师考试机构全景测评:零基础、在职、二战考生高效备考优选 - 医考机构品牌测评专家
  • 云原生环境中的AI推理服务部署
  • 蓝桥杯单片机第12届省赛2满分(西风)
  • AI辅助开发新思路:让快马AI智能分析你的谷歌浏览器下载习惯
  • 探索 Z 源逆变器的多种 SPWM 仿真模型
  • ESP32智能股票监控系统:实时价格触发电话提醒(附完整代码)
  • 2026执业药师网课测评:零基础、在职、二战考生如何选择备考方案 - 医考机构品牌测评专家
  • 四旋翼姿态解算实战:MahonyAHRS算法中的初始姿态角优化策略
  • 3步实现OpenCore EFI智能生成:黑苹果配置效率提升96%的实战指南
  • openpilot技术实战指南:从问题诊断到方案落地的完整路径
  • 【仅限本期开放】Python MCP生产就绪模板(含OpenTelemetry埋点+K8s readiness探针+灰度路由开关),附赠MCP 2.x兼容性迁移checklist
  • PotPlayer百度翻译插件:5分钟免费实现视频实时双语字幕,外语学习效率提升200%
  • 分镜设计指南
  • 给阿里云服务器加端口远程开关
  • 为什么你的Mojo-Python桥接总失败?资深编译器工程师逐行解析ctypes/ffi/callables三大接入路径
  • Kubernetes与Serverless的融合实践
  • 时间同步和频率同步的区别
  • 实战销售对账:基于快马平台快速构建业务级vlookup跨表匹配系统