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

原型与原型链到底是什么?

一、原型(Prototype):对象的"基因库"

定义:原型是JavaScript对象内置的隐藏属性([[Prototype]]),用于实现属性和方法的继承与共享。
核心作用:让多个对象能够共享同一套属性/方法,避免重复定义,优化内存占用。

1. 原型的两种访问方式

实例对象属性:proto(ES6标准化,浏览器实现,不推荐直接操作)
构造函数属性:prototype(仅函数拥有,指向实例的原型对象)
推荐API:Object.getPrototypeOf(obj) 和 Object.setPrototypeOf(obj, proto)

const obj = {};
console.log(obj.__proto__ === Object.prototype); // true(实例.__proto__指向原型)
console.log(Object.prototype.constructor === Object); // true(原型.constructor指向构造函数)

2. 原型对象的特性

包含所有实例共享的属性/方法(如Array.prototype.push)
自带constructor属性,指向关联的构造函数
本身也是对象,因此也有自己的原型(形成原型链)

二、原型链(Prototype Chain):属性查找的"族谱"

定义:多个对象通过__proto__属性串联形成的链式结构,是JavaScript实现继承的核心机制。

1. 工作原理

当访问对象的属性/方法时:

  1. 先检查对象自身是否存在该属性
  2. 若不存在,沿__proto__向上查找原型对象
  3. 依次类推,直至找到属性或到达原型链终点(null)
