深入浅出JavaScript 原型链:对象继承的“隐形链条”

深入浅出JavaScript 原型链:对象继承的“隐形链条”

在 JavaScript 的世界里,原型链(Prototype Chain)是一个核心概念。它如同一条隐形的链条,连接着所有对象,使得代码能够高效地共享属性和方法。理解原型链,不仅能帮助你写出更优雅的代码,还能让你在调试时快速定位问题。


一、从“继承”说起:为什么需要原型链?

想象一个场景:你正在开发一个游戏,游戏中有无数个角色(比如战士、法师、弓箭手)。这些角色共享一些基础能力(如移动、攻击),但又有各自独特的技能。如果每个角色都单独定义这些基础能力,代码会变得冗余且难以维护。

原型链的作用就是解决这个问题。它通过对象之间的关联关系,让所有对象共享一套基础能力,从而节省内存并提高代码的可维护性。


二、原型链的核心:对象与原型

1. 每个对象都有一个原型

在 JavaScript 中,每个对象(除了 null)都有一个隐式原型__proto__),它指向另一个对象——这个对象就是它的原型。原型本身也是一个对象,因此它也可以有自己的原型,形成一条链式结构。

const obj = {};
console.log(obj.__proto__); // 指向 Object.prototype

2. 构造函数的原型(prototype)

每个函数(包括构造函数)都有一个 prototype 属性,它指向一个对象。当用 new 调用构造函数时,新对象的 __proto__ 会指向这个 prototype

function Person(name) {this.name = name;
}
Person.prototype.sayHello = function() {console.log(`Hello, my name is ${this.name}`);
};const alice = new Person('Alice');
console.log(alice.__proto__ === Person.prototype); // true

3. 原型链的形成

当访问一个对象的属性或方法时,JavaScript 引擎会沿着对象的原型链向上查找,直到找到目标或到达原型链的尽头(null)。

// 原型链的结构
alice -> Person.prototype -> Object.prototype -> null

三、原型链的工作原理:属性查找的“接力赛”

1. 属性查找的流程

当你访问一个对象的属性时,JavaScript 会执行以下步骤:

  1. 检查对象自身:如果属性存在,直接返回。
  2. 沿原型链查找:如果对象自身没有该属性,则沿着 __proto__ 向上查找原型对象。
  3. 找到或返回 undefined:直到找到目标属性或到达原型链的尽头(null)。
function Animal(name) {this.name = name;
}
Animal.prototype.speak = function() {console.log(`${this.name} makes a sound.`);
};const dog = new Animal('Buddy');
dog.speak(); // 输出 "Buddy makes a sound."

在这个例子中:

  • dog 对象自身没有 speak 方法。
  • JavaScript 引擎沿着 dog.__proto__(即 Animal.prototype)找到 speak 方法。

2. 动态性:修改原型会影响所有实例

原型链的动态性是其强大之处,也是潜在的陷阱。修改原型对象的属性或方法,所有实例都会受到影响。

// 修改原型上的方法
Animal.prototype.speak = function() {console.log(`${this.name} barks!`);
};dog.speak(); // 输出 "Buddy barks!"

即使 dog 是在修改前创建的,它也会继承新的 speak 方法。


四、继承的实现:从构造函数到 ES6 的 class

1. 构造函数继承

通过将子类的原型设置为父类的实例,可以实现继承。

function Parent(name) {this.name = name;
}
Parent.prototype.sayName = function() {console.log(this.name);
};function Child(name, age) {Parent.call(this, name); // 继承属性this.age = age;
}
Child.prototype = Object.create(Parent.prototype); // 继承方法
Child.prototype.constructor = Child;const child = new Child('Lily', 12);
child.sayName(); // 输出 "Lily"

2. ES6 的 class 语法糖

ES6 的 class 本质上是原型继承的语法糖,简化了继承的实现。

