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

JS高阶 - day04

一、深浅拷贝

深浅拷贝只针对引用类型

1.1 浅拷贝

拷贝对象之后

        如果里面的属性值是简单数据类型直接拷贝值

        如果属性值是引用数据类型则拷贝的是地址

       (多层对象会有问题,第二层即以后就相当于直接赋值)

拷贝对象:Object.assgin() / 展开运算符 {...obj} 拷贝对象

拷贝数组:Array.prototype.concat() 或者 [...arr]

以拷贝对象为例

    // 浅拷贝只对于第一层有用,
    // 缺点:对于对象里面套对象的话,内置对象还是赋的地址,修改新的也是会修改旧的
    // 对单层没问题,对多层就会有问题
    const obj = {
        uname: '温小仙',
        age: 20
    }
    // 直接赋值的形式会导致修改赋值后的对象也会造成源对象的修改

    // 采用浅拷贝的形式
    // 1. 展开运算符
    const o = {...obj}
    console.log(o)
    o.age = 18
    // 改变 0 不会影响 obj
    console.log(o,obj)
    console.log('----------------------------------------')

    // 2. 使用Object.assign()
    const o1 = {}
    Object.assign(o1,obj)
    console.log(o1)
    o1.age  = 1999
    console.log(o1,obj)
    console.log('----------------------------------------')
    // 多层
    const obj2 = {
        uname: '温小仙',
        age: 20,
        game: {
            play: '云顶之奕'
        }
    }
    const o3 = {...obj2}
    console.log(o3)
    o3.game.play = '三国杀'
    console.log(o3,obj2)

1.2 深拷贝

深拷贝:拷贝的是对象。

深拷贝的三种常见方法:

        1. 通过递归实现深拷贝

        2. lodash/cloneDeep实现

        3. 通过JSON.stringify()实现

1.2.1  通过递归

函数递归:

        函数内部自己调用自己, 这个函数就是递归函数

        递归函数的作用和循环效果类似

        由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件return

    const obj = {
        uname: '温小仙',
        age: 20,
        game: {
            play: '云顶之奕'
        },
        hobby:['跑步','听歌']
    }

    //递归的形式进行深拷贝
    const o = {}
    //拷贝函数
    function deepCopy(newObj,oldObj){
        // 遍历对象
        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 {
                //  k是属性名 oldObj[k] 是属性值
                // newObj[k] === o.uname/o.age/o.game
                newObj[k] = oldObj[k]
            }
        }
    }
    deepCopy(o,obj)
    console.log(o)
    o.game.play = '三国杀'
    console.log(o)
    console.log('-------------')
    console.log(obj)

1.2.2 通过Lodash的cloneDeep

js库lodash里面cloneDeep内部实现了深拷贝

<!--从上往下执行,要先引入JS的外部库,lodash-->
<script src="lodash.min.js"></script>
<script>
    const obj = {
        uname: '温小仙',
        age: 20,
        game: {
            play: '云顶之奕'
        },
        hobby:['跑步','听歌']
    }
    // 通过JS外部库lodash的 _.cloneDeep()
    const o = _.cloneDeep(obj)
    console.log(o)
    o.game.play = `三国杀`
    console.log(obj)
</script>

1.2.3 通过JSON.stringify()

    const obj = {
        uname: '温小仙',
        age: 20,
        game: {
            play: '云顶之奕'
        },
        hobby:['跑步','听歌']
    }
    // 利用JSON实现深拷贝
    // 1.把对象转化为JSON字符串
    // 先转为简单数据类型,就和之前的对象没有任何关系了
    //JSON.stringify(obj)
    // 2.在将字符串转化为对象
    const o = JSON.parse(JSON.stringify(obj))
    // 注 这个方法对函数没有用,要注意对象里面的方法

二、异常处理

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

2.1 throw 抛异常

throw抛异常

        throw 抛出异常信息,程序也会终止执行

        throw 后面跟的是错误提示信息

        Error 对象配合throw 使用,能够设置更详细的错误信息

  // throw抛出异常
  const counter = (x,y) => {
    if(!x || !y){
      // throw抛出异常
      throw new Error('参数不能为空!')
    }
    // 抛出异常后中断程序
    return x + y
  }
  counter()

2.2 try /catch 捕获异常

try /catch 捕获异常:

        try...catch 用于捕获错误信息

        将预估可能发生错误的代码写在try代码段中

        如果try代码段中出现错误后,会执行catch代码段,并截获到错误信息

        finally 不管是否有错误,都会执行

// try/catch 捕获异常
  function fn(){
    try {
      // 可能发生错误的代码要写到这里面
      const p = document.querySelector('.p')
      p.style.color = 'red'
    }catch (e) {
      // 拦截错误,只有发生错误之后提示浏览器提供的错误信息  但是本身不会中断程序的执行
      console.log(e.message)
      // throw 来抛出异常 来中断程序,return 也可以
      throw new Error(`${e.message}`)
    }finally {
      // 不管try里面的程序对不对一定会执行的代码
      alert('弹出对话框')
    }
    console.log(1)
  }
  fn()