const arr = [1, 2, 3];
arr.toString(); // 实际调用 Object.prototype.toString// 原型链路径:arr → Array.prototype → Object.prototype → null
console.log(arr.__proto__ === Array.prototype); // true
console.log(Array.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null(终点)

2. 原型链的典型结构

实例对象  → 构造函数.prototype  → 父构造函数.prototype  → ...Object.prototype → nullarr     →  Array.prototype    →   Object.prototype     → ...nullfunc    → Function.prototype  →   Object.prototype     → ...null

三、构造函数、原型与实例的"三角关系"

// 1. 定义构造函数
function Person(name) {this.name = name; // 实例属性(每个实例独立拥有)
}// 2. 在原型上定义共享方法
Person.prototype.sayHello = function() {console.log(`Hello, ${this.name}`); // this指向调用方法的实例
};// 3. 创建实例
const person1 = new Person("Alice");
const person2 = new Person("Bob");// 4. 关系验证
console.log(person1.__proto__ === Person.prototype); // true(实例→原型)
console.log(Person.prototype.constructor === Person); // true(原型→构造函数)
console.log(person1.constructor === Person); // true(实例继承constructor)// 5. 共享方法调用
person1.sayHello(); // "Hello, Alice"(共享原型方法)
person2.sayHello(); // "Hello, Bob"

四、原型链的核心应用场景

1. 实现继承

组合继承(最经典的继承模式):

function Student(name, major) {Person.call(this, name); // 继承实例属性this.major = major;
}// 继承原型方法(关键步骤)
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student; // 修复constructor指向// 添加子类独有方法
Student.prototype.study = function() {console.log(`${this.name} is studying ${this.major}`);
};

ES6 class语法糖(本质仍是原型继承):

class Student extends Person {constructor(name, major) {super(name); // 等价于 Person.call(this, name)this.major = major;}study() { // 定义在Student.prototype上console.log(`${this.name} is studying ${this.major}`);}
}

2. 扩展内置对象功能

// 为数组添加去重方法(谨慎使用,可能引发冲突)
Array.prototype.unique = function() {return [...new Set(this)];
};
[1, 2, 2, 3].unique(); // [1, 2, 3]

3. 创建纯净字典对象

// 无原型链的对象,避免原型属性干扰
const safeMap = Object.create(null);
safeMap.toString = "自定义toString"; 
console.log(safeMap.toString); // "自定义toString"(不会访问Object.prototype.toString)

五、常见问题与避坑指南

1. 原型污染

风险:直接修改内置对象原型会影响所有实例

// 危险行为!
Object.prototype.foo = "bar";
console.log({}.foo); // "bar"(所有对象都会继承foo属性)

解决方案:使用Object.create(null)创建隔离对象

2. 属性遮蔽(Shadowing)

实例属性会覆盖原型链上的同名属性:

Person.prototype.age = 18;
const person = new Person("Alice");
person.age = 20; // 实例属性遮蔽原型属性
console.log(person.age); // 20(优先取实例属性)
delete person.age; // 删除实例属性后恢复原型属性
console.log(person.age); // 18

3. this指向问题

原型方法中的this指向调用该方法的实例:

Person.prototype.getName = function() {return this.name;
};
const person = new Person("Alice");
const getName = person.getName;
console.log(getName()); // undefined(this指向window/global)

解决方案:使用箭头函数绑定或Function.prototype.bind()

六、调试与可视化工具

1. 控制台查看原型链:
console.dir(obj) 可展开对象完整原型链结构

2. 判断原型关系:

// 方法1:instanceof(检查是否在原型链上)
console.log(person instanceof Person); // true
console.log(person instanceof Object); // true// 方法2:Object.prototype.isPrototypeOf()
console.log(Person.prototype.isPrototypeOf(person)); // true

3. 获取原型链路径:

function getPrototypeChain(obj) {const chain = [];while (obj) {chain.push(obj.constructor.name || obj);obj = Object.getPrototypeOf(obj);}return chain;
}
getPrototypeChain([1,2,3]); // ["Array", "Object", "null"]

七、总结:原型链的本质与价值

本质:JavaScript通过原型链实现了对象间的属性继承,是一种不同于传统类继承的"原型继承"模式

价值:

  1. 实现代码复用与扩展
  2. 动态特性(运行时可修改原型)
  3. 轻量级对象模型

最佳实践:

  1. 优先使用class语法(更清晰的继承结构)
  2. 避免修改内置对象原型
  3. 复杂继承场景考虑使用组合优于继承的设计模式

理解原型与原型链是掌握JavaScript面向对象编程的关键,也是深入理解框架源码(如Vue的响应式原理)的基础。

http://www.xdnf.cn/news/15076.html

相关文章:

  • Linux驱动学习day20(pinctrl子系统驱动大全)
  • 自动驾驶决策与规划
  • 【Kafka】登录日志处理的三次阶梯式优化实践:从同步写入到Kafka多分区批处理
  • 【PDF提取内容改名】批量提取pdf多个指定区域内容到excel表格的操作步骤和方法
  • 制作MikTex本地包可用于离线安装包
  • doris2.1.8连接报错ERROR 1203 (42000): Reach limit of connections解决办法
  • 冷冻电镜重构的GPU加速破局:从Relion到CryoSPARC的并行重构算法
  • 前端UI逻辑复杂可以用什么设计模式
  • 由 DB_FILES 参数导致的 dg 服务器无法同步问题
  • CVE-2025-32463复现
  • 在overleaf中使用bibtex格式引用文献
  • 自动化测试报告优化:jenkins+jmeter定制化HTML报告生成
  • QT 第八讲 --- 控件篇 Widget(三)界面系列
  • 米思齐2.0 3.0 mixly arduino 编程软件下载安装及详情使用指南 导入库文件方法 支持8266 esp32
  • 宇树 G1 部署(一)——综述
  • 如何将华为手机中的照片传输到电脑
  • Unreal引擎——动画系统详解-其二
  • 2025年INS SCI2区,灵活交叉变异灰狼算法GWO_C/M+集群任务调度,深度解析+性能实测
  • ${project.basedir}延申出来的Maven内置的一些常用属性
  • 编码器(Encoder)和解码器(Decoder)
  • 暑假复习篇之图像识别①
  • 社区云管家 - 智慧生活新方式 ——仙盟创梦IDE
  • 常见前端开发问题的解决办法
  • 用 Spring Boot + Redis 实现哔哩哔哩弹幕系统(上篇博客改进版)
  • 蓝桥杯 第十六届(2025)真题思路复盘解析
  • 软件设计与重构
  • Android UI 组件系列(四):EditText 使用详解与输入限制
  • 数据结构:数组:合并数组(Merging Arrays)
  • 大学人才引进初试试题(开卷)
  • IDEA Maven报错 无法解析 com.taobao:parent:pom:1.0.1【100%解决 此类型问题】