JavaScript 中的原型和原型链

news2024/11/30 5:44:08

JavaScript 中的原型和原型链也是一个相对较难理解透彻的知识点,下面结合详细例子来进行说明:

一、原型的概念

在 JavaScript 中,每个函数都有一个 prototype 属性,这个属性指向一个对象,这个对象就是所谓的 “原型对象”。当通过构造函数创建一个新的实例对象时,该实例对象会自动拥有一个指向构造函数原型对象的内部属性(在大多数浏览器中可以通过 proto 来访问这个内部属性,虽然它并非标准属性,但方便理解)。
例如:

function Person(name, age) {
    this.name = name;
    this.age = age;
}

Person.prototype.sayHello = function () {
    console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);
};

let john = new Person('John', 30);
john.sayHello(); 
// 输出:Hello, my name is John and I'm 30 years old.

在这个例子中:
首先定义了一个 Person 函数作为构造函数,用来创建 Person 类型的实例。
然后给 Person 函数的 prototype 属性添加了一个 sayHello 方法。
当通过 new Person(‘John’, 30) 创建了 john 这个实例后,john 本身并没有 sayHello 这个方法的定义,但是它可以通过内部的 proto 属性(指向 Person.prototype)找到并调用 sayHello 方法。

二、原型链的形成

当在一个对象上访问某个属性或方法时,如果该对象本身没有这个属性或方法,JavaScript 就会自动沿着它的原型链去查找。原型链就是由对象的 proto 属性连接起来的一系列对象。
继续上面的例子,假设我们有这样一个情况:

function Employee(name, age, department) {
    Person.call(this, name, age);
    this.department = department;
}

Employee.prototype = Object.create(Person.prototype);
Employee.prototype.constructor = Employee;

Employee.prototype.sayDepartment = function () {
    console.log(`I work in the ${this.department} department.`);
};

let jane = new Employee('Jane', 25, 'Engineering');
jane.sayHello(); 
// 输出:Hello, my name is Jane and I'm 25 years old.
jane.sayDepartment(); 
// 输出:I work in the Engineering department.

在这个例子中:
首先定义了 Employee 函数作为构造函数来创建 Employee 类型的实例,并且在构造函数内部通过 Person.call(this, name, age) 调用了 Person 构造函数,以便让 Employee 实例继承 Person 实例的 name 和 age 属性。
然后通过 Employee.prototype = Object.create(Person.prototype) 让 Employee 的原型对象继承自 Person 的原型对象,这样 Employee 实例就可以沿着原型链找到 Person 原型对象上的方法(如 sayHello)。同时,重新设置了 Employee.prototype.constructor 为 Employee,以保证构造函数的正确性。
最后给 Employee 原型对象添加了 sayDepartment 方法。
当我们在 jane(Employee 实例)上调用 sayHello 方法时,jane 本身没有 sayHello 方法,它会通过自己的 proto 属性(此时指向 Employee.prototype)找不到,就继续沿着 Employee.prototype 的 proto(因为 Employee.prototype = Object.create(Person.prototype),所以 Employee.prototype.proto 指向 Person.prototype)找到 Person.prototype 上的 sayHello 方法并调用。

三、原型链的查找顺序

为了更清楚地展示原型链的查找顺序,我们再看一个例子:

function Animal() {}
Animal.prototype.eat = function () {
    console.log('Eating...');
};

function Dog() {}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.bark = function () {
    console.log('Woof!');
};

let myDog = new Dog();
myDog.eat(); 
// 输出:Eating...
myDog.bark(); 
// 输出:Woof!

// 查看原型链
console.log(myDog.__proto__ === Dog.prototype); 
// 输出:true
console.log(myDog.__proto__.__proto__ === Animal.prototype); 
// 输出:true
console.log(myDog.__proto__.__proto__.__proto__ === Object.prototype); 
// 输出:true
console.log(myDog.__proto__.__proto__.__proto__.__proto__ === null); 
// 输出:true

