JavaScript进阶day4

目录

1.深浅拷贝

1.1 浅拷贝

1.1.1 浅拷贝的认识

1.1.2 浅拷贝的小结

1.2 深拷贝

1.2.1 递归实现深拷贝

1.2.2 js类库lodash/cloneDeep实现深拷贝

1.2.3 JSON.stringify()实现深拷贝

2.异常处理

2.1 throw 抛异常

2.2 try /catch 捕获异常

2.3 debugger

3.处理this

3.1 this指向

3.1.1 普通函数

3.1.2 箭头函数

3.2 改变this

3.2.1 call()

3.2.2 apply()

3.2.3 bind()--重点

4.性能优化

4.1 防抖

4.2 节流

4.3 总结

5.节流的综合案例


1.深浅拷贝

开发中我们经常需要复制一个对象。如果直接用赋值会有下面问题:

因为对象赋值是地址,因此修改其中一个,另外一个也会受到影响。

1.1 浅拷贝

1.1.1 浅拷贝的认识

浅拷贝:拷贝的是地址
常见方法:
  • 1. 拷贝对象:Object.assgin() / 展开运算符 {...obj} 拷贝对象
  • 2.拷贝数组:Array.prototype.concat() 或者 [...arr]

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

1.1.2 浅拷贝的小结

1. 直接赋值和浅拷贝有什么区别?
  • 直接赋值的方法,只要是对象,都会相互影响,因为是直接拷贝对象栈里面的地址
  • 浅拷贝如果是一层对象,不相互影响,如果出现多层对象拷贝还会相互影响
2. 浅拷贝怎么理解?
  • 拷贝对象之后,里面的属性值是简单数据类型直接拷贝值
  • 如果属性值是引用数据类型则拷贝的是地址

1.2 深拷贝

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

常见方法:
  • 1. 通过递归实现深拷贝
  • 2. lodash/cloneDeep
  • 3. 通过JSON.stringify()实现

1.2.1 递归实现深拷贝

函数递归:
  • 如果一个函数在内部可以调用其本身,那么这个函数就是递归函数
  • 简单理解:函数内部自己调用自己, 这个函数就是递归函数
  • 递归函数的作用和循环效果类似
  • 由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件 return

练习:

利用递归函数实现 setTimeout 模拟 setInterval效果

需求:
①:页面每隔一秒输出当前的时间
②:输出当前时间可以使用:new Date().toLocaleString()
代码:
递归实现深拷贝:
实现步骤:
  • 通过递归函数来实现
  • 如果是基本数据类型的键值对,就正常赋值
  • 如果是引用类型就需要递归函数,先处理数组后处理对象

1.2.2 js类库lodash/cloneDeep实现深拷贝

访问:https://www.lodashjs.com/

然后下载并引入js文件,才可以使用封装好的cloneDeep()

1.2.3 JSON.stringify()实现深拷贝

怎么做到的?

刚开始obj是一个对象,然后转换为JSON字符串,后面有parse重新转为对象,又因为前面的对象和后面的对象的地址不一样,因此可以实现深拷贝。

2.异常处理

2.1 throw 抛异常

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

总结:
  • 1. throw 抛出异常信息,程序也会终止执行
  • 2. throw 后面跟的是错误提示信息
  • 3. Error 对象配合 throw 使用,能够设置更详细的错误信息(会指出第几行代码出错)

2.2 try /catch 捕获异常

我们可以通过try / catch 捕获错误信息(浏览器提供的错误信息) ,try 试试 catch 拦住 finally 最后。

总结:
  • 1. try...catch 用于捕获错误信息
  • 2. 将预估可能发生错误的代码写在 try 代码段中
  • 3. 如果 try 代码段中出现错误后,会执行 catch 代码段,并截获到错误信息
  • 4. finally 不管是否有错误,都会执行
  • 5. catch捕获到错误信息,程序不会停止

2.3 debugger

当代码多的时候,不方便找到对应的片段,就可以加debugger,浏览器控制台就会定位到

该debugger,省去了找代码的时间。

3.处理this

3.1 this指向

学习路径:
  • 1. 普通函数this指向
  • 2. 箭头函数this指向

3.1.1 普通函数

普通函数的调用方式决定了 this 的值,即【谁调用 this 的值指向谁】
普通函数没有明确调用者时 this 值为 window,严格模式下没有调用者时 this 的值为 undefined

3.1.2 箭头函数

箭头函数中的 this 与普通函数完全不同,也不受调用方式的影响,事实上 箭头函数中并不存在 this 
  • 1. 箭头函数会默认帮我们绑定外层 this 的值,所以在箭头函数中 this 的值和外层的 this 是一样的
  • 2.箭头函数中的this引用的就是最近作用域中的this
  • 3.向外层作用域中,一层一层查找this,直到有this的定义

