Pink老师JS进阶DAY4学习记录-深浅拷贝,异常处理,梳理this,性能优化

Pink老师JS进阶DAY4学习记录-深浅拷贝,异常处理,梳理this,性能优化

一. 深浅拷贝(复制对象)
1. 普通复制

会改变原对象的属性,因为直接复制是复制的的对象的地址,改变的是同一坨变量。

    const obj = {
      uname: 'pink',
      age: 18
    }
    const o = obj
    console.log(o)
    o.age = 20
    console.log(o)
    console.log(obj) // ...obj.age = 20
2. 浅拷贝

可以解决上述问题,不会改变原对象的普通数据类型数据,但是仍然会改变原对象的引用数据类型,也是因为复制的是地址。

    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) // 第二种,Object.assign()
    o.age = 20
    o.family.baby = '老pink'
    console.log(o)
    console.log(obj) // 发现原对象的family也被改了
    
3. 深拷贝

实现深拷贝有三种方式

  1. 递归
    const obj = {
      uname: 'pink',
      age: 18,
      hobby: ['乒乓球', '足球'],
      family: {
        baby: '小pink'
      }
    }
    const o = {}
    // 拷贝函数
    function deepCopy(newObj, oldObj) {
      debugger
      for (let k in oldObj) {
        // 处理数组的问题  一定先写数组 在写 对象 不能颠倒
        if (oldObj[k] instanceof Array) {
          newObj[k] = []
          deepCopy(newObj[k], oldObj[k])
        } else if (oldObj[k] instanceof Object) {
          newObj[k] = {}
          deepCopy(newObj[k], oldObj[k])
        }
        else {
          // newObj[k]  === o.uname  给新对象添加属性
          newObj[k] = oldObj[k]
        }
      }
    }
    deepCopy(o, obj) // 函数调用  两个参数 o 新对象  obj 旧对象
    console.log(o)
    o.age = 20
    o.hobby[0] = '篮球'
    o.family.baby = '老pink'
    console.log(obj)

这里就是在普通的浅拷贝的基础上加上遇到 数组 和 对象 两种情况的处理方式,也就是传入空数组/对象,原对象属性值(数组,对象)进去递归,需要注意数组一定要在对象前面,因为Array instanceof Object为true。

  1. lodash
  // 需要先引入库
  <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>
  1. JSON.stringfy
    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)
二.异常处理
1. throw抛异常

会终止程序

    function fn(x, y) {
      if (!x || !y) {
        // throw '没有参数传递进来'
        throw new Error('没有参数传递过来')
      }

      return x + y
    }
    console.log(fn())
2. try / catch 捕获异常
      try {
        // 可能发送错误的代码 要写到 try
        const p = document.querySelector('.p')
        p.style.color = 'red'
      } catch (err) {
        // 拦截错误,提示浏览器提供的错误信息,但是不中断程序的执行
        console.log(err.message)
        throw new Error('你看看,选择器错误了吧')
      }
      finally {
        // 不管你程序对不对,一定会执行的代码
        alert('弹出对话框')
      }
      console.log(11)
    }
    fn()
3. debugger

关键字,就是相当于一个断点,方便代码多的时候debug

三. 梳理this
1. this指向

普通函数的this指向它的调用者
箭头函数没有this,默认为外层this
所以DOM事件,原型函数,构造函数不要使用箭头函数

2. 改变this指向

有三种方法。call(了解),apply(掌握),bind(掌握)

  1. call
    const obj = {
      uname: 'pink'
    }
    function fn(x, y) {
      console.log(this) // window
      console.log(x + y)
    }
    // 1. 调用函数  
    // 2. 改变 this 指向
    fn.call(obj, 1, 2) // 直接传入参数
  1. apply
    const obj = {
      age: 18
    }
    function fn(x, y) {
      console.log(this) // {age: 18}
      console.log(x + y)
    }
    //  fn.apply(this指向谁, 数组参数)
    fn.apply(obj, [1, 2]) // 传入参数必须为数组的形式

运用场景:求数组最大最小值

    const arr = [100, 44, 77]
    const max = Math.max.apply(Math, arr) // 100
    const min = Math.min.apply(null, arr) // 44
    const m = Math.max(...arr) // 之前学的方法
  1. bind
    bind 不会调用函数
    能改变this指向
    返回值是个函数
