1.深浅拷贝

  • 浅拷贝:拷贝的是地址

    • 常见方法:Object.assign()
    • 展开运算: {...obj}
    const obj = {
        uname: "浩楠",
        age: 22,
        family: {
            baby: "abc"
        }
    }
    // 浅拷贝
    const o = {};
    Object.assign(o, obj);
    o.age = 20;
    console.log(o.age);  // 20
    console.log(obj.age); // 22
    
    // 第二次属于复杂类型的则无法拷贝,因为只是吧第一层的具体值赋值给了新对象,复杂类型对象的值还是地址
    o.family.baby = "aaa"
    console.log(o.family.baby); // aaa
    console.log(obj.family.baby); // aaa
    

    浅拷贝,如果是单层对象可以,如果有多层则会出现问题

  • 递归:与Java一直

    模拟setInterval

    function getTime(){
        document.querySelector("div").innerHTML = new Date().toLocaleString();
        setTimeout(getTime,1000);
    }
    getTime();
    
  • 深拷贝

    • 常见方法:

      • 通过递归实现
      const obj = {
          uname: 'pink',
          age: 18,
          hobby: ['乒乓球', '足球'],
          family: {
              baby: '小pink'
          }
      }
      const o = {};
      function deepCopy(newObj,oldObj){
          for (let i in oldObj){
              if(oldObj[i] instanceof Array){
                  newObj[i] = [];
                  deepCopy(newObj[i],oldObj[i]);
              }else if(oldObj[i] instanceof Object){
                  newObj[i] = {};
                  deepCopy(newObj[i],oldObj[i]);
              }else {
                  newObj[i] = oldObj[i];
              }
          }
      }
      deepCopy(o,obj);
      // 修改值查看结果
      o.age = 22;
      console.log(o.age); // 22
      console.log(obj.age); // 18
      
      o.family.baby = "aaa";
      console.log(obj.family.baby); // 小pink
      
      • lodash:cloneDeep()
      const obj = {
          uname: 'pink',
          age: 18,
          hobby: ['乒乓球', '足球'],
          family: {
              baby: '小pink'
          }
      }
      const o = _.cloneDeep(obj);
      console.log(o);
      // 修改查看
      o.family.baby = "aaa";
      console.log(obj.family.baby); // 小pink
      
      • 通过JSON.stringify()
      const obj = {
          uname: 'pink',
          age: 18,
          hobby: ['乒乓球', '足球'],
          family: {
              baby: '小pink'
          }
      }
      const o = JSON.parse(JSON.stringify(obj));
      // 修改查看
      obj.family.baby = "aaa";
      console.log(o.family.baby); // 小pink
      

2.异常处理

  • throw 抛出异常

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

image-20230810204459703.png

  • try / catch / finally

    function fn(){
        try{
            // 可能错误的代码
            document.querySelector(".p").style.color = "red";
        }catch(error){
            // 拦截错误,提示浏览器提供的错误信息,但不中断程序的执行
            console.log(error.message);
            // 抛出异常信息
            throw new Error("选择器错误");
            // 程序中断需要加return
            // return 
        }finally{
            // 总会执行的
            console.log("一定会执行的代码");
        }
        console.log(1)
    }
    fn();
    
  • debugger

    写在代码中,浏览器会自动定义到该代码前形成断点