2.3 debugger

debugger会中断代码的执行,让开发者能够检查当前的执行环境,它类似于在代码中设置了一个断点。

三、处理this

3.1 this指向

对于普通函数:

        非严格模式下 谁调用this的值指向谁

        严格模式下 this指向undefined

对于箭头函数:(箭头函数中并不存在this)

        箭头函数会默认帮我们绑定外层this 的值,

        箭头函数中的this引用的就是最近作用域中的this

        会向外层作用域中,一层一层查找this,直到有this的定义

<body>
<button>点击</button>
<script>
  // 严格模式下,普通函数this指向undefined

  // 在非严格模式下

  // 普通函数 指向调用者
  console.log(this)//window //window.console.log(this)
  function fn(){
    console.log(this)//window
  }
  fn()// window.fn()
  setTimeout(function (){
    console.log(this) // window
  },10000) // window.setTimeout

  document.querySelector('button').addEventListener('click',function (){
    console.log(this) // 指向调用者 button
  })

  const obj = {
    sayHi: function (){
      console.log(this) // 指向调用者 obj
    },
    sayHello: () => console.log(this)//window
  }
  obj.sayHi()

  // 对象作用域没有this,函数作用域中才有  this
  // 箭头函数本没有this,它的this是上一层的this 即调用者的调用者
  obj.sayHello()

  //这里,箭头函数的上一级是对象obj,对象obj的调用者是window,
</script>
</body>

3.2 改变this

3.2.1 call()(了解即可)

call()调用函数,同时指定被调用函数中this的值

语法:

        fun.call(thisArg, arg1, arg2, ...)

                thisArg:在fun 函数运行时指定的this 值

                arg1,arg2:传递的其他参数

                返回值就是函数的返回值,因为它就是调用函数

 <script>
     function fn(x,y){
         console.log(this)// 改变之后就从window 变成了 obj
         console.log(x + y)
     }
     const obj = {
         uname:'温小仙'
     }
     // call  调用函数并改变指向,还可以传进去参数
     fn.call(obj,1,2)
 </script>

3.2.2 apply()(理解)

apply()调用函数,同时指定被调用函数中this的值

语法:

        fun.apply(thisArg, [argsArray])

                thisArg:在fun函数运行时指定的this 值

                argsArray:传递的值,必须包含在数组里面

                返回值就是函数的返回值,因为它就是调用函数

                apply 主要跟数组有关系,比如使用Math.max() 求数组的最大值

<script>
    function fn(x,y){
        // 接收的时候参数要和数组里面的一一对应
        console.log(this)// 改变之后就从window 变成了 obj
        console.log(x + y)
    }
    const obj = {
        uname:'温小仙'
    }
    // apply 调用函数并改变指向,但是第二个参数必须是数组
    // 传的时候参数必须是数组
    fn.apply(obj,[1,2])
    // 返回值,因为其本身就是调用函数,所以返回值就是函数的返回值

    // 使用场景 求数组最大值    第一个参数也可以写null,无影响
    const arr = [1,2,3,4,589,564,7889,4]
    const max = Math.max.apply(Math,arr)
    console.log(max)

    // 展开运算符求数组最大值
    console.log(Math.max(...arr))
</script>

3.2.3 bind()(重点)

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

语法:

        fun.bind(thisArg, arg1, arg2, ...)

                thisArg:在fun 函数运行时指定的this 值

                arg1,arg2:传递的其他参数(可选)

                返回由指定的this 值和初始化参数改造的原函数拷贝(新函数)

                当我们只是想改变this 指向,并且不想调用这个函数的时候,可以使用bind

<body>
<button>发送验证码</button>
<script>
    // bind 方法不会调用函数,但是能改变函数内部this的指向
    // 最重要
    function fn(){
        console.log(this)// 改变之后就从window 变成了 obj
    }
    const obj = {
        uname:'温小仙'
    }
    //改变指向
    // 返回值是个函数,指定this的值和原函数的拷贝(新函数)
    const fun = fn.bind(obj)
    fun()

    // 需求 有一个按钮,点击里面就禁用,2秒钟之后就打开
    const button = document.querySelector('button')
    button.addEventListener('click',function (){
        // 禁用按钮
        this.disabled = true
        // 第一种
        //setTimeout(() => this.disabled = false,2000)
        //第二种写法
        //const time = timeOut.bind(button)
        //setTimeout(time,2000)
        // 第三种
        setTimeout(function (){
            this.disabled = false
        }.bind(button),2000)
        // 还有其他,主要是搞定this的指向
    })
/*    function timeOut(){
        this.disabled = false
    }*/
</script>
</body>

3.2.4 三者对比 

相同点:

        都可以改变函数内部的this指向

