JS高级(三)、深浅拷贝,异常处理,this指向总结,改变this指向;节流和防抖

news2024/12/25 17:02:37

文章目录

  • 一、深浅拷贝
    • 1. 浅拷贝:object.assign;解构赋值
    • 2. 深拷贝:递归函数、lodash的cloneDeep、JSON
  • 二、异常处理
    • 1. throw
    • 2. try catch finally
  • 三. this总结
    • 1、this的指向
    • 2、箭头函数this的指向
    • 3、改变函数this的指向
  • 四. 节流和防抖
    • 1. 防抖(debounce)
    • 2 节流throttle
    • 3 节流案例

一、深浅拷贝

1. 浅拷贝:object.assign;解构赋值

给数据赋值时会存在的问题:

const peppa = {
    name: 'peppa',
    age: 6,
}
// 将peppa赋值给obj,
const obj = peppa
obj.age = 20 // obj修改数据
console.log(peppa.age); //20,peppa的数据也被修改了 

因为引用数据类型在赋值时,赋给的是地址,也就是peppaobj指向同一块内存

解决方法:浅拷贝

  • Object.assign()
  • 解构赋值
const peppa = {
    name: 'peppa',
    age: 6,
}
// 解构赋值
const pig1 = { ...peppa }
pig1.age = 7
console.log(peppa.age); // 6

// Object.assign方法
const pig2 = {}
Object.assign(pig2, peppa)
pig2.name = 'qiaozhi'
console.log(pig2.name); // qiaozhi
console.log(peppa.name); // peppa

问题:如果数据存在多层嵌套,浅拷贝还会出现赋值地址的问题

const peppa = {
    name: 'peppa',
    age: 6,
    family: {
        mother: 'pigMama',
        father: 'pigPapa'
    }
}
// pig1,pig2依旧为上段代码运行结果
pig2.family.mother = 'pig2Mama'
// 此时打印会发现,pig1和peppa里的mother数据都变成了pig2Mama
console.log(pig1);
console.log(peppa);

总结

  1. 浅拷贝在拷贝对象时,里面的属性值是简单数据类型则直接拷贝值
  2. 如果属性值是引用数据类型,则拷贝地址。

2. 深拷贝:递归函数、lodash的cloneDeep、JSON

方式一:通过递归函数实现深拷贝(简版,还有很多没考虑到。了解这种思想就行)

let obj = {
    name: 'tom',
    age: 10,
    color: ['pink', 'skyblue', 'yellow'],
    family: {
        father: 'dad',
        mother: 'mom'
    }
}
function deepCopy (newObj, oldObj) {
    for (const k in oldObj) {
        if (oldObj[k] instanceof Object) {
            newObj[k] = {}
            deepCopy(newObj[k], oldObj[k])
        } else if (oldObj[k] instanceof Array) {
            newObj[k] = []
            deepCopy(newObj[k], oldObj[k])
        } else {
            // k 属性名 oldObj[k] 属性值
            newObj[k] = oldObj[k]
        }
    }
}
// 使用
let newObj = {}
deepCopy(newObj, obj)

方式二:JS库lodash里的cloneDeep实现深拷贝

官网:Lodash 简介 | Lodash中文文档 | Lodash中文网 (lodashjs.com)

// 引入 lodash库
<script src="./js/lodash.min.js"></script>
<script>
    let obj = {
        name: 'tom',
        age: 10,
        color: ['pink', 'skyblue', 'yellow'],
        family: {
            father: 'dad',
            mother: 'mom'
        }
    }
// 实现深拷贝
const newObj2 = _.cloneDeep(obj)
</script>

方式三:JSON方法

let obj = {
    name: 'tom',
    age: 10,
    color: ['pink', 'skyblue', 'yellow'],
    family: {
        father: 'dad',
        mother: 'mom'
    }
}
// 将obj转为字符串,是简单数据类型。然后再转为对象赋给newObj
const newObj = JSON.parse(JSON.stringify(obj))