在这个例子中:
定义了 Animal 函数和其原型对象上的 eat 方法。
定义了 Dog 函数,并让其原型对象继承自 Animal 原型对象,同时在 Dog 原型对象上添加了 bark 方法。
当创建了 myDog 这个 Dog 实例后,在 myDog 上调用 eat 方法时,首先 myDog 本身没有 eat 方法,它会沿着原型链查找。先通过 myDog.proto(指向 Dog.prototype)找不到,再通过 myDog.proto.proto(指向 Animal.prototype)找到并调用 eat 方法。同样,调用 bark 方法时,在 myDog 本身找不到,通过 myDog.proto 就能找到并调用。
从 myDog 的原型链可以看出,查找顺序是先在实例本身查找,然后沿着 proto 指向的对象依次查找,一直到 Object.prototype,最后 Object.prototype.proto 为 null,表示原型链的尽头。

四、原型和原型链的难点

概念的抽象性:原型和原型链涉及到对象、函数、构造函数、原型对象等多个概念的相互关系,理解起来比较抽象。例如,要清楚地区分函数的 prototype 属性和实例对象的 proto 属性,以及它们之间是如何相互作用来实现属性和方法的继承的,这对于初学者来说并不容易。
复杂的继承关系:当涉及到多层继承时,如上面例子中的 Employee 继承自 Person,再加上可能还有更多层的继承关系,要准确把握每个实例沿着原型链查找属性和方法的路径以及如何正确设置继承关系(比如通过 Object.create 等方式),这需要对原型链的机制有深入的理解。
与其他语言的差异:对于有其他编程语言背景的开发者来说,JavaScript 的原型继承方式与基于类的继承(如 Java、C++ 等)有很大的不同。在基于类的继承中,继承关系通常是通过类的定义和关键字(如 extends)来明确规定的,而 JavaScript 是通过原型和原型链这种相对更灵活但也更抽象的方式来实现继承的,所以习惯了类继承的开发者可能会觉得难以适应。
原型和原型链在 JavaScript 中是非常重要的概念,虽然理解起来有一定难度,但掌握它们对于深入理解 JavaScript 的对象系统以及编写高效、灵活的代码至关重要。

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

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

相关文章

嵌入式linux系统中网络编程的具体实现

1.网络编程简介 要编写通过计算机网络通信的程序,首先要确定这些程序同通信的协议(protocol),在设计一个协议的细节之前,首先要分清程序是由哪个程序发起以及响应何时产生。 举例来说,一般认为WEB服务器程序是一个长时间运行的程序(守护进程deamon),它只在响应来自网…

vue3.0 根据富文本html页面生成压缩包(含视频在线地址、图片在线地址、前端截图、前端文档)

vue3.0生成压缩包(含在线地址、前端截图、前端文档) 需求描述效果开始下载插件包基本代码构造 点击下载按钮1.截图content元素,并转化为pdfcanvas putImageData、getImageDatagetImageData 获取指定矩形区域的像素信息putImageData 将这些数据…

由于导包而引发的错误

今天在调试时发现删除功能无论如何都无法实现,于是调试找到了mapper层的错误但不知道为什么报错。以下是报错信息。 Caused by: org.apache.ibatis.binding.BindingException: Parameter userIds not found. Available parameters are [arg0, collection, list]at o…

黑马2024AI+JavaWeb开发入门Day04-SpringBootWeb入门-HTTP协议-分层解耦-IOCDI飞书作业

视频地址:哔哩哔哩 讲义作业飞书地址:day04作业(IOC&DI) 作业很简单,主要是练习拆分为三层架构controller、service、dao,并基于IOC & DI进行解耦。 1、结构: 2、代码 网盘链接&…

【LeetCode: 145. 二叉树的后序遍历 + 栈】

🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…

哈希表算法题

