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.总结

相关推荐
虾球xz2 分钟前
游戏引擎学习第297天:将实体分离到Z层中
c++·人工智能·学习·游戏引擎
奈何不吃鱼8 分钟前
【Redis】二、Redis常用数据类型命令学习
java·redis·学习
夏季疯9 分钟前
学习笔记:黑马程序员JavaWeb开发教程(2025.4.9)
java·笔记·学习
光头小小强0079 分钟前
BurpSuite学习安装
学习
长勺15 分钟前
【JVM】学习笔记
jvm·笔记·学习
云空42 分钟前
《国家高等教育智慧平台:重塑学习新时代》
大数据·人工智能·科技·物联网·学习
nenchoumi31191 小时前
Mujoco 学习系列(三)机器人状态IO与仿真操作
深度学习·学习·机器人
耳总是一颗苹果1 小时前
C语言---内存函数
c语言·开发语言·学习
冷崖2 小时前
网络学习-利用reactor实现http请求(六)
网络·学习·http
YKPG3 小时前
C++学习-入门到精通-【7】类的深入剖析
c++·学习·算法