不同点:

        call 和apply 会调用函数, 并且改变函数内部this指向

        call 和apply 传递的参数不一样, call 传递参数aru1, aru2..形式 apply 必须数组形式[arg]

        bind 不会调用函数, 可以改变函数内部this指向

主要应用场景

        call 调用函数并且可以传递参数

        apply 经常跟数组有关系. 比如借助于数学对象实现数组最大值最小值

        bind 不调用函数,但是还想改变this指向. 比如改变定时器内部的this指向

四、性能优化

Lodash库里面有节流和防抖的函数

4.1 节流

节流(throttle)就是指连续触发事件但是在n秒中只执行一次函数

使用场景:

        轮播图点击效果 、 鼠标移动、页面尺寸缩放resize、滚动条滚动等都可以加节流

  <body>
  <div class="box"></div>
  <script src="lodash.min.js"></script>
    <!--下面这个是节流-----类似技能的CD-->
<script>
  const box = document.querySelector('.box')
  let i = 1
  function mouseMove(){
    box.innerHTML = `${i++}`
    console.log(new Date().toLocaleString())
  }

  // 利用Lodash库里面的节流函数 _.throttle(function,time)
  //box.addEventListener('mousemove',_.throttle(mouseMove,3000))

  // 自己手写 定义定时器 --间歇函数

  function throttle(fn,t){
    let timeId
    return function (){
      if (!timeId){
        timeId = setInterval(function (){
          fn()
          // 清空定时器
          // 应为在定时器里面无法清除定时器所以不能使用clearTimeout(timeId),应该使用timeId = null
          timeId = null
        },t)
      }
    }

  }
  box.addEventListener('mousemove',throttle(mouseMove,3000))
</script>
</body>

4.2 防抖

防抖(debounce)就是指触发事件后在n秒内函数只能执行一次,如果在n秒内又触发了事件,则会重新计算函数执行时间

使用场景:搜索框防抖等

<body>
  <div class="box"></div>
  <script src="lodash.min.js"></script>
  <!--下面这个是防抖-----类似回城-->
  <script>
    // 利用防抖实现性能 优化
    // 需求,鼠标在盒子上移动,里面的数组就会变化 + 1
    const box = document.querySelector('.box')
    let i = 1
    function mouseMove(){
      box.innerHTML = `${i++}`
    }

    // 我自己的想法
    // 利用定时器setTimeout写 防抖处理
/*    let timeId
    function mouseMove(){
      if(timeId) clearTimeout(timeId)
      timeId = setTimeout(function (){
        box.innerHTML = `${i++}`
      },500)
   box.addEventListener('mousemove',mouseMove)
    }*/

    // Lodash的防抖函数
   // _.debounce(mouseMove(),500,true)  // 会返回一个新的函数
    //box.addEventListener('mousemove',_.debounce(mouseMove,500,true))

    // Pink老师,利用定时器setTimeout写 防抖处理
    function debounce(fn, t) {
      let timeId
      return function() {
        if (timeId) clearTimeout(timeId)
        timeId = setTimeout(fn, t)
      }
    }
    box.addEventListener('mousemove',debounce(mouseMove,500))
  </script>
</body>


http://www.kler.cn/a/521283.html

相关文章:

  • 穿心莲内酯(andrographolide)生物合成CYP72-文献精读106
  • OpenBMC:编译
  • ArcGIS10.2 许可License点击始终启动无响应的解决办法及正常启动的前提
  • 基于物联网的智能环境监测系统(论文+源码)
  • 【愚公系列】《循序渐进Vue.js 3.x前端开发实践》022-定义组件
  • 【Super Tilemap Editor使用详解】(十六):高级主题:深入理解 Super Tilemap Editor
  • ubuntu取消定时锁定
  • 学院失物招领 app 的设计与实现
  • 计算机图形学实验练习(实验1.2-4.1AND补充实验12)
  • 【阅读笔记】基于整数+分数微分的清晰度评价算子
  • 数据的秘密:如何用大数据分析挖掘商业价值
  • Ubuntu 24.04 安装 NVIDIA Container Toolkit 全指南:让Docker拥抱GPU
  • for...in 和 Object.keys().forEach的区别
  • GO语言 链表(单向链表
  • 接口管理文档Yapi的安装与配置
  • 华硕笔记本装win10哪个版本好用分析_华硕笔记本装win10专业版图文教程
  • 无所不搜,吾爱制造
  • 深入 Rollup:从入门到精通(一)专栏介绍
  • 【Leetcode 热题 100】139. 单词拆分
  • Autogen_core: Quickstart
  • vulnhub靶场【kioptrix-2】靶机
  • 如何使用tushare pro获取股票数据——附爬虫代码以及tushare积分获取方式
  • Excel分区间统计分析(等步长、不等步长、多维度)
  • 瑞芯微方案:RV1126定制开发板方案定制
  • 【Elasticsearch 基础入门】Centos7下Elasticsearch 7.x安装与配置(单机)
  • 论文阅读(十六):利用线性链条件随机场模型检测阵列比较基因组杂交数据的拷贝数变异