当前位置: 首页 > news >正文

JS进阶——高级技巧

版权声明
本文章来源于B站上的某马课程,由本人整理,仅供学习交流使用。如涉及侵权问题,请立即与本人联系,本人将积极配合删除相关内容。感谢理解和支持,本人致力于维护原创作品的权益,共同营造一个尊重知识产权的良好环境。

深浅拷贝

浅拷贝

首先浅拷贝和深拷贝只针对引用类型

浅拷贝:拷贝的是地址

常见方法:

  1. 拷贝对象:Object.assgin() / 展开运算符 {…obj} 拷贝对象
const obj = {uname: 'pink',age: 18,family: {baby: '小pink'}}// 浅拷贝// const o = { ...obj }// console.log(o)// o.age = 20// console.log(o)// console.log(obj)const o = {}Object.assign(o, obj)o.age = 20o.family.baby = '老pink'console.log(o)console.log(obj)
  1. 拷贝数组:Array.prototype.concat() 或者 […arr]

如果是简单数据类型拷贝值,引用数据类型拷贝的是地址 (简单理解: 如果是单层对象,没问题,如果有多层就有问题)

深拷贝

首先浅拷贝和深拷贝只针对引用类型

深拷贝:拷贝的是对象,不是地址

常见方法:

  1. 通过递归实现深拷贝
  2. lodash/cloneDeep
  3. 通过JSON.stringify()实现
递归实现深拷贝

函数递归:

如果一个函数在内部可以调用其本身,那么这个函数就是递归函数

  • 简单理解:函数内部自己调用自己, 这个函数就是递归函数
  • 递归函数的作用和循环效果类似
  • 由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件 return
<body><script>const obj = {uname: 'pink',age: 18,hobby: ['乒乓球', '足球'],family: {baby: '小pink'}}const o = {}// 拷贝函数function deepCopy(newObj, oldObj) {debuggerfor (let k in oldObj) {// 处理数组的问题  一定先写数组 在写 对象 不能颠倒if (oldObj[k] instanceof Array) {newObj[k] = []//  newObj[k] 接收 []  hobby//  oldObj[k]   ['乒乓球', '足球']deepCopy(newObj[k], oldObj[k])} else if (oldObj[k] instanceof Object) {newObj[k] = {}deepCopy(newObj[k], oldObj[k])}else {//  k  属性名 uname age    oldObj[k]  属性值  18// newObj[k]  === o.uname  给新对象添加属性newObj[k] = oldObj[k]}}}deepCopy(o, obj) // 函数调用  两个参数 o 新对象  obj 旧对象console.log(o)o.age = 20o.hobby[0] = '篮球'o.family.baby = '老pink'console.log(obj)console.log([1, 23] instanceof Object)// 复习// const obj = {//   uname: 'pink',//   age: 18,//   hobby: ['乒乓球', '足球']// }// function deepCopy({ }, oldObj) {//   // k 属性名  oldObj[k] 属性值//   for (let k in oldObj) {//     // 处理数组的问题   k 变量//     newObj[k] = oldObj[k]//     // o.uname = 'pink'//     // newObj.k  = 'pink'//   }// }</script>
</body>
js库lodash里面cloneDeep内部实现了深拷贝
<body><!-- 先引用 --><script src="./lodash.min.js"></script><script>const obj = {uname: 'pink',age: 18,hobby: ['乒乓球', '足球'],family: {baby: '小pink'}}const o = _.cloneDeep(obj)console.log(o)o.family.baby = '老pink'console.log(obj)</script>
</body>
JSON序列化
<body><script>const obj = {uname: 'pink',age: 18,hobby: ['乒乓球', '足球'],family: {baby: '小pink'}}// 把对象转换为 JSON 字符串// console.log(JSON.stringify(obj))const o = JSON.parse(JSON.stringify(obj))console.log(o)o.family.baby = '123'console.log(obj)</script>
</body>

异常处理

了解 JavaScript 中程序异常处理的方法,提升代码运行的健壮性。

throw

异常处理是指预估代码执行过程中可能发生的错误,然后最大程度的避免错误的发生导致整个程序无法继续运行