const fun = fn.bind(obj) // 改变fn的this指向并赋值给fun

使用场景

    // 需求,有一个按钮,点击里面就禁用,2秒钟之后开启
    document.querySelector('button').addEventListener('click', function () {
      // 禁用按钮
      this.disabled = true
      window.setTimeout(function () {
        // 在这个普通函数里面,我们要this由原来的window 改为 btn
        this.disabled = false
      }.bind(this), 2000)   // 这里的this 和 btn 一样
    })

改变setTimeout的回调函数的this为事件源

四. 性能优化
1. 防抖

在这里插入图片描述

单位时间内,频繁触发事件,只执行最后一次
使用场景:

  1. 搜索框搜索输入。只需用户最后一次输入完再发送请求。
  2. 手机号、邮箱验证输入检测
2. 节流

在这里插入图片描述

单位时间内。频繁触发事件,只执行一次
使用场景:高频事件:mousemove, resize, scroll等等。

3.案例(鼠标滑动盒子内数+1)

防抖: 鼠标移动结束500ms后,数字才会发生变化

  1. lodash
    const box = document.querySelector('.box')
    let i = 1  // 让这个变量++
    // 鼠标移动函数
    function mouseMove() {
      box.innerHTML = ++i
      // 如果里面存在大量操作 dom 的情况,可能会卡顿
    }
    box.addEventListener('mousemove', _.debounce(mouseMove, 500))
  1. 手写
    const box = document.querySelector('.box')
    let i = 1  // 让这个变量++
    // 鼠标移动函数
    function mouseMove() {
      box.innerHTML = ++i
      // 如果里面存在大量操作 dom 的情况,可能会卡顿
    }
    // 防抖函数
    function debounce(fn, t) {
      let timeId
      return function () {
        // 如果有定时器就清除
        if (timeId) clearTimeout(timeId)
        // 开启定时器 200
        timeId = setTimeout(function () {
          fn()
        }, t)
      }
    }
    // box.addEventListener('mousemove', mouseMove)
    box.addEventListener('mousemove', debounce(mouseMove, 200))

节流: 鼠标在盒子上移动,不管移动多少次,每隔500ms才加1

  1. lodash
box.addEventListener('mousemove', _.throttle(mouseMove, 500))
  1. 手写
    const box = document.querySelector('.box')
    let i = 1  // 让这个变量++
    // 鼠标移动函数
    function mouseMove() {
      box.innerHTML = ++i
      // 如果里面存在大量操作 dom 的情况,可能会卡顿
    }
    // console.log(mouseMove)
    // 节流函数 throttle 
    function throttle(fn, t) {
      // 起始时间
      let startTime = 0
      return function () {
        // 得到当前的时间
        let now = Date.now()
        // 判断如果大于等于 500 采取调用函数
        if (now - startTime >= t) {
          // 调用函数
          fn()
          // 起始的时间 = 现在的时间   写在调用函数的下面 
          startTime = now
        }
      }
    }
    box.addEventListener('mousemove', throttle(mouseMove, 500))
4. 节流综合案例

因为节流之后会用得比较多,这里写一个综合案例
需求: 页面一打开,记录上一次播放位置
知识储备: 这里会用到ontimeupdateonloadeddata两个事件,前者是在播放位置改变时触发,后者是打开页面时触发(在当前帧的数据加载完成且还没有足够的数据播放视频/音频的下一帧时触发)
思路:

  1. ontimeupdate事件触发的时候,每隔1秒钟,就记录当前时间到本地存储(因为ontimeupdate触发的频次太高,这里用到节流)

  2. 下次打开页面,onloadeddata 事件触发,就可以从本地存储取出时间,让视频从取出的时间播放,如果没有就默认为0s

  3. 获得当前时间 video.currentTime

代码:

    // 1. 获取元素  要对视频进行操作
    const video = document.querySelector('video')
    video.ontimeupdate = _.throttle(() => {
      // console.log(video.currentTime) 获得当前的视频时间
      // 2. 把当前的时间存储到本地存储
      localStorage.setItem('currentTime', video.currentTime)
    }, 1000)

    // 3. 打开页面触发事件,就从本地存储里面取出记录的时间, 赋值给  video.currentTime
    video.onloadeddata = () => {
      // console.log(111)
      video.currentTime = localStorage.getItem('currentTime') || 0
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值