目录 题目一——1. 两数之和 - 力扣(LeetCode) 1.1.暴力解法1 1.2.暴力解法2 1.2.哈希表解法 题目二——面试题 01.02. 判定是否互为字符重排 - 力扣(LeetCode) 题目三——217. 存在重复元素 - 力扣(LeetCode&…

Galaxy预测比特币期权活跃交易将持续至2027年,特朗普执政中期

随着比特币及其相关产品的交易量不断增加,加密货币市场的期权交易也迎来了前所未有的活跃。Galaxy Digital的交易团队指出,贝莱德IBIT ETF期权在美国股市的交易量已经创下了新纪录。首日交易量高达353,716份合约,几乎与2012年Facebook期权上线…

Java-GUI(登录界面示例)

简述: 步骤: (1)构造界面(将组件对象加入容器对象,注意:应设定对容器对象的布局策略) (2)为界面加入事件响应处理(如单击按钮) 实现: 两种方式实现,只有用户名为"admin"且密码为…

屏幕分辨率|尺寸|颜色深度指纹

一、前端通过window.screen接口获取屏幕分辨率 尺寸 颜色深度,横屏竖屏信息。 二、window.screen c接口实现: 1、third_party\blink\renderer\core\frame\screen.idl // https://drafts.csswg.org/cssom-view/#the-screen-interface[ExposedWindow ] …

【论文阅读】三平面相关与变体

文章目录 1. 【CVPR2023】Tri-Perspective View for Vision-Based 3D Semantic Occupancy Prediction动机可视化方法Pipeline 2. 【2023/08/31】PointOcc: Cylindrical Tri-Perspective View for Point-based 3D Semantic Occupancy Prediction动机(针对雷达点云、与…

Java - JSR223规范解读_在JVM上实现多语言支持

文章目录 1. 概述2. 核心目标3. 支持的脚本语言4. 主要接口5. 脚本引擎的使用执行JavaScript脚本执行groovy脚本1. Groovy简介2. Groovy脚本示例3. 如何在Java中集成 Groovy4. 集成注意事项 6. 与Java集成7. 常见应用场景8. 优缺点9. 总结 1. 概述 JSR223(Java Spe…

定时/延时任务-ScheduledThreadPoolExecutor的使用

文章目录 1. 概要2. 固定速率和固定延时2.1 固定速率2.2 固定延时 3. API 解释3.1 schedule3.2 固定延时 - scheduleWithFixedDelay3.2 固定速率 - scheduleWithFixedDelay 4. 小结 1. 概要 前三篇文章的地址: 定时/延时任务-自己实现一个简单的定时器定时/延时任…

什么是sfp,onu,​为什么PON(​俗称“光猫”​)模块使用SC光纤接口

在现代网络设备中,我们经常会看到SFP或SFP接口的身影,这些接口有时被简称为光口,但这个称呼并不严谨。有些厂商则称之为多功能口或多用途口,然而这对于不了解的人来说可能还是一头雾水。SFP,即Small Form-Factor Plugg…

005 MATLAB符号微积分

前言: 在MATLAB中,数值与符号的主要区别在于它们的处理方式和应用场景 数值计算适用于实际的数值计算问题,如矩阵运算、数据分析等。符号计算适用于符号推导、公式化简和符号解析,如理论物理和工程计算。 01 符号对象 1.基本符…

深入实践:从零开始掌握GPT的应用开发

1. 为什么选择GPT? GPT(Generative Pre-trained Transformer)是当下最具影响力的语言生成模型之一,适用于生成文本、分析语言情感、翻译、多任务对话等多种场景。相比传统算法和模型,GPT有以下显著优势: …

WRF-Chem模式安装、环境配置、原理、调试、运行方法;数据准备及相关参数设置方法

大气污染是工农业生产、生活、交通、城市化等方面人为活动的综合结果,同时气象因素是控制大气污染的关键自然因素。大气污染问题既是局部、当地的,也是区域的,甚至是全球的。本地的污染物排放除了对当地造成严重影响外,同时还会在…

开源项目:纯Python构建的中后台管理系统

来源:Python大数据分析 费弗里 大家好我是费老师,目前市面上有很多开源的「中后台管理系统」解决方案,复杂如「若依」那种前端基于Vue,后端基于Java的框架,虽然其提供了较为完善的一整套前后端分离权限管理系统解决方…

汽车免拆诊断案例 | 2017款捷豹F-PACE车发动机偶尔怠速不稳

故障现象  一辆2017款捷豹F-PACE车,搭载2.0 L GTDi发动机,累计行驶里程约为16万km。车主反映,车辆组合仪表上发动机故障灯点亮(图1),且发动机偶尔怠速不稳。 图1 发动机故障灯点亮 故障诊断 接车后试车…

SQL进阶技巧:非等值连接--单向近距离匹配

目录 0 场景描述 1 数据准备 2 问题分析 ​编辑 ​编辑 3 小结 数字化建设通关指南 0 场景描述 表 t_1 和表 t_2 通过 a 和 b 关联时,有相等的取相等的值匹配,不相等时每一 个 a 的值在 b 中找差值最小的来匹。 表 t_1:a 中无重复值…

泷羽sec-云技术

基础之云技术 声明! 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下,如涉及侵权马上删除文章,笔记只是方便各位师傅的学习和探讨,文章所提到的网站以及内容,只做学习交流,其他均与本人以及泷羽sec…