深浅拷贝只针对引用类型,简单数据类型直接赋值的是值

  • 直接赋值的方法,只要是对象,都会受影响,因为直接赋值获取的是栈里的地址值
  • 浅拷贝如果是一层对象,不会有影响。若出现多层对象拷贝,则仍旧会拷贝地址值
    • Object.assign方法
    • 解构赋值
  • 实现深拷贝的三种方法:
    • 递归函数
    • lodash的cloneDeep函数
    • JSON转换字符串

二、异常处理

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

1. throw

throw:抛出异常信息,

function counter (x, y) {
    if (!x || !y) {
        throw new Error('参数不能为空!')
    }
    return x + y
}
counter()

2. try catch finally

try {
    // 可能会发生错误的代码,要写到try
} catch (err) {
    // 拦截错误,提示浏览器提供的错误信息,但是不中断程序的执行
    throw new Error('xxx错误')
} finally {
    // 无论程序对错,一定会执行的代码
}

三. this总结

1、this的指向

普通函数中,谁调用this,this就指向谁

console.log(this); // window
function fn () {
    console.log('函数里的this', this); // window
}
fn() // window.fn()

setTimeout(function () {
    console.log('延时函数里的this', this); // window
})

document.querySelector('button').addEventListener('click', function () {
    console.log('按钮监听函数内的this');
    console.log(this); // button
})

const obj = {
    name: 'tom',
    sayHi: function () {
        console.log('对象里的函数的this', this);
    }
}
obj.sayHi() // object

在这里插入图片描述

2、箭头函数this的指向

箭头函数中并不存在this,箭头函数中this的值就是最近作用域中的this。
向外层作用域中,一层一层查找this,直到有this的定义
注意情况一:事件回调函数使用箭头函数时,this为全局的window。

 const btn = document.querySelector('.btn')
 btn.addEventListener('click', () => {
     console.log(this); // window,而不是button
 })

注意情况二:对原型对象的操作也不推荐采用箭头函数

