JavaScript进阶-高阶技巧
文章目录
- 高阶技巧
- 深浅拷贝
- 浅拷贝
- 深拷贝
- 异常处理
- throw抛异常
- try/caych捕获异常
- debugger
- 处理this
- this指向
- 改变this
- 性能优化
- 防抖
- 节流
高阶技巧
深浅拷贝
只针对引用类型
浅拷贝
拷贝对象后,里面的属性值是简单数据类型直接拷贝值,如果属性值是引用数据类型则拷贝的是地址
常见语法:
1.拷贝对象:Object.assgin(新变量,被拷贝对象)
或const 变量名 = {...对象}
2.拷贝数组:Array.prototype.concat()
或[...arr]
注意:如果是简单数据类型拷贝值,引用数据类型拷贝的是地址(如果是单层对象没有问题,如果是多层对象就有问题)
深拷贝
拷贝的是对象,不是地址
常见方法:
- 通过递归实现深拷贝
函数递归:如果一个函数在内部可以调用其本身,那么这个函数就是递归函数
由于递归容易发生“栈溢出”错误。所以必须要加退出条件return
const obj = {uname:'pink',age:18,hobby:['乒乓球','足球'],family:{baby:'xiaop'}
}
const o = {}
// 拷贝函数
// 深拷贝,拷贝的新对象不会影响旧对象,遇到数组和对象就再次调用递归函数,先数组再对象
function deepCopy(newObj,oldObj) {for(let k in oldObj) {// 一定先写数组,在写对象,因为万物皆对象// 处理数组的问题if (oldObj[k] instanceof Array) {newObj[k] = []// 函数递归deeepCopy(newObj[k],oldObj[k])} // 处理对象的问题else if (oldObj[k] instanceof Object) {newObj[k] = {}deepCopy(newObj[k],oldObj[k])} else {// k属性名,oldObj[k]属性值// newObj[k] === o.unamenewObj[k] = oldObj[k]}}
}
deepCopy(o,obj)// 函数调用两个参数
o.age = 20
console.log(o)
o.hobby[0] = '篮球'
o.family.baby = 'oldp'
console.log(obj)
- lodash/cloneDeep
JavaScript库lodash里面cloneDeep内部实现了深拷贝
语法:const deep = _.cloneDeep(object)
<!-- 先引用 -->
<script src="./lodash.min.js"></script>
<script>const obj = {uname:'pink',age:18,hobby:['乒乓球','足球'],family:{baby:'xiaop'}}const o = _.cloneDeep(obj)o.family.baby = 'oldp'console.log(obj)
</script>
- 通过JSON.stringfy()实现
const obj = {uname:'pink',age:18,hobby:['乒乓球','足球'],family:{baby:'xiaop'}
}
// JSON.stringify() 把对象转换为JSON字符串
// JSON.parse() 把JSON字符串转换为对象
const o = JSON.parse(JSON.stringify(obj))
o.family.baby = 'oldp'
console.log(obj)
异常处理
异常处理是指预估代码执行过程中可能发生的错误,然后最大程度的避免错误的发生导致整个程序无法继续运行
throw抛异常
1.throw抛出异常信息,程序也会终止执行
2.throw后面跟的是错误提示信息
3.Error对象配合throw使用,能够设置更详细的错误信息
function fn(x,y) {if (!x || !y) {// throw '没有参数传递进来'throw new Error('没有参数传递进来')}return x + y
}
try/caych捕获异常
通过try/catch捕获错误信息(浏览器提供的错误信息)
1.try…catch用于捕获错误信息
2.将预估可能发生了错误的代码写在try代码段中
3.如果try代码段中出现错误后,会执行catch代码段,并截获到错误信息
4.finally不管是否错误,都会执行
function fn() {try {// 可能发生错误的代码要写到tryconst p = document.querySelector('.p')p.style.color = 'red' } catch (err) {// 拦截错误,提示浏览器提供的错误信息,但是不中断程序的执行console.log(err.message)// 需要return中断程序,可以用throw代替throw new Error('错误')} finally {// 不管你程序对不对,一定会执行的代码alert('弹出对话框')}
}
debugger
在代码中需要调试的地方写debugger,打开控制台后自动来到debugger的位置,类似于断点
处理this
this指向
- 普通函数
普通函数的调用方式决定了this的值,即谁调用就指向谁
普通函数没有明确调用者时this值为window,严格模式下没有调用者时this的值为undefined - 箭头函数
箭头函数中的this与普通函数完全不同,也不受调用方式影响,事实上箭头函数中并不存在this
1.箭头函数会默认帮我们绑定外层this的值,所以在箭头函数中this的值和外层的this是一样的
2.箭头函数中的this引用的就是最近作用域中的this
3.向外层作用域中,一层一层查找this,直到有this的定义
注意:
1.在开发中使用箭头函数前需要考虑函数中this的值,事件回调函数使用箭头函数时,this为全局变量时,this为全局的window
因此DOM事件回调函数如果里面需要DOM对象的this,则不推荐使用箭头函数
2.同样由于箭头函数this的原因,基于原型的面向对象也不推荐采用箭头函数
改变this
- call()
使用call()方法调用函数,同时指定被调用函数中的this的值
语法:fn.call(thisArg,arg1,arg2,...)
thisArg:在fn函数运行时指定的this值
arg1,arg2:传递的其他参数
返回值就是函数的返回值,因为它就是调用函数 - apply()
使用apply()方法调用函数,同时指定被调用函数中的this的值
语法:fn.apply(thisArg,[argsArray])
thisArg:在fn函数运行时指定的this值
argsArray:传递的值,必须包含在数组里面
返回值就是函数的返回值,因为它就是调用函数
因此apply()主要跟数组有关系,比如使用Math.max()求数组的最大值
const obj = {age:18
}
function fn(x,y) {console.log(this)// {age:18}console.log(x + y)// 3
}
// 调用函数
// 改变this指向
fn.apply(obj,[1,2])
// 3.返回值 就是函数的返回值// 使用场景:求数组最大值
const arr = [1,2,3]
const max = Math.max.apply(Math,arr)
// console.log(Math.max(...arr))
console.log(max)// 3
- bind()(重点)
bind()方法不会调用函数,但是能改变函数内部this指向
语法:fn.bind(thisArg,arg1,arg2...)
thisArg:在fn函数运行时指定的this的值
arg1,arg2:传递的其他参数
返回由指定的this值和初始化参数改造的原拷贝函数(新函数)
因此当我们只是想改变this指向,并且不想调用这个函数的时候,可以使用bind,比如改变计时器内部的this指向
const obj = {age:18
}
function fn() {console,log(this)
}
// 返回值是个函数,但是这个函数里面的this是更改过的
const fn = fn.bind(obj)
// console.log(fn)
fn()// {age:18}
<body><button>发送</button><script>// 需求,有一个按钮,点击里面就禁用,2秒钟之后开始const btn = document.querySelector('button')btn.addEventListener('click', function() {// 禁用按钮this.display = true//this指向btnsetTimeout(function() {// 在这个普通函数里面,要this由原来的window改为btnthis.disabled = false}.bind(this),2000)//这个this是上面指向btn的this})</script>
</body>
性能优化
防抖
防抖:单位时间内,频繁触发事件,只执行最后一次
使用场景:
1.搜索框搜索输入。只需要用户最后一次输入完,再发送请求
2.手机号、邮箱验证输入检测
常用方法:
- lodash提供的防抖来处理
语法:_.debounce(func,[wait=0],[options=])
func:要防抖的函数
[wait=0]:需要延迟的毫秒数
[options=]:选项对象 - 手写防抖函数
核心思路:利用定时器(setTimeout)来实现
例如:
<style>.box {width:500px;height:500px;background-color:#ccc;color:#fff;text-align:center;font-size:100px;}
</style>
<body><div class="box"></div><script src='./lodash.min.js'><script>// 利用防抖实现性能优化// 需求:鼠标在盒子上移动,里面的数字就会变化+1const box = document.querySelector('.box')let i = 1function mouseMove() {box.innerHTML = i++// 如果里面存在大量消耗性能的代码,比如DOM操作、数据处理,可能造成卡顿}// 添加事件// box.addEventListener('mousemove',mouseMove)// 利用lodash库实现防抖 500毫秒之后+1box.addEventListener('mousemove',_.debounce(mouseMove,500))// 手写函数部分// 1.声明定时器变量// 2.每次鼠标移动(事件触发)的时候都要先判断是否有定时器,如果有先清除以前的定时器// 3.如果没有定时器,则开启定时器,存入到定时器变量里面// 4.定时器里面写函数调用function debounce(fn,t) {let timer// return返回一个匿名函数return function() {if(timer) {clearTimeout(timer)} timer = setTimeout(function() {fn()//加小括号调用fn函数},t) }}box.addEventListener('mousemove',debounce(mouseMove,500))</script>
</body>
节流
节流:单位时间内,频繁触发事件,只执行一次
使用场景:
高频事件:鼠标移动mousemove、页面尺寸缩放resize、滚动条滚动scroll
常用方法:
- lodash提供的节流函数来处理
语法:_.throttle(func,[wait=0],[options=])
func:要节流的函数
[wait=0]:需要节流的毫秒数
[options=]:选项对象
[options.leading=true]:指定调用在节流开始前
[options.leading=false]:指定调用在节流结束后 - 手写一个节流函数来处理
核心思路:利用定时器(setTimeout)来实现
例如:
<style>.box {width:500px;height:500px;background-color:#ccc;color:#fff;text-align:center;font-size:100px;}
</style>
<body><div class="box"></div><script src='./lodash.min.js'><script>// 利用节流实现性能优化// 需求:鼠标在盒子上移动,里面的数字就会变化+1const box = document.querySelector('.box')let i = 1function mouseMove() {box.innerHTML = i++// 如果里面存在大量消耗性能的代码,比如DOM操作、数据处理,可能造成卡顿}// 添加事件// box.addEventListener('mousemove',mouseMove)// 利用lodash库实现节流 500毫秒之后+1box.addEventListener('mousemove',_.throttle(mouseMove,500))// 手写函数部分// 1.声明一个定时器变量// 2.当鼠标每次滑动都先判断是否有定时器,如果有定时器则不开启新定时器// 3.如果没有定时器则开启定时器,记得存到变量里面// 3.1定时器里面调用执行的函数// 3.2定时器里面要把定时器清空function throttle(fn,t) {let timer = nullreturn function() {if(!timer) {timer = setTime(function() {fn()// 清空定时器// 在setTimeout中是无法删除定时器的,因为定时器还在运作,故不使用clearTimeront(timer)timer = null},t)}}}box.addEventListener('mousemove',throttle(mouseMove,500))</script>
</body>
相关文章:

JavaScript进阶-高阶技巧
文章目录 高阶技巧深浅拷贝浅拷贝深拷贝 异常处理throw抛异常try/caych捕获异常debugger 处理thisthis指向改变this 性能优化防抖节流 高阶技巧 深浅拷贝 只针对引用类型 浅拷贝 拷贝对象后,里面的属性值是简单数据类型直接拷贝值,如果属性值是引用数…...
C语言中“#“和“##“的用法
1. 前言 # :把宏参数变为一个字符串, ##:把两个宏参数贴合在一起. 2. 一般用法 #include<stdio.h> #define toString(str) #str //转字符串 #define conStr(a,b) (a##b)//连接 int main() { printf(toString(12345)): //输出字符串&q…...
Linux命令-clock命令(用于调整 RTC 时间)
说明 clock命令用于调整 RTC 时间。 RTC 是电脑内建的硬件时间,执行这项指令可以显示现在时刻,调整硬件时钟的时间,将系统时间设成与硬件时钟之时间一致,或是把系统时间回存到硬件时钟。 语法 clock [--adjust][--debug][--dir…...
编程笔记 Golang基础 045 math包
编程笔记 Golang基础 045 math包 一、math包主要功能常量:函数:数值运算:三角函数:对数函数:随机数相关: 二、示例代码一三、示例代码二小结 Go 语言的标准库 math 提供了一系列基础数学函数和常量…...

[Java 探索者之路] 一个大厂都在用的分布式任务调度平台
分布式任务调度平台是一种能够在分布式计算环境中调度和管理任务的系统,在此环境下,各个任务可以在独立的节点上运行。它有助于提升资源利用率,增强系统扩展性以及提高系统对错误的容忍度。 文章目录 1. 分布式任务调度平台1. 基本概念1.1 任…...

基于JAVA springboot+mybatis智慧生活分享平台设计和实现
基于JAVA springbootmybatis智慧生活分享平台设计和实现 博主介绍:多年java开发经验,专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《1000套》 欢迎点赞 收藏 ⭐留言 文末…...

详细了解C++中的namespace命名空间
键盘敲烂,月薪过万,同学们,加油呀! 目录 键盘敲烂,月薪过万,同学们,加油呀! 一、命名空间的理解 二、::作用域运算符 三、命名空间(namespace&…...

#WEB前端(HTML属性)
1.实验:a,img 2.IDE:VSCODE 3.记录: a: href插入超链接 默认情况下在本窗口打开链接, target可以设置打开的窗口,parent在父窗口打开,blank新开串口打开,top在顶层串口打开,self为默认在本窗口打开 img: 插入图片 可以插…...
LeetCode---【和的操作】
目录 两数之和我的答案在b站up那里学到的【然后自己复写】 和为 K 的子数组在b站up那里学到的【然后自己复写】 三数之和在b站up那里学到的【然后自己复写】 两数相加【链表】我的半路答案:没有看到是链表在b站up那里学到的【复写失败后整理】 两数之和 我的答案 …...

Docker容器与虚拟化技术:OpenEuler 使用 docker-compose 部署 LNMP
目录 一、实验 1.环境 2.OpenEuler 部署 docker-compose 3.docker-compose 部署 LNMP 二、问题 1.ntpdate未找到命令 2.timedatectl 如何设置时区与时间同步 3.php网页显示时区不对 一、实验 1.环境 (1)主机 表1 主机 系统架构版本IP备注Lin…...

13-微服务初探-自研微服务框架
微服务初探 1. 架构变迁之路 1.1 单体架构 互联网早期,一般的网站应用流量较小,只需要一个应用,将所有的功能代码都部署在一起就可以,这样可以减少开发,部署和维护的成本。 比如说一个电商系统,里面包含…...

LeetCode——二叉树(Java)
二叉树 简介[简单] 144. 二叉树的前序遍历、94. 二叉树的中序遍历、145. 二叉树的后序遍历二叉树层序遍历[中等] 102. 二叉树的层序遍历[中等] 107. 二叉树的层序遍历 II[中等] 199. 二叉树的右视图[简单] 637. 二叉树的层平均值[中等] 429. N 叉树的层序遍历[中等] 515. 在每个…...

LDR6328芯片:智能家居时代的小家电充电革新者
在当今的智能家居时代,小家电的供电方式正变得越来越智能化和高效化。 利用PD(Power Delivery)芯片进行诱骗取电,为后端小家电提供稳定电压的技术,正逐渐成为行业的新宠。在这一领域,LDR6328芯片以其出色的…...

用node写后端环境运行时报错Port 3000 is already in use
解决方法:关闭之前运行的3000端口,操作如下 1.WindowR输入cmd确定,打开命令面板 2.查看本机端口详情 netstat -ano|findstr "3000" 3.清除3000端口 taskkill -pid 41640 -f 最后再重新npm start即可,这里要看你自己项目中package.joson的启动命令是什…...

Git 如何上传本地的所有分支
Git 如何上传本地的所有分支 比如一个本地 git 仓库里定义了两个远程分支,一个名为 origin, 一个名为 web 现在本地有一些分支是 web 远程仓库没有的分支,如何将本地所有分支都推送到 web 这个远程仓库上呢 git push web --all...

【airtest】自动化入门教程(一)AirtestIDE
目录 一、下载与安装 1、下载 2、安装 3、打开软件 二、web自动化配置 1、配置chrome浏览器 2、窗口勾选selenium window 三、新建项目(web) 1、新建一个Airtest项目 2、初始化代码 3、打开一个网页 四、恢复默认布局 五、新建项目…...

ChatGPT支持下的PyTorch机器学习与深度学习技术应用
近年来,随着AlphaGo、无人驾驶汽车、医学影像智慧辅助诊疗、ImageNet竞赛等热点事件的发生,人工智能迎来了新一轮的发展浪潮。尤其是深度学习技术,在许多行业都取得了颠覆性的成果。另外,近年来,Pytorch深度学习框架受…...

Springboot+vue的医药管理系统(有报告)。Javaee项目,springboot vue前后端分离项目。
演示视频: Springbootvue的医药管理系统(有报告)。Javaee项目,springboot vue前后端分离项目。 项目介绍: 采用M(model)V(view)C(controller)三层…...

C语言:预处理
C语言:预处理 预定义符号#define定义常量定义宏宏与函数对比 #操作符##操作符条件编译头文件包含库文件包含本地文件包含嵌套文件包含 预定义符号 C语⾔设置了⼀些预定义符号,可以直接使⽤,预定义符号也是在预处理期间处理的。 __FILE__ //…...

计算机网络:路由协议
路由协议简介 路由协议是计算机网络中不可或缺的一部分,它们负责确定数据包从源地址到目的地址的最佳路径。想象一下,如果你是一个数据包,路由协议就像是地图或导航工具,指导你如何到达目的地。 目录 路由协议简介 工作原理简化…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...

Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...
Linux系统部署KES
1、安装准备 1.版本说明V008R006C009B0014 V008:是version产品的大版本。 R006:是release产品特性版本。 C009:是通用版 B0014:是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存:1GB 以上 硬盘…...
面试高频问题
文章目录 🚀 消息队列核心技术揭秘:从入门到秒杀面试官1️⃣ Kafka为何能"吞云吐雾"?性能背后的秘密1.1 顺序写入与零拷贝:性能的双引擎1.2 分区并行:数据的"八车道高速公路"1.3 页缓存与批量处理…...
前端高频面试题2:浏览器/计算机网络
本专栏相关链接 前端高频面试题1:HTML/CSS 前端高频面试题2:浏览器/计算机网络 前端高频面试题3:JavaScript 1.什么是强缓存、协商缓存? 强缓存: 当浏览器请求资源时,首先检查本地缓存是否命中。如果命…...