JS进阶学习04

一、深浅拷贝

1.浅拷贝

首先浅拷贝和深拷贝只针对引用类型

浅拷贝:拷贝的是地址

常见方法:

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

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

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

html 复制代码
<!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>
  </head>

  <body>
    <script>
      const obj = {
        uname: "pink",
        age: 18,
        family: {
          baby: "小pink",
        },
      };
      // 浅拷贝
      // const o = { ...obj };
      // console.log(o); //Object { uname: "pink", age: 18, family: {...} }
      // o.age = 20;
      // console.log(o); //Object { uname: "pink", age: 20, family: {...} }
      // console.log(obj); //Object { uname: "pink", age: 18, family: {...} }

      const o = {};
      Object.assign(o, obj);
      o.age = 20;
      o.family.baby = "老pink";
      console.log(o); //Object { uname: "pink", age: 20, family: {...} }
      console.log(obj); //Object { uname: "pink", age: 18, family: {...} }
    </script>
  </body>
</html>

2.深拷贝

首先浅拷贝和深拷贝只针对引用类型
深拷贝:拷贝的是对象,不是地址
常见方法:

  1. 通过递归实现深拷贝
  2. lodash/cloneDeep
  3. 通过JSON.stringify()实现

(1)函数递归

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

html 复制代码
<!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>
</head>

<body>
  <script>
    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] = []
          //  newObj[k] 接收 []  hobby
          //  oldObj[k]   ['乒乓球', '足球']
          deepCopy(newObj[k], oldObj[k])
        } else if (oldObj[k] instanceof Object) {
          newObj[k] = {}
          deepCopy(newObj[k], oldObj[k])
        }
        else {
          //  k  属性名 uname age    oldObj[k]  属性值  18
          // 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)
    console.log([1, 23] instanceof Object)
    // 复习
    // const obj = {
    //   uname: 'pink',
    //   age: 18,
    //   hobby: ['乒乓球', '足球']
    // }
    // function deepCopy({ }, oldObj) {
    //   // k 属性名  oldObj[k] 属性值
    //   for (let k in oldObj) {
    //     // 处理数组的问题   k 变量
    //     newObj[k] = oldObj[k]
    //     // o.uname = 'pink'
    //     // newObj.k  = 'pink'
    //   }
    // }
  </script>
</body>

</html>

(2)lodash

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

html 复制代码
<!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>
  </head>

  <body>
    <!-- 先引用 -->
    <script src="./lodash.min.js"></script>
    <script>
      const obj = {
        uname: "pink",
        age: 18,
        hobby: ["乒乓球", "足球"],
        family: {
          baby: "小pink",
        },
      };
      const o = _.cloneDeep(obj);
      console.log(o); //Object { uname: "pink", age: 18, hobby: (2) [...], family: Object { baby: "老pink" } }
      o.family.baby = "老pink";
      console.log(obj); //Object { uname: "pink", age: 18, hobby: (2) [...], family: Object { baby: "小pink" } }
    </script>
  </body>
</html>

(3)JSON.stringify()

javascript 复制代码
<!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>
  </head>

  <body>
    <script>
      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); //Object { uname: "pink", age: 18, hobby: Array [ "乒乓球", "足球" ], family: {Object { baby: "123" }} }
      o.family.baby = "123";
      console.log(obj); //Object { uname: "pink", age: 18, hobby: Array [ "乒乓球", "足球" ], family: Object { baby: "小pink" } }
    </script>
  </body>
</html>

二、异常处理

1.throw抛异常

2.try/catch捕获异常

3.debugger

相当于断点调试

三、处理this

1.this指向

(1)普通函数

普通函数的调用方式决定了 this 的值,即【谁调用 this 的值指向谁】

html 复制代码
<!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>
</head>

<body>
  <button>点击</button>
  <script>
    // 普通函数:  谁调用我,this就指向谁
    console.log(this)  // window
    function fn() {
      console.log(this)  // window    
    }
    window.fn()
    window.setTimeout(function () {
      console.log(this) // window 
    }, 1000)
    document.querySelector('button').addEventListener('click', function () {
      console.log(this)  // 指向 button
    })
    const obj = {
      sayHi: function () {
        console.log(this)  // 指向 obj
      }
    }
    obj.sayHi()
  </script>
</body>

</html>

(2)箭头函数

html 复制代码
<script>
  console.log(this); // 此处为 window
  // 普通函数
  const sayHi = function () {
    console.log(this);
  };
  // 普通对象
  const user = {
    name: "小明",
    // 该箭头函数中的 this 为函数声明环境中 this 一致
    walk: () => {
      console.log(this);
    },

    sleep: function () {
      let str = "hello";
      console.log(this);
      let fn = () => {
        console.log(str);
        console.log(this); // 该箭头函数中的 this 与 sleep 中的 this 一致
      };
      // 调用箭头函数
      fn();
    },
  };

  // 动态添加方法
  user.sayHi = sayHi;

  // 函数调用
  // user.sayHi();
  // this指的是user
  // Object { name: "小明", walk: walk(), sleep: sleep(), sayHi: sayHi() }
  // user.sleep();
  // this指的是user(普通函数)
  // Object { name: "小明", walk: walk(), sleep: sleep(), sayHi: sayHi() }
  // hello
  // Object { name: "小明", walk: walk(), sleep: sleep(), sayHi: sayHi() }

  user.walk();
  // this指的是window(箭头函数)
  // Window
  // Window