function Person () {

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

3、改变函数this的指向

  • call()—了解

使用call方法调用函数,同时指定被调用函数中this的值.

语法:fun.call(thisArg,arg1,arg2)
thisArg:在fun函数运行时指定的this值
arg1,arg2:传递的其他参数

let obj = {
    name: 'tom',
    x: 10,
}
function fun (y) {
    console.log('函数内的this', this);
    // this指向中的x,参数y
    return this.x + y
}
const res = fun.call(obj, 1)
console.log(res); // 11

在这里插入图片描述

  • apply()
    语法:fun.apply(thisArg,[argsArray])
    thisArg:在fun函数运行时指定的this值
    argsArray:传递的值,必须包含在数组里面
function fun2 (y1, y2) {
    return this.x + y1 + y2
}
// obj还是上边那个obj
fun2.apply(obj, [1, 2]) // 13
//    可用在求数组最大值
const arr = [3, 5, 2, 9]
console.log(Math.max.apply(null, arr)); // 利用apply
console.log(Math.max.apply(Math, arr)); // 利用apply
console.log(Math.max(...arr)); // 利用展开运算符
  • bind()

bind方法只会改变this指向,并不会在改变的同时调用这个函数

语法:bind(thisArg,arg1,arg2)
thisArg:在fun函数运行时指定的this值
arg1,arg2:传递的其他参数

**应用:**比如改变定时器内部的this指向

需求 :一个按钮,点击就禁用,两秒之后回复使用

const button = document.querySelector('button')
button.addEventListener('click', function () {
    this.disabled = true, // 这里的this指向的是button
    setTimeout(function () {
        this.disabled = false
    }.bind(button), 2000)
})

// 或者直接
document.querySelector('button').addEventListener('click', function () {
    this.disabled = true,
    setTimeout(function () {
        this.disabled = false
    }.bind(this), 2000)
})

总结

image-20240820161211413.png

四. 节流和防抖

1. 防抖(debounce)

防抖:单位时间内,频繁触发事件,只执行最后一次。
在这里插入图片描述
使用场景:搜索框搜索输入。只需用户最后一次输入完,再发送请求。手机号、邮箱验证输入检测同理

如果每输入一个字就发送一次请求,十分消耗性能
案例:鼠标滑过盒子,盒子上的数字+1

<div>
    <h1>1</h1>
</div>
 <script>
	let i = 0
    function mouseMove () {
      i++
      document.querySelector('h1').innerHTML = i
         // 如果里面存在大量消耗性能的代码,比如Dom操作,数据处理,则可能造成卡顿
    }
    document.querySelector('div').addEventListener('mousemove', mouseMove)
 </script>

在这里插入图片描述
防抖优化: 鼠标停止移动500ms之后开始运行函数
方式一:lodash库实现防抖
语法:_.debounce(fun,时间)

document.querySelector('div').addEventListener('mousemove', _.debounce(mouseMove, 500))

方式二:手写一个防抖函数来处理

底层就是一个定时器
思路:
​ ①:声明一个定时器变量
​ ②:每次执行函数先判断有无定时器,有则清除以前的定时器
​ ③:没有,则开启定时器,并存到变量里
​ ④:在定时器里调用要执行的函数

function debounce (fn, t) {
    let timer = null
    return function () {
        // 有定时器,则清除
        if (timer) clearTimeout(timer)
        timer = setTimeout(function () {
            //调用fn(),就是mouseMove监听事件
            fn()
        }, t,)
    }
}
document.querySelector('div').addEventListener('mousemove', debounce(mouseMove, 500))

在这里插入图片描述

2 节流throttle

节流:单位时间内,频繁触发事件,只执行一次。
在这里插入图片描述

触发一次事件,事件响应需要3秒,若在这3秒内再次触发,则会将当前触发事件响应完之后,再执行下一次事件响应。

还是鼠标划过盒子的案例

节流处理: 鼠标在盒子上移动,不管移动多少次,每隔500ms才+1(也就是500ms内,只执行一次)

方式一:lodash库

document.querySelector('div').addEventListener('mousemove', _.throttle(mouseMove, 500))

方式二:手写一个节流函数,节流的核心就是利用定时器setTimeout来实现

思路:

​ ①:变量定义一个计时器

​ ②:鼠标滑过时判断是否有定时器,有定时器则不开启新定时器

​ ③:没用定时器则开启新定时器并存到变量里面

​ (1) 定时器里调用要执行的函数

​ (2) 在定时器里把定时器清空

function throttle (fn, t) {
    let timer = null
    return function () {
        if (!timer) {
            // 为空则开启新定时器
            timer = setTimeout(function () {
                fn()
                // 清空定时器
                timer = null
            }, t)
        }
    }
}
div.addEventListener('mousemove', throttle(mouseMove, 500))

:为什么不用clearTimeout清除定时器?

因为在定时器里清除定时器本身就是不合理的,所以定时器里是无法删除定时器的,如果执行clearTimeout(timer)会发现,timer仍旧有值。所以使用timer=null来清空定时器。

let timer = null
 timer = setTimeout(function () {
       clearTimeout(timer)
       console.log(timer); // clear清空之后,并不为null,所以要采用赋值操作清空定时器
 }, t)

在这里插入图片描述

3 节流案例

  播放视频,视频播放到一半,刷新界面,继续从上一次播放位置进行播放。需要记录当前的播放时间,timeupdate是当播放位置发生变化就触发。而我们不需要触发的这么频繁,因此采用节流。

<script>
 /*
 timeupdate:事件在视频/音频当前的播放位置发生改变时触发
 loadeddata:事件在当前帧的数据加载完成且还没有足够的数据播放视频或音频的下一帧时触发。
 */

 // 1. 获取元素,对元素进行操作
 const video = document.querySelector('video')
 
 // 1.1 保存
 video.addEventListener('timeupdate', _.throttle(() => {
   // 保存当前的播放事件
   localStorage.setItem('currentTime', video.currentTime)
 }, 1000))

 // 1.2 读取
 video.addEventListener('loadeddata', () => {
   // 获取当前时间,若没有,则默认为0s
   video.currentTime = localStorage.getItem('currentTime') || 0
 })
</script>

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

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

相关文章

【KiCAD安装教程】

【KiCAD安装教程】 KiCAD安装教程1. 访问KiCAD官网2. 选择版本3. 下载镜像4. 运行安装程序5. 开始安装6. 用户类型选择7. 组件选择8. 安装位置9. 安装过程10. 完成安装 KiCAD安装教程 KiCAD是一款开源的电子设计自动化&#xff08;EDA&#xff09;软件套件&#xff0c;主要用于…

Axure RP 9最新安装程序及汉化包下载(支持Win、Mac版,附下载安装教程)

数月前Axure RP官方已经发布了Axure RP 9的消息&#xff0c;并计划在今年夏天发布beta版本。新版Axure RP 9将是该工具向前迈出的重要一步&#xff0c;其中包括一系列广泛的改进&#xff1a;全面的UI修改&#xff0c;新的设计和文档功能以及前所未有的内部优化。我们已经彻底重…

【渗透测试】——Upload靶场实战(1-5关)

&#x1f4d6; 前言&#xff1a;upload-labs是一个使用 php 语言编写的&#xff0c;专门收集渗透测试和CTF中遇到的各种上传漏洞的靶场。旨在帮助大家对上传漏洞有一个全面的了解。目前一共21关&#xff0c;每一关都包含着不同上传方式。 目录 &#x1f552; 0. 安装phpstudy&a…

Modelsim SE-64 2020.4关闭优化

一、问题起源 本人由于之前一直使用AMD的板子&#xff0c;使用vivado自带仿真器进行功能仿真&#xff0c;由于自带的页面简洁和仿真时间自己还都可以接受就没有什么modelsim联合仿真&#xff0c;又因准备FPGA大赛的国产FPGA易灵思的题目&#xff0c;使用Efinity&#xff0b;Mod…

嵌入式开发与应用实验四——通过串口通信实现收发功能

一、实验目的 1. 了解 USART 的基本特性&#xff1b; 2. 掌握STM32串口通信的基本原理&#xff0c;了解异步通信的概念&#xff1b; 3. 掌握用库函数操作 USART 的方法&#xff0c;学习编程实现STM32的USART通信&#xff1b; 4. 掌握如何使用 STM32 的串口发送和接收数据。…

vue part 11

vuex的模块化与namespace 115_尚硅谷Vue技术_vuex模块化namespace_1_哔哩哔哩_bilibili 116_尚硅谷Vue技术_vuex模块化namespace_2_哔哩哔哩_bilibili vue-router路由 很常见的很重要的应用&#xff1a;Ajax请求&#xff0c;将响应的数据替换掉原先的代码从而实现不跳转页面…

监控系列之-Grafana面板展示及制作

一 Grafana设置添加数据源 1、设置Grafana中文显示 最后保存退出&#xff0c;数据源添加完毕 2、导入node_exporter主机监控面板 此处 有外网的情况下&#xff0c;直接输入对应面板的ID号&#xff0c;然后点击加载即可&#xff1b;无无外网的话&#xff0c;则考虑使用上传仪表…

钢材表面缺陷数据集以coco格式做好了数据集的划分,1200张训练集,600张验证集,对应的json文件也在里面

钢材表面缺陷数据集 以coco格式做好了数据集的划分&#xff0c;1200张训练集&#xff0c;600张验证集&#xff0c;对应的json文件也在里面。 钢材表面缺陷检测数据集营销介绍 项目背景&#xff1a; 钢材作为工业生产的重要原材料之一&#xff0c;其表面质量直接影响到成品的性…

基于菜鸟教程的flask学习记录 —— Flask视图函数

文章目录 前言Flask视图函数1.定义视图函数2.接收请求数据&#xff08;1&#xff09;获取URL参数&#xff08;2&#xff09;获取表单数据&#xff08;3&#xff09;获取查询参数 3.返回响应&#xff08;1&#xff09;返回字符串&#xff08;2&#xff09;返回HTML模板&#xff…

机器人自主导航从零开始第四步———Rviz、Gazebo、Meshlab的安装

本文参考资料&#xff1a; rviz - ROS 维基 Gazebo : Tutorial : Ubuntu (gazebosim.org) 零. 什么是Rviz和Gazebo&#xff1a; Rviz是一个三维可视化工具&#xff0c;它利用已有的数据将数据可视化&#xff0c;并提供了可以显示图像、模型、表格、路径等信息的插件&#x…

css百分比布局中height:100%不起作用

百分比布局时&#xff0c;我们有时候会遇到给高度 height 设置百分比后无效的情况&#xff0c;而宽度设置百分比却是正常的。 当为一个元素的高度设定为百分比高度时&#xff0c;是相对于父元素的高度来计算的。当没有给父元素设置高度&#xff08;height&#xff09;时或设置…

浮点数始末详解|科学计数法、浮点数表示、精度有效值、规格化与非规格化

如果直接看浮点数表示法有点费力或者不好理解&#xff0c;不妨复习一下科学计数法。毕竟我们一直接触十进制&#xff0c;从十进制的角度可能更好理解其特性。 目录 科学计数法 Scientific Notation1. 科学计数法的定义2. 精确度与有效数字3. 转换为科学计数法示例&#xff1a;…

商务人士必备的精准翻译工具盘点

网易翻译是一款我外出游玩时候必备的翻译工具&#xff0c;最近没出去玩但是有更多的翻译需求了&#xff0c;为了方便在电脑上的操作我也找了不少翻译工具&#xff0c;这次一起分享给大家&#xff0c;看看哪款更得你的眼缘。 1.福昕在线翻译 链接直达&#xff1a;https://fany…

集群聊天服务器项目【C++】(六)MySql数据库

前面已经介绍了网络模块和业务模块&#xff0c;本章介绍数据模块&#xff0c;同样保持模块解耦的特性&#xff0c;即业务模块不能出现数据模块内容&#xff0c;如出现SQL语句&#xff0c;接下来看看怎么实现的。 1.环境安装 第一章已经介绍了MySql安装&#xff0c;但注意需要…

基于R语言的统计分析基础:使用SQL语句操作数据集

在使用R语言数据分析时&#xff0c;可以融合SQL语言使数据聚集操作更加便利&#xff0c;同时也可以增加对SQL语句的熟悉。借助sqldf、DBI、RSDLite等包&#xff0c;可以在R环境中直接运用SQL语句&#xff0c;轻松实现数据的分组统计、汇总分析&#xff0c;SQL的强大查询能力简化…

MTC完成右臂抓取放置任务\\放置姿态设置

#include "mtc_tutorial/mtc_glass_bottle.hpp" static const rclcpp::Logger LOGGER rclcpp::get_logger("mtc_glass_right"); // 获取节点基础接口的实现 rclcpp::node_interfaces::NodeBaseInterface::SharedPtr MTCTaskNode_Right::getNodeBaseInterf…

(c++)字符串相加(真没想到字符串还有相加运算)

#include<iostream> #include<string> using namespace std;int main() {string ch1 "你好";string ch2 "再见";string ch3 ch1 ch2;cout << ch3 << endl;system("pause");return 0; } 运行结果&#xff1a; 学了c…

FreeRTOS学习——链表list

FreeRTOS学习——链表&#xff08;列表&#xff09;list&#xff0c;仅用于记录自己阅读与学习源码 FreeRTOS Kernel V10.5.1 参考大佬的好文章&#xff1a; freertos内核原理 Day1(链表) FreeRTOS-链表的源码解析 *list_t只能存储指向list_item_t的指针。每个list_item_t都…

【UE5 C++课程系列笔记】01——Visual Studio环境安装

1. 进入Visual Studio 官网&#xff0c;点击下载 下载社区版即可 下载后点击应用程序开始安装 2. 在“工作负荷”中&#xff0c;勾选如下选项 在“单个组件”中&#xff0c;勾选如下选项&#xff1a; 3. 等待下载安装 4. 安装好后&#xff0c;点击“继续但无需代码” 选择“工具…

《python语言程序设计》2018版第8章17题point类设计一个名为point的类

TypeError: point_class.dis_m() missing 1 required positional argument: ‘y2’ 这段代码为什么出错 一个又一个错误 终于摸到点头绪 #distance方法 我做的叫get_dis_m def get_dis_m(self):a_m self.__x1 - self.__x2b_m self.__y1 - self.__y2return (pow(a_m, 2) po…