注意情况1:
在开发中【使用箭头函数前需要考虑函数中 this 的值】,事件回调函数使用箭头函数时,this 为全局的 window ,因此DOM事件回调函数 如果里面需要 DOM对象的this,则不推荐使用箭头函数

注意情况2:
同样由于箭头函数 this 的原因,基于原型的面向对象也不推荐采用箭头函数

总结:

1.函数内不存在this,沿用上一级的,过程:向外层作用域中,一层
一层查找this,直到有this的定义
2.不适用
  •  构造函数
  • 原型函数
  • dom事件函数
3. 适用 :需要使用上层this的地方

3.2 改变this

3.2.1 call()

作用:使用 call 方法调用函数,同时传入需要被指定的this对象,来实现改变函数的this对象

语法:

参数说明:

  • thisArg:在 fun 函数运行时指定的 this 值
  • arg1,arg2:传递的其他参数
  • 返回值就是函数的返回值,因为它就是调用函数

3.2.2 apply()

作用:使用 apply 方法调用函数,同时指定被调用函数中 this 的值

语法:

参数说明:

  • thisArg:在fun函数运行时指定的 this 值
  • argsArray:传递的值,必须包含在数组里面
  • 返回值就是函数的返回值,因为它就是调用函数
使用场景:apply 主要跟数组有关系,比如使用 Math.max() 求数组的最大值。

3.2.3 bind()--重点

作用:bind() 方法不会调用函数。但是能改变函数内部this 指向

语法:

参数说明:

  • thisArg:在 fun 函数运行时指定的 this 值
  • arg1,arg2:传递的其他参数
  • 返回由指定的 this 值和初始化参数改造的 原函数拷贝 (拷贝原函数,并且改变原函数的this指向)
使用场景:当我们只是想改变 this 指向,并且不想调用这个函数的时候,可以使用 bind, 比如改变定时器内部的 this指向。

4.性能优化

4.1 防抖

防抖(debounce),所谓防抖,就是单位时间内,频繁触发事件,只执行最后一次。
举个例子:王者荣耀回城,只要被打断回城,就需要重新来按回城。
开发使用场景- 搜索框防抖
搜索框搜索输入,需要用户最后一次输入完,再发送请求。
案例:
利用防抖来处理-鼠标滑过盒子显示文字
实现方式:
  • 1.lodash提供的防抖函数来处理
  • 2.手写一个防抖函数来处理

需求: 鼠标在盒子上移动,鼠标停止之后,500ms后里面的数字就会变化+1
核心思路:  

①: 写一个demo函数 ,来控制这个操作函数(mouseMove), 500ms之后才去执行这个函数

②: 节流函数传递2个参数, 第一个参数 mouseMove函数,第二个参数 指定时间500ms

③: 鼠标移动事件,里面写的是demo函数

④:定时器有就先清除,后再添加新的定时器

代码:
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
  <style>
    .box {
      width: 500px;
      height: 500px;
      background-color: #ccc;
      color: #fff;
      text-align: center;
      font-size: 100px;
    }
  </style>
</head>

<body>
  <div class="box"></div>
  <script>
   
     /* 手写防抖函数 */

    // 获取对象
    const box = document.querySelector('.box')

    let i = 1
    // 写入html文档
    function mouseMove() {
      box.innerHTML = i++
    }

    // 鼠标移动事件,因为 demo(mouseMove, 500)返回的是一个回调函数,因此没问题
    box.addEventListener('mousemove', demo(mouseMove, 500))

    //     核心思路:
    // ①: 写一个demo函数 ,来控制这个操作函数(mouseMove), 500ms之后才去执行这个函数
    // ②: 节流函数传递2个参数, 第一个参数 mouseMove函数,第二个参数 指定时间500ms
    // ③: 鼠标移动事件,里面写的是demo函数
    function demo(fun, time) {
      let timeOut
      return function () {
        // 定时器有就先删除,后再添加新的定时器
        if (timeOut) {
          clearTimeout(timeOut)
        }
        timeOut = setTimeout(fun, time)
      }
    }

  </script>
</body>

</html>

4.2 节流

节流(throttle),所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。
举个例子:王者荣耀技能冷却,期间无法继续使用技能。
开发使用场景 – 小米 轮播图点击效果 、 鼠标移动、页面尺寸缩放resize就可以加节流。
假如一张轮播图完成切换需要300ms, 不加节流效果,快速点击,则嗖嗖嗖的切换。
加上节流效果, 不管快速点击多少次, 300ms时间内,只能切换一张图片。
高级软件人才培训专家

 

案例:
利用防抖来处理-鼠标滑过盒子显示文字
实现方式:
  • 1.lodash提供的节流函数来处理
  • 2.手写一个节流函数来处理