3.改变this指向

  • 函数.call()

    • 语法

      fun.call(this指向,参数1,参数2...);
      
    • 返回值是函数的返回值,因为本意就是调用函数

    const obj = {
        name: "浩楠"
    }
    function fn(x,y){
        console.log(this); // 没有改变this指向的时候为 window
        console.log(x + y);
    }
    // call(调用this指向,arg1,arg2,..):改变指向并调用函数【返回值为函数结果返回值】
    fn.call(obj,1,2);
    
  • apply()

    • 语法
    fun.apply(this指向,[数组参数]);
    
    • 返回值就是函数的返回值
    const obj = {
        name: "浩楠"
    }
    function fn(x,y){
        console.log(this);
        console.log(x + y);
    }
    // 函数.apply(this指向,数组参数):改变this指向并调用函数【返回值与call相同,不同在于参数传递为数组】
    fn.apply(obj,[1,2]);
    
    // 使用场景:求数组最大值
    const arr = [100,44,77];
    console.log("最大值:"+Math.max.apply(null,arr)); // 100
    console.log("最小值:"+Math.min.apply(null,arr)); // 44
    // 类似展开运算符
    console.log(Math.max(...arr));
    
  • bind()

    • 语法
    fun.bind(this指向,参数1,参数2..)
    
    • 返回值由指定的this值和初始化参数改造的 原函数拷贝

    • 当指向改变this指向,并不想调用的时候,使用 bind,比如改变定时器内部的this指向

    const obj = {
        name: "浩楠"
    }
    function fn(){
        console.log(this);
    }
    // 函数.bind(this指向):改变this指向,不会调用函数【返回值是更改过this指向的函数】
    const fun = fn.bind(obj);
    fun();
    
    // 需求:按钮点击就禁用,两秒后开启
    document.querySelector("button").addEventListener("click",function(){
        // 禁用
        this.disabled = true;
        window.setTimeout(function(){
            // 要将指向window的this改为 按钮
            this.disabled = false;
        }.bind(this),2000)
    })
    
  • 总结三个方法

    • 相同点
      • 都可以改变函数内部的 this 指向
    • 区别点
      • call 和 apply 会调用函数,并且改变函数内部this指向
      • call 和 apply 传递的参数不一样
      • bind 不会调用函数,可以改变函数内部this指向
    • 主要应用场景
      • call 调用函数并且可以传递参数
      • apply 经常跟数组有关系,比如借助Math来实现最大值最小值
      • bind 不调用函数,但还是想改变this指向,比如定时器内部的this指向

4.性能优化

  • 防抖

    • 指出发事件后在 n 秒内函数只执行一次,如果在 n 秒内又出发了事件,则会重新计算函数执行事件
    • 场景
      • 假设输入就可以发送请求,但不能每次输入都去发送请求,而是设定一个时间,当输入第一个字符时候,一定的时间后才去发送求情,如果在这个时间内又输入则重新计算这个时间
    • 案例

image-20230810204528951.png

  // 利用防抖实现性能优化
  const box = document.querySelector(".box");
  let i = 0;
  function mouseMove(){
      box.innerHTML = i++;
  }
  // box.addEventListener("mousemove",mouseMove);
  // 500秒后 +1:利用lodash库
  // box.addEventListener("mousemove",_.debounce(mouseMove,500));

  // 手写防抖函数
  function debounce(fun,time){

      let timerId;
      return function(){

          if(timerId){
              clearTimeout(timerId);
          }
          timerId = setTimeout(fun,time)
      }
  }
  box.addEventListener("mousemove",debounce(mouseMove,500));
  • 节流

    • 指连续触发事件但在 n 秒钟只执行一次
    • 场景
      • 轮播图点击、鼠标移动、页面尺寸缩放、滚动条滚动
    • 案例

image-20230810204536704.png

  // 利用节流实现性能优化
  const box = document.querySelector(".box");
  let i = 0;
  function mouseMove(){
      box.innerHTML = i++;
  }
  // box.addEventListener("mousemove",mouseMove);
  // 500秒后 +1:利用lodash库
  // box.addEventListener("mousemove",_.throttle(mouseMove,3000));

  // 手写节流函数
  function throttle(fun,time){
      let timerId = null
      return function(){
          if(!timerId){
              timerId = setTimeout(function(){
                  fun();
                  timerId = null
              },time)
          }
      }
  }
  box.addEventListener("mousemove",throttle(mouseMove,2000));

5.综合案例

image-20230810204601099.png

<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="./js/lodash.min.js"></script>
  <script>
    const video = document.querySelector("video");
    video.ontimeupdate = _.throttle(() => {
      // 获取当前视频时间 video.currentTime
      localStorage.setItem("currentTime",video.currentTime);
      console.log(video.currentTime);
    },1000)
    // 打开页面出发事件
    video.onloadeddata = () => {
      video.currentTime = localStorage.getItem("currentTime") || 0
    }
  </script>
</body>

results matching ""

    No results matching ""