class Parent {constructor(name) {this.name = name;}sayName() {console.log(this.name);}
}class Child extends Parent {constructor(name, age) {super(name); // 调用父类构造函数this.age = age;}sayAge() {console.log(this.age);}
}const child = new Child('DT', 1);
child.sayName(); // 输出 "DT"
child.sayAge();  // 输出 1

五、原型链的实际应用与注意事项

1. 共享方法,节省内存

通过原型链,多个实例可以共享同一个方法,而不是每个实例都保存一份副本。

function Circle(radius) {this.radius = radius;
}
Circle.prototype.getArea = function() {return Math.PI * this.radius ** 2;
};const circle1 = new Circle(5);
const circle2 = new Circle(10);
console.log(circle1.getArea === circle2.getArea); // true

2. 原型链的终点:Object.prototype

所有对象的原型链最终都会指向 Object.prototype,这是 JavaScript 对象模型的起点。

console.log(Object.prototype.__proto__); // null

3. 避免原型污染

不要在 Object.prototype 上随意添加属性或方法,这可能导致所有对象意外继承这些属性,引发难以排查的错误。


六、总结:原型链的本质

原型链是 JavaScript 实现对象继承的核心机制。它通过对象之间的关联,让代码能够高效地共享属性和方法。理解原型链,不仅能帮助你写出更高效的代码,还能让你在面对复杂的对象关系时游刃有余。

关键点回顾:

  1. 每个对象都有原型,原型本身也是一个对象。
  2. 属性查找沿原型链向上进行。
  3. 构造函数的 prototype 是共享属性和方法的载体。
  4. ES6 的 class 是原型继承的语法糖。
  5. 动态性共享性是原型链的双刃剑,需谨慎使用。

七、延伸思考:原型链与现代 JavaScript

随着 ES6 的普及,classextends 逐渐取代了传统的原型链写法,但它们的本质仍是原型继承。理解原型链,能让你更深入地掌握 JavaScript 的底层机制,并在面对性能优化、框架设计等场景时做出更合理的决策。

原型链如同 JavaScript 的“基因链”,它让语言具备了灵活的对象模型。掌握它,你将真正理解 JavaScript 的魅力所在。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/85076.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/85076.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LINUX中MYSQL的使用

LINUX中MYSQL的使用 MYSQL的数据类型 bool: 布尔类型 0 或者 1 CHAR: 单字符的字符 CHAR(n):多字节字符 VARCHAR(n):可变长度的字符型 TINYINT : 单字节整型 SMALLINT&#x…

打卡第48天:随机函数与广播机制

知识点回顾: 随机张量的生成:torch.randn函数卷积和池化的计算公式(可以不掌握,会自动计算的)pytorch的广播机制:加法和乘法的广播机制 ps:numpy运算也有类似的广播机制,基本一致 …

学习昇腾开发的第四天--基本指令

1、查看npu当前状态信息 npu-smi info 2、查看NPU的ID npu-smi info -l3、调用python python3 4、修改用户名 su - HwHiAiUser 5、查看cann版本 cat /usr/local/Ascend/ascend-toolkit/latest/compiler/version.info 6、删除文件夹 sudo rm -rf HelloWorld7、在本地环…

vue3 - 自定义hook

自定义hook 简单点来说就是将人物或者订单的所有数据和方法放在一个ts文件里面 这样便于维护 假如一个人只需要管 人物的模块 那他只需要操作usePerson.ts文件就可以了 //useDog.ts import { ref,reactive} from vue; import axios from axios;export default function(){…

【python】bash: !‘: event not found

报错 # 2. 测试smplx是否工作(可能不需要chumpy) python -c "import smplx; print(✅ smplx works!)"bash: !: event not found 分析 这是bash的历史扩展问题,感叹号被解释为历史命令。用这些方法解决: &#x1f680…

【Python打卡Day47】注意力热力图可视化@浙大疏锦行

可视化空间注意力热力图的意义: 提升模型可解释性 热力图能直观展示模型决策的依据区域,破除深度学习"黑箱"困境。例如在图像识别中,可以看到模型识别"猫"是因为关注了猫耳和胡须区域,识别"禁止通行&qu…

树状数组 2

L - 树状数组 2 洛谷 - P3368 Description 如题,已知一个数列,你需要进行下面两种操作: 将某区间每一个数加上 x; 求出某一个数的值。 Input 第一行包含两个整数 N、M,分别表示该数列数字的个数和操作的总个数。…

YOLOv2 技术详解:目标检测的又一次飞跃

🧠 YOLOv2 技术详解:目标检测的又一次飞跃 一、前言 在 YOLOv1 提出后,虽然实现了“实时性 单阶段”的突破,但其在精度和小物体检测方面仍有明显不足。为了弥补这些缺陷,Joseph Redmon 等人在 2017 年提出了 YOLOv2…

JAFAR Jack up Any Feature at Any Resolution

GitHub PaPer JAFAR: Jack up Any Feature at Any Resolution 摘要 基础视觉编码器已成为各种密集视觉任务的核心组件。然而,它们的低分辨率空间特征输出需要特征上采样以产生下游任务所需的高分辨率模式。在这项工作中,我们介绍了 JAFAR——一种轻量级…

SamWaf 开源轻量级网站防火墙源码(源码下载)

SamWaf网站防火墙是一款适用于小公司、工作室和个人网站的开源轻量级网站防火墙,完全私有化部署,数据加密且仅保存本地,一键启动,支持Linux,Windows 64位,Arm64。 主要功能: 代码完全开源 支持私有化部署…

79Qt窗口_QDockWidget的基本使用

目录 4.1 浮动窗⼝的创建 4.2 设置停靠的位置 浮动窗⼝ 在 Qt 中,浮动窗⼝也称之为铆接部件。浮动窗⼝是通过 QDockWidget类 来实现浮动的功能。浮动窗 ⼝⼀般是位于核⼼部件的周围,可以有多个。 4.1 浮动窗⼝的创建 浮动窗⼝的创建是通过 QDockWidget…

UE/Unity/Webgl云渲染推流网址,如何与外部网页嵌套和交互?

需求分析:用threejs开发的数字孪生模型, 但是通过webgl技术网页中使用,因为模型数据量大,加载比较慢,且需要和其他的业务系统进行网页嵌套和交互,使用云渲染技术形成的推流网址,如何与外部网页嵌…

在Termux中搭建完整Python环境(Ubuntu+Miniconda)

蹲坑也能写python? 📱 环境准备🛠 详细搭建步骤步骤1:安装Linux容器工具步骤2:查看可用Linux发行版步骤3:安装Ubuntu系统步骤4:登录Ubuntu环境步骤5:下载Miniconda安装包步骤6:安装Miniconda⚡ 环境验证💡 使用技巧⚠️ 注意事项前言:想在吃饭、通勤甚至休息间隙…

EventSourcing.NetCore:基于事件溯源模式的 .NET Core 库

在现代软件架构中,事件溯源(Event Sourcing)已经成为一种非常流行的模式,尤其适用于需要高可用性和数据一致性的场景。EventSourcing.NetCore 是一个基于事件溯源模式的 .NET Core 库,旨在帮助开发者更加高效地实现这一…

Linux下的第一个程序——进度条(命令行版本)

文章目录 编写Linux下的第一个小程序——进度条进度条的样式前置知识回车和换行缓冲区对回车、换行、缓冲区、输出的测试代码简单的测试样例倒计时程序 进度条程序理论版本基本框架代码实现 真实版本基础框架 代码实现 编写Linux下的第一个小程序——进度条 在前面的基础开发工…

【项目】仿muduo库one thread one loop式并发服务器前置知识准备

📚 博主的专栏 🐧 Linux | 🖥️ C | 📊 数据结构 | 💡C 算法 | 🅒 C 语言 | 🌐 计算机网络 |🗃️ mysql 本文介绍了一种基于muduo库实现的主从Reactor模型高并发服务器框架…

steam报网络错误,但电脑是网络连接的

steam报网络错误,但电脑是网络连接的 如: 解决办法: 关闭电脑防火墙和所有杀毒软件,然后重新打开steam开代理,可能国内有时候访问不了 首选1进行尝试 steam安装路径一定要在纯英文路径下 已ok

Vue 组合式 API 与 选项式 API 全面对比教程

一、前言:Vue 的两种 API 风格 Vue 提供了两种编写组件逻辑的方式:组合式 API (Composition API) 和 选项式 API (Options API)。理解这两种方式的区别和适用场景,对于 Vue 开发者至关重要。 为什么会有两种 API? 选项式 API&a…

HarmonyOS 应用模块化设计 - 面试核心知识点

HarmonyOS 应用模块化设计 - 面试核心知识点 在 HarmonyOS 开发面试中,模块化设计是必考知识点。本文从面试官角度深度解析 HarmonyOS 应用模块化设计,涵盖 HAP、HAR、HSP 等核心概念,助你轻松应对技术面试! 🎯 面试高…

Maven高级学习笔记

分模块设计 为什么分模块设计?将项目按照功能拆分成若干个子模块,方便项目的管理维护、扩展,也方便模块间的相互调用,资源共享。 注意事项:分模块开发需要先针对模块功能进行设计,再进行编码。不会先将工程开发完毕&…