</script>

html 复制代码
<!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>
  </head>

  <body>
    <button class="btn">点击</button>
    <script></script>
  </body>
  <script>
    // DOM 节点
    const btn = document.querySelector(".btn");
    // 箭头函数 此时 this 指向了 window
    btn.addEventListener("click", () => {
      console.log(this); //Window
    });
    // 普通函数 此时 this 指向了 DOM 对象
    btn.addEventListener("click", function () {
      console.log(this);//<button class="btn">
    });
  </script>
</html>

2.改变this

(1)call()- this指向传入的参数

(2)apply()-传入必须是数组,this指向数组

(3)bind()-不会调用函数,但改变this

(4)总结- important

四、性能优化 (重要)

1.防抖

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

实现方式

(1)lodash提供的防抖来处理

html 复制代码
<!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 src="./lodash.min.js"></script>
  <script>
    const box = document.querySelector('.box')
    let i = 1  // 让这个变量++
    // 鼠标移动函数
    function mouseMove() {
      box.innerHTML = ++i
      // 如果里面存在大量操作 dom 的情况,可能会卡顿
    }
    // 添加事件
    // box.addEventListener('mousemove', mouseMove)
    // lodash 防抖的写法 - 500ms后加一
    // 语法:_.debounce(fun,时间)
    box.addEventListener('mousemove', _.debounce(mouseMove, 500))

  </script>
</body>

</html>

(2)手写一个防抖函数来处理

手写防抖函数

核心是利用setTimeout()定时器来实现

1.声明定时器变量

2.每次鼠标移动(时间触发)的时候都要先判断是否有定时器,如果有先清除以前的定时器

3.如果没有定时器,则开启定时器,存入到定时器变量里面。

4.定时器里面写函数调用

html 复制代码
<!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  // 让这个变量++
    // 鼠标移动函数
    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))

  </script>
</body>

</html>

2.节流

所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。

(1) lodash函数

html 复制代码
<!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 src="./lodash.min.js"></script>
    <script>
      const box = document.querySelector(".box");
      let i = 1; // 让这个变量++
      // 鼠标移动函数
      function mouseMove() {
        box.innerHTML = ++i;
        // 如果里面存在大量操作 dom 的情况,可能会卡顿
      }

      // box.addEventListener('mousemove', mouseMove)
      // lodash 节流写法
      box.addEventListener("mousemove", _.throttle(mouseMove, 500));

    </script>
  </body>
</html>

(2)手写一个节流函数

手写一个节流函数- 每隔 500ms + 1

节流的核心就是利用定时器(setTimeout)来实现

1.声明一个定时器变量

2.当鼠标每次滑动都先判断是否有定时器了,如果有定时器则不开启新定时器

3.如果没有定时器则开启定时器,记得存到变量里面

3.1 定时器里面调用执行的函数

3.2定时器里面要把定时器清空

html 复制代码
<!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; // 让这个变量++
      // 鼠标移动函数
      function mouseMove() {
        box.innerHTML = ++i;
        // 如果里面存在大量操作 dom 的情况,可能会卡顿
      }
      // 手写一个节流函数- 每隔 500ms + 1

      // 节流的核心就是利用定时器(setTimeout)来实现
      // 1.声明一个定时器变量
      // 2.当鼠标每次滑动都先判断是否有定时器了,如果有定时器则不开启新定时器
      // 3.如果没有定时器则开启定时器,记得存到变量里面
      // 3.1 定时器里面调用执行的函数
      // 3.2定时器里面要把定时器清空
      // 节流函数
      function throttle(fn, t) {
        let timeId = null;
        return function () {
          if (!timeId) {
            timeId = setTimeout(function () {
              fn();
              // 清空定时器
              timeId = null;
              // 不用clearTimeout() 
              // 原因:在setTimeout中是无法删除定时器多,因为定时器还在运作,所以使用timer=null 而不是clearTimeout(timer)
            }, t);
          }
        };
      }
      // box.addEventListener('mousemove', mouseMove)
      box.addEventListener("mousemove", throttle(mouseMove, 200));
    </script>
  </body>
</html>

3.总结

相关推荐
知识分享小能手2 小时前
React学习教程,从入门到精通, React 属性(Props)语法知识点与案例详解(14)
前端·javascript·vue.js·学习·react.js·vue·react
茯苓gao5 小时前
STM32G4 速度环开环,电流环闭环 IF模式建模
笔记·stm32·单片机·嵌入式硬件·学习
是誰萆微了承諾5 小时前
【golang学习笔记 gin 】1.2 redis 的使用
笔记·学习·golang
DKPT6 小时前
Java内存区域与内存溢出
java·开发语言·jvm·笔记·学习
aaaweiaaaaaa6 小时前
HTML和CSS学习
前端·css·学习·html
看海天一色听风起雨落7 小时前
Python学习之装饰器
开发语言·python·学习
speop8 小时前
llm的一点学习笔记
笔记·学习
非凡ghost8 小时前
FxSound:提升音频体验,让音乐更动听
前端·学习·音视频·生活·软件需求
ue星空8 小时前
月2期学习笔记
学习·游戏·ue5
萧邀人8 小时前
第二课、熟悉Cocos Creator 编辑器界面
学习