【H2O2|全栈】JS进阶知识(八)ES6(4)
目录
前言
开篇语
准备工作
浅拷贝和深拷贝
浅拷贝
概念
常见方法
弊端
案例
深拷贝
概念
常见方法
弊端
逐层拷贝
原型
构造函数
概念
形式
成员
弊端
显式原型和隐式原型
概念
形式
constructor
概念
形式
原型链
概念
形式
结束语
前言
开篇语
本系列博客主要分享JavaScript的进阶语法知识,本期为第八期,依然围绕ES6的语法进行展开。
本期内容为:拷贝和原型。
与基础部分的语法相比,ES6的语法进行了一些更加严谨的约束和优化,因此,在之后使用原生JS时,我们应该尽量使用ES6的语法进行代码编写。
准备工作
软件:【参考版本】Visual Studio Code
插件(扩展包):Open in browser, Live Preview, Live Server, Tencent Cloud AI Code Assistant, htmltagwrap
提示:在不熟练的阶段建议关闭AI助手
浏览器版本:Chrome
系统版本: Win10/11/其他非Windows版本
浅拷贝和深拷贝
浅拷贝
概念
对于基本数据类型,它们存储于栈内存中,浅拷贝直接就是把这份数据复制一份,依然存储在栈内存中。
而对于引用数据类型,在栈内存中存储的只是指向堆内存中数据的地址。浅拷贝只能复制这个地址,而不能真正复制里面的数据。
简而言之,浅拷贝只能实现复制一层。
常见方法
ES6提供的浅拷贝的方式为Object.assign(),JQuery也为我们提供了$.clone()方法。
弊端
由于浅拷贝只能复制引用类型数据的地址,所以复制之后的“所谓的新变量”实际上和原来的变量还是指向了堆内存中相同的数据。
那么,当我们修改“新变量”中的数据时,原来的变量下存储的数据同样被改变了。
案例
来看下面的例子——
let obj1 = {name: 'aa', age: 18}
let obj2 = obj1;
obj2.age = 20;
console.log(obj1)
此时输出结果为:
很显然,我们的目的明明只是更改obj2的数据,最后却将obj1的数据更改了。
深拷贝
概念
对于引用类型的数据,我们需要创建一个新的空对象,对数据内部的每一项都进行拷贝,并添加到空对象中,以此达到真正的意义上的拷贝。
有时,我们对象中的数据也可能是引用类型的,乃至在这些数据中,还有多层的引用类型数据嵌套。我们的深拷贝,应该不断深入至数据项的每一层,确保每一层都进行拷贝。
简单来说,深拷贝就是多层拷贝,逐层进行拷贝。
常见方法
我们知道,JSON的stringify()方法可以按照对象原来的格式(结构)进行复制,而parse()方法又可以将它们转换回对象。
JSON.parse(JSON.stringify(obj))
在两次转换的过程中,parse()实际上返回了一个新的对象,由此,我们就实现了引用数据类型的深拷贝。
弊端
然而,使用JSON转化的方式实现深拷贝,会带来如下的问题——
- 对象属性的enumerable: false会失效
- Date数据类型会被转化为字符串
- RegExp,Error数据类型会被转换成空对象 {}
- undefined,function,symbol的属性会丢失
- NaN,-Infinity,Infinity将会被转化为null
这就会导致我们复制的新对象和原对象的数据可能会出现差异,造成拷贝失败的后果。
逐层拷贝
对此,我们处理上述问题的思路是不一次性全处理,而是分开应对各类情况。
由于我们的数据项中可能依然有引用数据类型或多层的结构,所以我们要不断地向深层进行搜索,而递归方法就能很好的解决多层搜索的问题,即在遇到某些引用类型的数据时,重复搜索-拷贝的步骤。
假设我们的递归方法的名称为deep(ori),传入的参数ori为待拷贝变量,方法内部返回拷贝后的结果的变量res。
首先,判断这个变量是否为引用数据类型,即使用typeof运算符得到的结果为Object,如果不是,则为基本数据类型,直接使用赋值运算符=复制即可。
if (typeof ori == 'object') {} else {res = ori;
}
然后,利用构造器constructor判断数据是否为RegExp,Date和null中的一种,如是则依然直接使用=复制。
这三种情况下,数据不会是多层的结构,不需要再进入数据内部拷贝深层。
if (ori.constructor == RegExp || ori.constructor == Date || !ori) {res = ori
}
然后,判断数据是否为Array类型,这种类型的数据可以存储元素,所以需要对其进行遍历。
而Array的每一项,依然可能为Array或对象等可以存储元素的结构,因此还需要对遍历的每一项item再进行一次deep(item)(递归)。
else if (ori.constructor == Array) {res = []ori.forEach(item => res.push(deep(item)))
}
最后,剩下的情况就是对象类型,使用类似处理数组的方式处理即可。
else {res = {}for (let key in ori) {res[key] = deep(ori[key])}
}
最后,逐层拷贝的完整代码如下——
function deep(ori) {let res;if (typeof ori == 'object') {if (ori.constructor == RegExp || ori.constructor == Date || !ori) {res = ori} else if (ori.constructor == Array) {res = []ori.forEach(item => res.push(deep(item)))} else {res = {}for (let key in ori) {res[key] = deep(ori[key])}}} else {res = ori}return res}
原型
构造函数
概念
构造函数是我们用来创建实例化对象的方法,它将传入的参数赋值给this中对应的属性。它的this指向为创建出来的实例化对象。
形式
比方说,我们想要用构造函数Per来创建人对象,那么我们可以这样来写Per()方法——
function Per(name, age, sex) {this.name = name;this.age = age;this.sex = sex;this.getMsg = function () {console.log(`姓名:${this.name};年龄:${this.age};性别:${this.sex}。`);}}
成员
构造函数的成员是指构造函数内部的属性,分为实例成员和静态成员两种。
实例成员是构造函数内通过this创建的成员,这类成员的特点是可以被实例化,也就是可以使用下面的形式去访问——
实例.成员
静态成员是构造函数外通过构造函数名创建的成员,这类成员的特点是不可以被实例访问,但是只能使用下面的形式去访问——
构造函数名.成员
弊端
我们知道,方法也是可以作为构造函数的实例成员的。
比如,我们创建两个人对象——
let aa = Per('aa', 22, 'man');
let bb = Per('bb', 20, 'man');
对于它们的getMsg()成员,使用==进行比较,显然是不同的。
console.log(aa.getMsg == bb.getMsg); // false
因此,这种方式存在一个问题,就是我们每创建一个实例化对象调用一次方法,就需要开辟一块新的栈空间来完成一件重复的事,造成内存浪费的问题。
所以,我们需要使用一块公用的空间,来存储同一个构造函数创建出来的公用方法。
如果使用静态成员方法,由于它不能被实例对象访问,所以方法的this将会失效。
显式原型和隐式原型
概念
原型实际上就是一块空间,用来存储一些构造函数和实例化对象的公用方法与属性。
当我们使用到这些公用的内容时,就可以利用原型来访问,由此达到节省空间的目的。
对于构造函数,使用prototype来获取原型,这类原型就是显式原型;
对于实例化对象,使用__proto__来获取原型(注意两边都有两个_哈),这类原型就是隐式原型。
对于构造函数和它实例化的对象,它们的显式原型和隐式原型相同。
形式
将上面的getMsg()方法添加到原型中,就像下面这样——
Per.prototype.getMsg = function () {console.log(`姓名:${this.name};年龄:${this.age};性别:${this.sex}。`);
}
那么,对于Per构造函数创建出来的所有实例化对象,都可以调用原型的getMsg()方法。
对于下面的两种方式调用getMsg(),实际上访问的是内存中的相同位置——
console.log(Per.prototype === aa.__proto__); // true
constructor
概念
constructor,中文释义为构造函数,用来从原型指回原来的构造函数。
我们可以在原型中声明多个方法,然后利用constructor把它们一次性交给原来的构造函数。
形式
使用下面的三个方法,将Per实例化对象的name,age,sex属性分别输出出来——
Per.prototype = {constructor: Per,getName() {console.log(this.name);},getAge() {console.log(this.age);},getSex() {console.log(this.sex);}}
原型链
概念
原型链,实质上就是链式查找方法。
如果在调用方法时,自身没有方法,则程序会在原型链上查找方法。
一条完整的原型链为实例=>构造函数原型=>Object=>null(报错),Object是所有构造函数、对象的原型,又叫做基类。
如果在基类上依然没有找到我们需要的方法,则程序会向我们报错,提示没有该方法。
形式
最长的原型链如下——
实例化对象.prototype.__proto__.__proto__
在该位置仍然找不到目标方法时报错。
整个原型链的各个部分的转化关系示意图如下——
结束语
本期内容到此结束。关于本系列的其他博客,可以查看我的JS进阶专栏。
在全栈领域,博主也只不过是一个普通的萌新而已。本系列的博客主要是记录一下自己学习的一些经历,然后把自己领悟到的一些东西总结一下,分享给大家。
文章全篇的操作过程都是笔者亲自操作完成的,一些定义性的文字加入了笔者自己的很多理解在里面,所以仅供参考。如果有说的不对的地方,还请谅解。
==期待与你在下一期博客中再次相遇==
——临期的【H2O2】
相关文章:

【H2O2|全栈】JS进阶知识(八)ES6(4)
目录 前言 开篇语 准备工作 浅拷贝和深拷贝 浅拷贝 概念 常见方法 弊端 案例 深拷贝 概念 常见方法 弊端 逐层拷贝 原型 构造函数 概念 形式 成员 弊端 显式原型和隐式原型 概念 形式 constructor 概念 形式 原型链 概念 形式 结束语 前言 开篇语…...
OmniDiskSweeper :一款专为 macOS 设计的磁盘使用分析工具
OmniDiskSweeper 是一款专为 macOS 设计的磁盘使用分析工具,由 The Omni Group 开发。它的主要目的是帮助用户可视化磁盘上的文件和文件夹,并找出占用大量空间的文件,从而帮助用户释放磁盘空间。 OmniDiskSweeper 的特点包括: 简…...
【什么是Redis?】
Redis:高性能内存数据库的深度探索 在当今这个数据驱动的世界里,数据库的选择直接关系到应用程序的性能、可扩展性和可靠性。在众多数据库解决方案中,Redis以其卓越的性能、丰富的数据结构和灵活的使用场景脱颖而出,成为众多开发…...
React第十六章(useLayoutEffect)
useLayoutEffect useLayoutEffect 是 React 中的一个 Hook,用于在浏览器重新绘制屏幕之前触发。与 useEffect 类似。 用法 useLayoutEffect(() > {// 副作用代码return () > {// 清理代码}}, [dependencies]);参数 setup:Effect处理函数,可以返回…...

shell 基础知识2 ---条件测试
目录 一、条件测试的基本语法 二、文件测试表达式 三、字符串测试表达式 四、整数测试表达式 五、逻辑操作符 六、实验 为了能够正确处理 Shell 程序运行过程中遇到的各种情况, Linux Shell 提供了一组测试运算符。 通过这些运算符,Shell 程序能够…...

【线程】Java线程操作
【线程】Java线程操作 一、启动线程1.1 run()和start()的区别 二、终止线程三、等待线程四、线程的状态 一、启动线程 Java中通过start()方法来启动一个线程,其次我们要着重理解start()和run()的区别。 1.1 run()和start()的区别 我们通过一份代码来进行观察&…...
Linux内核
Linux内核是Linux操作系统的核心部分,它管理着硬件资源并提供基本的服务给用户程序。以下是Linux内核的几个关键方面: 1. 架构: 单内核设计:Linux采用的是单内核设计,这意味着所有操作系统服务都在一个地址空间内运行…...

Sentinel服务保护
Sentinel是阿里巴巴开源的一款服务保护框架,目前已经加入SpringCloudAlibaba中。官方网站: home | Sentinel Sentinel 的使用可以分为两个部分: 核心库(Jar包):不依赖任何框架/库,能够运行于 Java 8 及以…...

python代码制作数据集的测试和数据质量检测思路
前言 本文指的数据集为通用数据集,并不单是给机器学习领域使用。包含科研和工业领域需要自己制作数据集的。 首先,在制作大型数据集时,代码错误和数据问题可能会非常复杂。 前期逻辑总是简单的,库库一顿写,等排查的时…...
笔记记录 k8s-install
master节点安装: yum upgrade -y 更新系统 yum update -y 升级内核 ifconfig ens33 关闭swap swapoff -a (临时) vim /etc/fstab (永久) #/dev/mapper/cl-swap swap swap defaults 0 0 vim /etc/sysctl.conf vm.swappin…...

丹摩征文活动|基于丹摩算力的可图(Kolors)的部署与使用
Kolors是一个以生成图像为目标的人工智能系统,可能采用了类似于OpenAI的DALLE、MidJourney等文本生成图像的技术。通过自然语言处理(NLP)和计算机视觉(CV)相结合,Kolors能够根据用户提供的文本描述生成符合…...

【Vue】 npm install amap-js-api-loader指南
前言 项目中的地图模块突然打不开了 正文 版本太低了,而且Vue项目就应该正经走项目流程啊喂! npm i amap/amap-jsapi-loader --save 官方说这样执行完,就这结束啦!它结束了,我还没有,不然不可能记录这篇文…...

MacOS下的Opencv3.4.16的编译
前言 MacOS下编译opencv还是有点麻烦的。 1、Opencv3.4.16的下载 注意,我们使用的是Mac,所以ios pack并不能使用。 如何嫌官网上下载比较慢的话,可以考虑在csdn网站上下载,应该也是可以找到的。 2、cmake的下载 官网的链接&…...

Android中的依赖注入(DI)框架Hilt
Hilt 是 Android 提供的一种依赖注入(DI)框架,它基于 Dagger,目的是简化依赖注入的使用,提供更易用的接口和与 Android 生命周期组件的紧密集成。下面是 Hilt 的详细介绍。 为什么选择 Hilt? 依赖注入的优势…...

5.STM32之通信接口《精讲》之USART通信---实验串口接收程序
根据上节,我们一已经完成了串口发送程序的代码,并且深入的解析探索了串口的原理,接下来,Whappy小编将带领大家进入串口接收程序的探索与实验,并将结合上一节串口发送一起来完成串口的发送和接收实验。 上来两张图 上图…...

【Redis_Day6】Hash类型
【Redis_Day6】Hash类型 Hash类型操作hash的命令hset:设置hash中指定的字段(field)的值(value)hsetnx:想hash中添加字段并设置值hget:获取hash中指定字段的值hexists:判断hash中是否…...

[开源] SafeLine 好用的Web 应用防火墙(WAF)
SafeLine,中文名 “雷池”,是一款简单好用, 效果突出的 Web 应用防火墙(WAF),可以保护 Web 服务不受黑客攻击 一、简介 雷池通过过滤和监控 Web 应用与互联网之间的 HTTP 流量来保护 Web 服务。可以保护 Web 服务免受 SQL 注入、XSS、 代码注…...

40分钟学 Go 语言高并发:Select多路复用
Select多路复用 学习目标 知识点掌握程度应用场景select实现原理深入理解底层机制channel通信和多路选择超时处理掌握超时控制方法避免阻塞和资源浪费优先级控制理解优先级实现处理多个channel的顺序性能考虑了解性能优化点高并发场景优化 1. Select实现原理 让我们通过一个…...

candence: 如何快速设置SUBCLASS 的颜色
如何快速设置SUBCLASS 的颜色 一、一般操作 正常情况下修改SUBCLASS,需要如下步骤进行设置: 二、快速操作 右键,选择一个颜色即可...

FinalShell进行前端项目部署及nginx配置
首先需要准备服务器(阿里云、腾讯云都可)与域名; 示例为阿里云服务器; 1.进行FinalShell下载 下载官网 https://www.hostbuf.com/ 2.下载完毕后 配置FinalShell ssh 名称自定义即可! 2-1 提示连接成功 3.首先检查nginx是否下载 …...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...

GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...
Redis:现代应用开发的高效内存数据存储利器
一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发,其初衷是为了满足他自己的一个项目需求,即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源,Redis凭借其简单易用、…...
HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散
前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说,在叠衣服的过程中,我会带着团队对比各种模型、方法、策略,毕竟针对各个场景始终寻找更优的解决方案,是我个人和我司「七月在线」的职责之一 且个人认为,…...