总结:

  1. throw 抛出异常信息,程序也会终止执行
  2. throw 后面跟的是错误提示信息
  3. Error 对象配合 throw 使用,能够设置更详细的错误信息
<script>function counter(x, y) {if(!x || !y) {// throw '参数不能为空!';throw new Error('参数不能为空!')}return x + y}counter()
</script>

总结:

  1. throw 抛出异常信息,程序也会终止执行
  2. throw 后面跟的是错误提示信息
  3. Error 对象配合 throw 使用,能够设置更详细的错误信息

try … catch

<script>function foo() {try {// 查找 DOM 节点const p = document.querySelector('.p')p.style.color = 'red'} catch (error) {// try 代码段中执行有错误时,会执行 catch 代码段// 查看错误信息console.log(error.message)// 终止代码继续执行return}finally {alert('执行')}console.log('如果出现错误,我的语句不会执行')}foo()
</script>

总结:

  1. try...catch 用于捕获错误信息
  2. 将预估可能发生错误的代码写在 try 代码段中
  3. 如果 try 代码段中出现错误后,会执行 catch 代码段,并截获到错误信息

debugger

相当于断点调试

处理this

了解函数中 this 在不同场景下的默认值,知道动态指定函数 this 值的方法。

this 是 JavaScript 最具“魅惑”的知识点,不同的应用场合 this 的取值可能会有意想不到的结果,在此我们对以往学习过的关于【 this 默认的取值】情况进行归纳和总结。

普通函数

普通函数的调用方式决定了 this 的值,即【谁调用 this 的值指向谁】,如下代码所示:

<script>// 普通函数function sayHi() {console.log(this)  }// 函数表达式const sayHello = function () {console.log(this)}// 函数的调用方式决定了 this 的值sayHi() // windowwindow.sayHi()// 普通对象const user = {name: '小明',walk: function () {console.log(this)}}// 动态为 user 添加方法user.sayHi = sayHiuesr.sayHello = sayHello// 函数调用方式,决定了 this 的值user.sayHi()user.sayHello()
</script>

注: 普通函数没有明确调用者时 this 值为 window,严格模式下没有调用者时 this 的值为 undefined

箭头函数

箭头函数中的 this 与普通函数完全不同,也不受调用方式的影响,事实上箭头函数中并不存在 this !箭头函数中访问的 this 不过是箭头函数所在作用域的 this 变量。

<script>console.log(this) // 此处为 window// 箭头函数const sayHi = function() {console.log(this) // 该箭头函数中的 this 为函数声明环境中 this 一致}// 普通对象const user = {name: '小明',// 该箭头函数中的 this 为函数声明环境中 this 一致walk: () => {console.log(this)},sleep: function () {let str = 'hello'console.log(this)let fn = () => {console.log(str)console.log(this) // 该箭头函数中的 this 与 sleep 中的 this 一致}// 调用箭头函数fn();}}// 动态添加方法user.sayHi = sayHi// 函数调用user.sayHi()user.sleep()user.walk()
</script>

在开发中【使用箭头函数前需要考虑函数中 this 的值】,事件回调函数使用箭头函数时,this 为全局的 window,因此DOM事件回调函数不推荐使用箭头函数,如下代码所示:

<script>// DOM 节点const btn = document.querySelector('.btn')// 箭头函数 此时 this 指向了 windowbtn.addEventListener('click', () => {console.log(this)})// 普通函数 此时 this 指向了 DOM 对象btn.addEventListener('click', function () {console.log(this)})
</script>

同样由于箭头函数 this 的原因,基于原型的面向对象也不推荐采用箭头函数,如下代码所示:

<script>function Person() {}// 原型对像上添加了箭头函数Person.prototype.walk = () => {console.log('人都要走路...')console.log(this); // window}const p1 = new Person()p1.walk()
</script>

改变this指向

以上归纳了普通函数和箭头函数中关于 this 默认值的情形,不仅如此 JavaScript 中还允许指定函数中 this 的指向,有 3 个方法可以动态指定普通函数中 this 的指向:

call

使用 call 方法调用函数,同时指定函数中 this 的值,使用方法如下代码所示:

<script>// 普通函数function sayHi() {console.log(this);}let user = {name: '小明',age: 18}let student = {name: '小红',age: 16}// 调用函数并指定 this 的值sayHi.call(user); // this 值为 usersayHi.call(student); // this 值为 student// 求和函数function counter(x, y) {return x + y;}// 调用 counter 函数,并传入参数let result = counter.call(null, 5, 10);console.log(result);
</script>

总结:

  1. call 方法能够在调用函数的同时指定 this 的值
  2. 使用 call 方法调用函数时,第1个参数为 this 指定的值
  3. call 方法的其余参数会依次自动传入函数做为函数的参数
apply

使用 call 方法调用函数,同时指定函数中 this 的值,使用方法如下代码所示:

<script>// 普通函数function sayHi() {console.log(this)}let user = {name: '小明',age: 18}let student = {name: '小红',age: 16}// 调用函数并指定 this 的值sayHi.apply(user) // this 值为 usersayHi.apply(student) // this 值为 student// 求和函数function counter(x, y) {return x + y}// 调用 counter 函数,并传入参数let result = counter.apply(null, [5, 10])console.log(result)
</script>

总结:

  1. apply 方法能够在调用函数的同时指定 this 的值
  2. 使用 apply 方法调用函数时,第1个参数为 this 指定的值
  3. apply 方法第2个参数为数组,数组的单元值依次自动传入函数做为函数的参数
bind

bind 方法并不会调用函数,而是创建一个指定了 this 值的新函数,使用方法如下代码所示:

<script>// 普通函数function sayHi() {console.log(this)}let user = {name: '小明',age: 18}// 调用 bind 指定 this 的值let sayHello = sayHi.bind(user);// 调用使用 bind 创建的新函数sayHello()
</script>

注:bind 方法创建新的函数,与原函数的唯一的变化是改变了 this 的值。

防抖节流

  1. 防抖(debounce)
    所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间
  2. 节流(throttle)
    所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数
http://www.lryc.cn/news/309084.html

相关文章:

  • TG-ADMIN 权限管理系统
  • 十五届蓝桥杯第三期模拟赛题单(C++、java、Python)
  • 嵌入式驱动学习第一周——git的使用
  • 界面控件DevExpress .NET MAUI v23.2新版亮点 - 拥有全新的彩色主题
  • 大语言模型LLM Pro+中Pro+(Prompting)的意义
  • React 中,children 属性
  • 多行业万能预约门店小程序源码系统 支持多门店预约小程序 带完整的安装代码包以及搭建教程
  • Node.js 中 fs 模块文件操作的应用教程
  • 一些常用到的git命令
  • spring boot3解决跨域的几种方式
  • 【Spring】19 @Autowired注解使用详解
  • Educational Codeforces Round 132 (Rated for Div. 2) E. XOR Tree(启发式合并+贪心)
  • JavaScript 基本数据类型的详解
  • DDR5内存相比DDR4内存的优势和区别?选择哪一个服务器内存配置能避免丢包和延迟高?
  • 篮球游戏中的挑战精神与怄气心理:扣篮被帽后的再度冲击
  • JavaScript高级程序设计
  • 初阶数据结构:栈与队列
  • Houdini学习笔记
  • 电销机器人识别客户情绪状态
  • AI推介-大语言模型LLMs论文速览(arXiv方向):2024.02.25-2024.03.01
  • Cesium插件系列——3dtiles压平
  • APS面试审核准备的常规问题
  • jvm 基础知识和jvm 调优
  • USB4之ASM2464PD与ASM2464PDX兼容与运用
  • python笔记_进制
  • 面试数据库篇(mysql)- 05什么是聚簇索引什么是非聚簇索引
  • 如何开好一家汽车美容店,汽车美容保养与装饰教学
  • Taro + node.js 注册 仿照java 中的加盐算法
  • 全量知识系统问题及SmartChat给出的答复 之9 三套工具之4语法解析器 之2
  • 简洁版用户登录系统