《从分遗产说起:JS 原型与继承详解》

 

“天天开心就好”


先来讲讲概念: 

原型(Prototype)

什么是原型?

原型是 JavaScript 中实现对象间共享属性和方法的机制。每个 JavaScript 对象(除了 null)都有一个内部链接指向另一个对象,这个对象就是它的"原型"(prototype)。

继承(Inheritance)

什么是继承?

继承是面向对象编程中的一个核心概念,它允许一个对象(子对象)获取另一个对象(父对象)的属性和方法。在 JavaScript 中,继承主要通过原型链来实现。

好的现在我们明确了什么是原型,什么是继承。简单来说,原型就是一个机制,每个对象内部都一个内部链接指向他的原型。继承我的理解就是一种行为,就是像继承财产那样继承父对象的属性和方法,可谓是形容十分贴切。

原型基础

原型对象 (prototype)

  • 每个函数都有一个 prototype 属性(箭头函数除外)
  • 这个属性指向一个对象,称为"原型对象"
  • 原型对象包含可以被特定类型的所有实例共享的属性和方法
function Person() {}
Person.prototype.name = 'Default';
Person.prototype.sayHello = function() {console.log(`Hello, I'm ${this.name}`);
};

__proto__ 属性

  • 每个对象都有一个 __proto__ 属性(现已标准化为 Object.getPrototypeOf()
  • 指向创建该对象的构造函数的原型对象
const person = new Person();
console.log(person.__proto__ === Person.prototype); // true

这个很好理解了,我在这里想用c语言里面的指针来形容了。prototype就像是地址对应的数据,而_proto_就像是指向他的指针。不太恰当哈

 我们经常这样说:对象的继承是通过原型链实现的。

那么什么是原型链:

什么是原型链?

原型链(Prototype Chain)是 JavaScript 中实现继承的核心机制。当访问一个对象的属性或方法时,JavaScript 引擎会沿着对象的原型链向上查找,直到找到该属性或到达原型链的末端(null)。

原型链的构成

  1. ​每个对象都有一个 __proto__ 属性​​(现已标准化为 Object.getPrototypeOf()
  2. ​每个函数都有一个 prototype 属性​
  3. ​原型链的终点是 null

原型链的工作原理

当访问一个对象的属性时:

  1. 首先在对象自身查找该属性
  2. 如果没有找到,则查找对象的 __proto__(即其构造函数的 prototype
  3. 如果还没有找到,继续查找 __proto__.__proto__,依此类推
  4. 直到找到该属性或到达 null(此时返回 undefined
function Animal(name) {this.name = name;
}
Animal.prototype.eat = function() {console.log(`${this.name} is eating`);
};function Dog(name, breed) {Animal.call(this, name);this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {console.log('Woof!');
};const myDog = new Dog('Buddy', 'Golden Retriever');// 原型链:
// myDog -> Dog.prototype -> Animal.prototype -> Object.prototype -> null

我们讲原型和继承就是在意js中继承这种行为是怎么实现的,就像现实中大家只在乎怎么分遗产一样!

继承实现方式

 1.原型链继承

function Parent() {this.parentProperty = true;
}
Parent.prototype.getParentValue = function() {return this.parentProperty;
};function Child() {this.childProperty = false;
}
// 继承 Parent
Child.prototype = new Parent();const instance = new Child();
console.log(instance.getParentValue()); // true

​问题​​:

  • 所有子类实例共享同一个父类实例
  • 无法向父类构造函数传参

2. 构造函数继承

function Parent(name) {this.name = name;
}function Child(name) {Parent.call(this, name); // 在子类构造函数中调用父类构造函数
}const child = new Child('Alice');
console.log(child.name); // 'Alice'

​优点​​:

  • 可以向父类传参
  • 每个子类实例都有独立的父类属性副本

​缺点​​:

  • 无法继承父类原型上的方法

3.组合继承(最常用)

function Parent(name) {this.name = name;
}
Parent.prototype.sayName = function() {console.log(this.name);
};function Child(name, age) {Parent.call(this, name); // 第二次调用 Parentthis.age = age;
}
Child.prototype = new Parent(); // 第一次调用 Parent
Child.prototype.constructor = Child; // 修复构造函数指向const child = new Child('Alice', 25);
child.sayName(); // 'Alice'

​优点​​:

  • 结合了原型链和构造函数的优点
  • 既能继承原型方法,又能保证实例属性独立

​缺点​​:

  • 父类构造函数被调用了两次

4. 原型式继承

function object(o) {function F() {}F.prototype = o;return new F();
}const parent = { name: 'Parent' };
const child = object(parent);
console.log(child.name); // 'Parent'

ES5 标准化为 Object.create()

const child = Object.create(parent);

5. 寄生式继承

function createAnother(original) {const clone = Object.create(original);clone.sayHi = function() {console.log('Hi');};return clone;
}

6. 寄生组合式继承(最佳实践)

function inheritPrototype(child, parent) {const prototype = Object.create(parent.prototype);prototype.constructor = child;child.prototype = prototype;
}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;
}inheritPrototype(Child, Parent);const child = new Child('Alice', 25);
child.sayName(); // 'Alice'

​优点​​:

  • 只调用一次父类构造函数
  • 原型链保持正确
  • 最理想的继承方式

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;}
}const child = new Child('Alice', 25);
child.sayName(); // 'Alice'

​注意​​:

  • class 本质上是语法糖,底层仍然是基于原型的继承
  • extends 实现了寄生组合式继承
  • 必须在使用 this 前调用 super()

继承是js中很核心的机制了,有很多中方式来实现继承,继承的好处就是我们可以直接继承父对象的方法和属性而不用自己再次定义了。用好继承可以大大提升我们的代码水平,帮助我们实现更多复杂需求。 


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

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

相关文章

立马耀:通过阿里云 Serverless Spark 和 Milvus 构建高效向量检索系统,驱动个性化推荐业务

作者:厦门立马耀网络科技有限公司大数据开发工程师 陈宏毅 背景介绍 行业 蝉选是蝉妈妈出品的达人选品服务平台。蝉选秉持“陪伴达人赚到钱”的品牌使命,致力于洞悉达人变现需求和痛点,提供达人选高佣、稳变现、速响应的选品服务。 业务特…

Android显示学习笔记本

根据博客 Android-View 绘制原理(01)-JAVA层分析_android view draw原理分析-CSDN博客 提出了我的疑问 Canvas RenderNode updateDisplayListDirty 这些东西的关系 您的理解在基本方向上是对的,但让我详细解释一下 Android 中 updateDisplayListDirty、指令集合、…

JavaWeb学习打卡-Day4-会话技术、JWT、Filter、Interceptor

会话技术 会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应。会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求…

让数据优雅落地:用 serde::Deserialize 玩转结构体实体

前言 想象一下,服务器突然飞来一堆 JSON 数据,就像一群无头苍蝇冲进办公室,嗡嗡作响,横冲直撞。此刻,你的任务,就是把这群“迷路数据”安置进正确的格子里,分门别类,秩序井然,不混不乱,不漏一只。 好在 Rust 早就为我们备好瑞士军刀:serde::Deserialize。它不仅刀…

Virtio 技术解析 | 框架、设备实现与实践指南

本文为 “Virtio” 相关文章合辑。 略作重排,如有内容异常,请看原文。 Virtio 简介(一)—— 框架分析 posted 2021-04-21 10:14 Edver 1. 概述 在传统设备模拟中,虚拟机内部设备驱动完全不知自身处于虚拟化环境&a…

云计算赋能质检LIMS的价值 质检LIMS系统在云计算企业的创新应用

在云计算技术高速发展的背景下,实验室信息化管理正经历深刻变革。质检LIMS(实验室信息管理系统)作为实验室数字化转型的核心工具,通过与云计算深度融合,为企业提供了高弹性、高安全性的解决方案。本文将探讨质检LIMS在…

【win11 安装WSL2 详解一遍过!!】

共有五个步骤,按部就班的做,保准成功! 1. 打开开发者模式 设置->系统->开发者模式 2. 打开linux的win子系统 找到控制面板-程序和功能-启用或关闭Windows功能,选中“适用于Linux的Windows子系统”,“虚拟机…

Godot开发2D冒险游戏——第三节:游戏地图绘制

一、初步构建游戏地图 在游戏场景当中添加一个新的子节点:TileMapLayer 这一层称为瓦片地图层 根据提示,下一步显然是添加资源 为TileMapLayer节点添加一个TileSet 将地板添加进来,然后选择自动分割图集 自定义时要确保大小合适 让Godot自…

Django创建的应用目录详细解释以及如何操作数据库自动创建表

创建好Django项目后 如果要创建 python manage.py startapp 模块名模块 使用 我创建一个system模块后是 注意:urls是我自己建的文件 1.migrations目录 存放数据库的迁移文件,当models.py中模型定义发生变化时,通过迁移操作能同步数据库结构变化 __init__ 使该目录…

将输入帧上下文打包到下一个帧的预测模型中用于视频生成

Paper Title: Packing Input Frame Context in Next-Frame Prediction Models for Video Generation 论文发布于2025年4月17日 Abstract部分 在这篇论文中,FramePack是一种新提出的网络结构,旨在解决视频生成中的两个主要问题:遗忘和漂移。 具体来说,遗忘指的是在生成视…

STM32 串口USART

目录 常见的通信方式 串行通信和并行通信 全双工,半双工和单工通信 同步通信和异步通信 通信速率 常见的通信协议 串口基础知识 电平特性 串口传输协议 STM32F103的USART资源 端口引脚 数据寄存器单元 发送接收控制单元 实现串口发送 printf…

Taro on Harmony :助力业务高效开发纯血鸿蒙应用

背景 纯血鸿蒙逐渐成为全球第三大操作系统,业界也掀起了适配鸿蒙原生的浪潮,用户迁移趋势明显,京东作为国民应用,为鸿蒙用户提供完整的购物体验至关重要。   去年 9 月,京东 AP…

gem5-gpu教程05 内存建模

memory-modeling|Details on how memory is modeled in gem5-gpu gem5-gpu’s Memory Simulation gem5-gpu在很大程度上避开了GPGPU-Sim的单独功能模拟,而是使用了gem5的执行中执行模型。因此,当执行存储/加载时,内存会被更新/读取。没有单独的功能路径。(顺便说一句,这…

【python】lambda用法(结合例子理解)

目录 lambda 是什么? 为什么叫 lambda? 语法 举例 1. 最简单的 lambda:单个数字处理 2. 用 lambda 排序一组字符串(按照长度排序) 3. 在列表里找出绝对值最小的数字 4. 给 map() 用 lambda 5. 组合使用:筛选出偶数 lambda 和 def 的对比 lambda 适合用在什么地…

【ROS2】机器人操作系统安装到Ubuntu22.04简介(手动)

主要参考: https://book.guyuehome.com/ROS2/1.系统架构/1.3_ROS2安装方法/ 官方文档:https://docs.ros.org/en/humble/Installation.html 虚拟机与ubuntu系统安装 略,见参考文档 ubutun换国内源,略 1. 设置本地语言 确保您有…

C 调用 C++:extern “C” 接口详解与实践 C/C++混合编译

C 调用 C:extern “C” 接口详解与实践 核心问题在于 C 编译器会对函数名进行“修饰”(Name Mangling)以支持函数重载等特性,而 C 编译器则不会。此外,C 语言本身没有类、对象等概念。为了解决这个问题,我…

汽车制造行业如何在数字化转型中抓住机遇?

近年来,随着新一轮科技革命和产业变革的深入推进,汽车制造行业正迎来一场前所未有的数字化转型浪潮。无论是传统车企还是新势力品牌,都在积极探索如何通过数字化技术提升竞争力、开拓新市场。那么,在这场变革中,汽车制…

k8s学习记录(五):Pod亲和性详解

一、前言 上一篇文章初步探讨了 Kubernetes 的节点亲和性,了解到它在 Pod 调度上比传统方式更灵活高效。今天我们继续讨论亲和性同时Kubernetes 的调度机制。 二、Pod亲和性 上一篇文章中我们介绍了节点亲和性,今天我们讲解一下Pod亲和性。首先我们先看…

HarmonyOS:Navigation实现导航之页面设置和路由操作

导读 设置标题栏模式设置菜单栏设置工具栏路由操作页面跳转页面返回页面替换页面删除移动页面参数获取路由拦截 子页面页面显示类型页面生命周期页面监听和查询 页面转场关闭转场自定义转场共享元素转场 跨包动态路由系统路由表自定义路由表 示例代码 Navigation组件适用于模块…

雪花算法

目录 一、什么是雪花算法 二、使用雪花算法 ​三、使用UUID 使用自增主键是数据库中常用的唯一标识,今天尝试使用mybatisplus来实现三种方式的主键ID 使用起来也很简单 用注解指定一下使用那种方式的主键 一、什么是雪花算法 一种特殊的算法可以计算得到一个唯…