JavaScript进阶-高阶技巧

news2024/11/14 22:07:51

文章目录

  • 高阶技巧
    • 深浅拷贝
      • 浅拷贝
      • 深拷贝
    • 异常处理
      • 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.uname
			newObj[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 {
		// 可能发生错误的代码要写到try
		const 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指向btn
			setTimeout(function() {
				// 在这个普通函数里面,要this由原来的window改为btn
				this.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>
		// 利用防抖实现性能优化
		// 需求:鼠标在盒子上移动,里面的数字就会变化+1
		const box = document.querySelector('.box')
		let i = 1
		function mouseMove() {
			box.innerHTML = i++
			// 如果里面存在大量消耗性能的代码,比如DOM操作、数据处理,可能造成卡顿
		}
		
		// 添加事件
		// box.addEventListener('mousemove',mouseMove)
		
		// 利用lodash库实现防抖 500毫秒之后+1
		box.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>
		// 利用节流实现性能优化
		// 需求:鼠标在盒子上移动,里面的数字就会变化+1
		const box = document.querySelector('.box')
		let i = 1
		function mouseMove() {
			box.innerHTML = i++
			// 如果里面存在大量消耗性能的代码,比如DOM操作、数据处理,可能造成卡顿
		}
		
		// 添加事件
		// box.addEventListener('mousemove',mouseMove)
		
		// 利用lodash库实现节流 500毫秒之后+1
		box.addEventListener('mousemove',_.throttle(mouseMove,500))

		// 手写函数部分
		// 1.声明一个定时器变量
		// 2.当鼠标每次滑动都先判断是否有定时器,如果有定时器则不开启新定时器
		// 3.如果没有定时器则开启定时器,记得存到变量里面
		// 3.1定时器里面调用执行的函数
		// 3.2定时器里面要把定时器清空
		function throttle(fn,t) {
			let timer = null
			return function() {
				if(!timer) {
					timer = setTime(function() {
					fn()
					// 清空定时器
					// 在setTimeout中是无法删除定时器的,因为定时器还在运作,故不使用clearTimeront(timer)
					timer = null
					},t)
				}
			}
		}
		box.addEventListener('mousemove',throttle(mouseMove,500))
	</script>
</body>

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

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

相关文章

STM32(8)NVIC编程

中断源由部分片上外设产生 在misc.h中找&#xff0c;杂项 配置NVIC GPIO和AFIO不能产生中断源&#xff0c;但能通过EXTI&#xff0c;由EXTI产生中断源 NVIC不需要开启时钟&#xff0c;因为NVIC模块位于内核内部&#xff0c;芯片一上电就能工作。 中断响应函数 中断向量表在启…

移动感知终端软件发布过程中遇到的问题以及解决方案

一.软件发布 软件的效果展示在文章&#xff1a; 网络图谱构建系统目前已实现的功能-CSDN博客 在android studio编写完程序之后&#xff0c;要打包并发布软件&#xff0c;供其他用户使用&#xff0c;以下几篇文章给出了方法&#xff1a; Android Studio使用签名打包发布APP&…

day09_商品管理订单管理SpringTaskEcharts

文章目录 1 商品管理1.1 添加功能1.1.1 需求说明1.1.2 核心概念SPUSKU 1.1.3 加载品牌数据CategoryBrandControllerCategoryBrandServiceCategoryBrandMapperCategoryBrandMapper.xml 1.1.4 加载商品单元数据ProductUnitProductUnitControllerProductUnitServiceProductUnitMap…

Qt 简约美观的动画 摆钟风格 第十季

&#x1f60a; 今天给大家分享一个摆钟风格的加载动画 &#x1f60a; 效果如下: 最近工作忙起来了 , 后续再分享其他有趣的加载动画吧. 一共三个文件 , 可以直接编译运行 //main.cpp #include "LoadingAnimWidget.h" #include <QApplication> #include <Q…

构建安全的REST API:OAuth2和JWT实践

引言 大家好&#xff0c;我是小黑&#xff0c;小黑在这里跟咱们聊聊&#xff0c;为什么REST API这么重要&#xff0c;同时&#xff0c;为何OAuth2和JWT在构建安全的REST API中扮演着不可或缺的角色。 想象一下&#xff0c;咱们每天都在使用的社交媒体、在线购物、银行服务等等…

Spring框架精髓:带你手写IoC

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大三在校生&#xff0c;喜欢AI编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;落798. &#x1f43c;个人WeChat&#xff1a;hmmwx53 &#x1f54a;️系列专栏&#xff1a;&#x1f5bc;️…

C语言之OJ刷题

今天刷一下题 刷的不多 第一道 链表的回文结构 仔细看这个题它是有限制条件的 首先是时间复杂度和空间复杂度 所以我们并不是用数组去做 但怎么做呢&#xff1f; 思路 既然是判断是否是回文结构&#xff0c;那么我们就找一下他的中间节点 然后将后半段倒置 进行比较…

【简说八股】Redisson的守护线程是怎么实现的

Redisson Redisson 是一个 Java 语言实现的 Redis SDK 客户端&#xff0c;在使用分布式锁时&#xff0c;它就采用了「自动续期」的方案来避免锁过期&#xff0c;这个守护线程我们一般也把它叫做「看门狗」线程。 Redission是一个在Java环境中使用的开源的分布式缓存和分布式锁实…

C2远控Loader红队技巧

inlineHook技术(钩子技术) MessageBoxA C自带弹窗函数 test_MessageBoxA 代码中自定义函数 InlineHook技术&#xff1a;testA原本插入jmp指令跳转到testB&#xff0c;实现testB自定义的函数 实现方式&#xff1a;X86&#xff1a; // 方式一&#xff0c;使用jmp相对地址跳转…

基于springboot音乐翻唱与分享平台源码和论文

1.1研究背景 随着网络不断的普及发展&#xff0c;音乐网站与分享平台依靠网络技术的支持得到了快速的发展&#xff0c;首先要从用户的实际需求出发&#xff0c;通过了解用户的需求开发出具有针对性的首页、音乐资讯、音乐翻唱、在线听歌、留言反馈、个人中心、后台管理、客服功…

ABAP - SALV教程16 合计、小计

虽然ALV的标准状态栏功能就能实现合计、小计、平均值、最大值等这些功能&#xff0c;但用户更希望一进去ALV就希望ALV已经对数量&#xff0c;金额的字段进行合计&#xff0c;小计。SALV实现合计&#xff0c;调用CL_SALV_AGGREGATIONS的ADD_AGGREGATION即可 DATA(lo_aggrs) …

[数据结构]链表OJ--环形链表判断是否有环(快慢指针法)

141. 环形链表 - 力扣&#xff08;LeetCode&#xff09; 这里我采用的是快慢指针法,这是我认为最容易理解的方法,这个方法的思路是这样的. 我们可以定义两个指针一个快一个慢,如果这个链表有环,则快慢指针一定会相遇. 这里我画图举个例子: 我们很明显的可以看出,有环链表,快指…

成功解决git clone遇到的error: RPC failed; curl 16 Error in the HTTP2 framing layer fatal: expected flush af

成功解决git clone遇到的error: RPC failed; curl 16 Error in the HTTP2 framing layer fatal: expected flush af 问题描述解决方案 问题描述 用git的时候可能会遇到这个问题&#xff1a; (base) zhouzikang7443-8x4090-120:~/project$ git clone https://github.com/123/12…

Outlook邮箱IMAP密码怎么填写?账户设置?

Outlook邮箱IMAP密码是什么&#xff1f;Outlook如何设置IMAP&#xff1f; 许多用户会选择通过IMAP协议将邮箱与各种邮件客户端进行连接。而在设置过程中&#xff0c;填写IMAP密码是必不可少的一步。那么&#xff0c;Outlook邮箱的IMAP密码应该如何填写呢&#xff1f;接下来&am…

Matlab 最小二乘插值(曲线拟合)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 在多项式插值时,当数据点个数较多时,插值会导致多项式曲线阶数过高,带来不稳定因素。因此我们可以通过固定幂基函数的最高次数 m(m < n),来对我们要拟合的曲线进行降阶。之前的函数形式就可以变为: 二、实现…

【硬件工程师面经整理13_电容电阻电感等效电路】

1 电容/电阻/电感的等效电路 ①电容的等效电路是由一个电容和一个电阻组成的&#xff0c;其中电阻称为ESR&#xff08;Equivalent Series Resistance&#xff0c;等效串联电阻&#xff09;。在真实情况下&#xff0c;一个电容会被表示成由“一个电容一个电阻一个电感”组合而成…

冒泡排序 和 qsort排序

目录 冒泡排序 冒泡排序部分 输出函数部分 主函数部分 总代码 控制台输出显示 总代码解释 冒泡排序优化 冒泡排序 主函数 总代码 代码优化解释 qsort 排序 qsort 的介绍 使用qsort排序整型数据 使用qsort排序结构数据 冒泡排序 首先&#xff0c;我先介绍我的冒泡…

解决虚拟机启动报错:“End kernel panic - not syncing: attempted to kill the idle task”

原本能正常运行的虚拟机&#xff0c;很长一段时间没用后&#xff0c;今天再次启动&#xff0c;然后就出现下面的问题&#xff1a; 然后走了一些弯路&#xff0c;比如说删除该虚拟机然后新建一个虚拟机&#xff08;问题未解决&#xff09;、直接删除VitualBox重新安装&#xff0…

wordpress外贸独立站

WordPress外贸电商主题 简洁实用的wordpress外贸电商主题&#xff0c;适合做外贸跨境的电商公司官网使用。 https://www.jianzhanpress.com/?p5025 华强北面3C数码WordPress外贸模板 电脑周边、3C数码产品行业的官方网站使用&#xff0c;用WordPress外贸模板快速搭建外贸网…

计算机指令、指令跳转原理

文章目录 前言存储程序型计算机代码怎么变成机器码&#xff1f;解析指令和机器码CPU 是如何执行指令的&#xff1f;CPU中的寄存器 if…else 来看程序的执行和跳转分析 通过 if…else 和 goto 来实现循环 前言 大家好我是jiantaoyab&#xff0c;这是我所总结作为学习的笔记第三篇…