需求: 鼠标在盒子上移动,鼠标停止之后,500ms后里面的数字就会变化+1
核心思路:  

①: 写一个throttle函数 ,来控制这个操作函数(mouseMove), 500ms之后才去执行这个函数

②: 节流函数传递2个参数, 第一个参数 mouseMove函数,第二个参数 指定时间500ms

③: 鼠标移动事件,里面写的是throttle函数

④:如果没有定时器就开启,如果有定时器则不开启新的定时器

        //   1.1 定时器里面调用函数

        //   1.2 定时器里面清除该定时器

代码:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
  <style>
    .box {
      width: 500px;
      height: 500px;
      background-color: #ccc;
      color: #fff;
      text-align: center;
      font-size: 100px;
    }
  </style>
</head>

<body>
  <div class="box"></div>
  <script>

    /* 手写节流函数 */

    // 获取对象
    const box = document.querySelector('.box')

    let i = 1
    // 写入html文档
    function mouseMove() {
      box.innerHTML = i++
    }

    // 鼠标移动事件
    box.addEventListener('mousemove', throttle(mouseMove, 500))

    //     核心思路:
    // ①: 写一个throttle函数 ,来控制这个操作函数(mouseMove), 500ms之后才去执行这个函数
    // ②: 节流函数传递2个参数, 第一个参数 mouseMove函数,第二个参数 指定时间500ms
    // ③: 鼠标移动事件,里面写的是throttle函数
    function throttle(fun, time) {
      let timeOut = null
      return function () {
        // 1.如果没有定时器就开启,如果有定时器则不开启新的定时器
        //   1.1 定时器里面调用函数
        //   1.2 清除定时器
        if (!timeOut) {
          timeOut = setTimeout(function () {
            // 调用要执行的函数
            fun()
            // 开启定时器里面清理定时器
            // 设置null的原因是因为开启定时器里面直接clearTimeout会出现异常情况,因此直接把变量赋值为null
            // 异常情况就是开启定时器是timeOut = 1,在里面调用了clearTimeout的timeOut还是为1
            timeOut = null
          }, time)
        }

      }
    }

  </script>
</body>

</html>

注意:setTimeout中无法删除定时器,因为定时器还在运作,因此用timeOut = null,而不是

clearTimeout(timeOut)

4.3 总结

Lodash 库 实现节流和防抖

1. 节流和防抖的区别是?
  • 节流: 就是指连续触发事件但是在 n 秒中只执行一次函数,比如 可以利用节流实现 1s之内 只能触发一次鼠标移动事件
  • 防抖:如果在 n 秒内又触发了事件,则会重新计算函数执行时间
2. 节流和防抖的使用场景是?
  • 节流: 鼠标移动,页面尺寸发生变化,滚动条滚动等开销比较 大的情况下
  • 防抖: 搜索框输入,设定每次输入完毕n秒后发送请求,如果期 间还有输入,则从新计算时间

5.节流的综合案例

页面打开,可以记录上一次的视频播放位置

先认识两个事件:
  • ①:ontimeupdate 事件在视频/音频(audio/video)当前的播放位置发送改变时触发
  • ②:onloadeddata 事件,每次打开页面,加载当前帧的数据
  • ③:利用本地存储视频播放时间,下次打开页面就可以重本地存储获取该时间来实现

思路:
  • 1. 在ontimeupdate事件触发的时候,每隔1秒钟,就记录当前时间到本地存储
  • 2. 下次打开页面, onloadeddata 事件触发,就可以从本地存储取出时间,让视频从取出的时间播放,如果没有就默认为0s
  • 3. 获得当前时间 video.currentTime
效果图:
代码:
<body>
  <div class="container">
    <div class="header">
      <a href="http://pip.itcast.cn">
        <img src="https://pip.itcast.cn/img/logo_v3.29b9ba72.png" alt="" />
      </a>
    </div>
    <div class="video">
      <video src="https://v.itheima.net/LapADhV6.mp4" controls></video>
    </div>
    <div class="elevator">
      <a href="javascript:;" data-ref="video">视频介绍</a>
      <a href="javascript:;" data-ref="intro">课程简介</a>
      <a href="javascript:;" data-ref="outline">评论列表</a>
    </div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
  <script>
    // 获取对象
    const video = document.querySelector('video')
    // 播放视频就会触发(利用节流让它一秒只触发一次)
    video.ontimeupdate = _.throttle(function () {
      // console.log(11)
      localStorage.setItem('currentTime', video.currentTime)
    }, 1000)

    // 下次打开页面,视频的进度停留在 video.currentTime
    video.onloadeddata = function () {
      video.currentTime = localStorage.getItem('currentTime') || 0
    }
  </script>
</body>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值