JavaScript - 进阶+高级(笔记)

这篇具有很好参考价值的文章主要介绍了JavaScript - 进阶+高级(笔记)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

  • 给孩子点点关注吧!😭
    JavaScript - 进阶+高级(笔记)
  • 本篇文章主要记录以下几部分:
    • 进阶:
      • 作用域;
      • 函数进阶(函数提升、函数参数、箭头函数);
      • 解构赋值;
      • 对象进阶(构造函数、实例成员、静态成员);
      • 内置构造函数(Object、Array、String、Number);
      • 编程思想;
      • 构造函数;
      • 原型
    • 高级:
      • 深浅拷贝;
      • 异常处理;
      • this指向;
      • 性能优化(防抖、节流);

  • 以下部分请移步JavaScript - 基础+WebAPI(笔记)
    • 基础:
      • 输入输出语法;
      • 数据类型;
      • 运算符;
      • 流程控制 - 分支语句;
      • 流程控制 - 循环语句;
      • 数组 - 基础;
      • 函数 - 基础;
      • 对象 - 基础;
    • Web API:
      • DOM;
      • DOM事件基础(事件监听、常用事件、事件对象);
      • DOM事件进阶(事件流、事件委托);
      • 日期对象;
      • 节点操作;
      • M端事件;
      • JS插件;
      • window对象;
      • 本地存储;
      • 正则表达式;

叁、❗❗ JavaScript进阶

一、 作用域

  • 作用域(scope) = 全局作用域(Global) + 局部作用域(Local)
  • 局部作用域 = 函数作用域 + 块作用域
  • 作用域: 规定了变量 能够 被访问 的 范围,离开了这个范围,变量便不能被访问
    • 变量的作用范围

1.1 局部作用域

  • Local
  • 局部作用域 = 函数作用域 + 块作用域
1.1.1 函数作用域
  • 注意:
    • 函数内部 声明的 变量 只能在 函数内部访问,外部 无法直接访问(可以利用闭包来访问)
    • 函数参数 也是 函数内部局部变量
    • 不同函数 内部声明 的 变量 无法 互相访问
    • ⚠ 函数执行完毕后,函数内部的变量实际被清空了(JS垃圾回收机制)
1.1.2 块作用域
  • 在JavaScript中使用 {} 包裹的 代码 称为 代码块,代码块内部 声明的变量 外部【有可能:var声明的可以】无法访问
    • 代码展示:
      // 用 let 声明的变量 有 块作用域
      for (let i = 0; i < 5; i++) {
        console.log(i);
      }
      console.log(i); // i is not defined
      -------------------------------------------------------------------
      // 用 var 声明的变量 没有 块作用域
      for (var i = 0; i < 5; i++) {
        console.log(i);
      }
      console.log(i); // 5 
      
  • 注意:
    • letconst 声明的变量 会产生 块作用域
    • var 🔺不会产生🔻 块作用域
      • var 会被 函数作用域 所限制
    • 不同代码块之间 的 变量 无法 互相访问
    • 推荐使用 letconst

1.2 全局作用域

  • Global
  • 注意:
    • window 对象 动态添加的 属性 默认 是 全局变量,不推荐!
    • 函数中 未使用 任何关键字 声明的 变量 是 全局变量,不推荐!!!
    • 尽可能减少声明全局变量,防止变量污染

1.3 作用域链

  • 作用域: 规定了变量 能够 被访问 的 范围,离开了这个范围,变量便不能被访问
    • 变量的作用范围
  • 嵌套关系作用域 串联 起来形成了 作用域链
  • 本质: 底层 变量查找机制
    • 在函数执行时,会 优先 在 当前 函数作用域 中 查找变量
    • 如果 当前作用域 查找不到 则会 依次逐级 查找 父级作用域 直到 全局作用域
    • 代码展示:
      // 作用域链
      // 本质上就是底层(最小的那个作用域) 变量的查找机制
      // 变量访问机制:就近原则,由内向外
      // 全局作用域
      let a = 1;
      let b = 2;
      // 局部作用域
      function f() {
          let a = 1;
          let b = 3;
          // 局部作用域
          function g() {
              a = 2;
              let c = 4;
              console.log(a);
          }
          g();   // 调用g
      }
      f();   //调用f	// 2
      
  • 注意:
    • 相同作用域链 中按照 从小到大 的规则查找变量
    • ⚠🔺 作用域 能够访问 作用域,作用域 无法访问 作用域
      • 见上面代码展示:g函数 可以 log b,f函数 无法 log c

1.4 JS垃圾回收机制 - GC

  • JS中 内存的分配回收 都是 自动完成 的,内存 不使用 的时候 会被 垃圾回收器 自动回收
    • 内存泄漏: 不再用到的内存,没有及时释放
  • 内存的生命周期
    • 1️⃣ 内存分配: 声明 变量函数对象的时候,系统会 自动 为它们 分配内存
    • 2️⃣ 内存使用:读写内存,也就是 使用 变量、函数等
    • 3️⃣ 内存回收: 使用完毕,由垃圾回收器 自动回收 不再使用的内存
    • 注意:
      • 全局变量 一般 不会回收(关闭页面回收)
      • ⚠ 一般情况下 局部变量 的值,不用了就会被自动回收
  • 垃圾回收算法说明:
    • 1️⃣❌ 引用计数法
      • 致命缺陷:嵌套引用
      • 如果两个对象相互引用,尽管他们不再使用,垃圾回收器不会进行回收,导致内存泄漏
      • JavaScript - 进阶+高级(笔记)
    • 2️⃣ 标记清除法
      • JavaScript - 进阶+高级(笔记)

1.5 ❗❗ 闭包

  • 闭包:Closure

  • 概念: 一个函数 对 周围状态的引用 捆绑在一起,内层函数 能够访问到 其外层函数 的 作用域

    • 闭包是一种使用过程

    • 闭包 = 内层函数 + 外层函数的变量

    • 代码展示:

      function fn() {
        let b = 9;
        function outer() {
          const a = 1;
          console.log(b);
          function f() {
            console.log(a);
            console.log(b);
          }
          f();
        }
        outer();
      }
      fn();
      
      • 函数套函数 不一定会 产生 闭包
      • 如果 内层函数 用到 外层函数变量 这种情况就 会产生闭包
      • JavaScript - 进阶+高级(笔记)
  • 闭包使用注意: 闭包使用的时候,内部的变量因为被外部引用了,所以代码执行完毕不会释放内存 - 内存泄漏(引用计数法)

  • 闭包作用:

    • 实现数据的私有化
    • 外部 可以 访问 函数内部 变量
    • 允许将 函数 与其所操作的 某些数据(环境)关联起来
  • 应用场景:

    • 防抖
    • 节流(定时器)
  • 基本格式:

    function fn() {
      let b = 9;
      function outer() {
        const a = 1;
        console.log(b);
        function f() {
          let c = 6;
          console.log(a);
          console.log(b);
        }
        return f;	// function 复杂数据类型,返回出去的是指向函数的地址
      }
      return outer;
    }
    const fun = fn(); // fn() === outer === function outer() {} => fun
    fun(); // b = 9 // 相当于调用outer()函数
    
    const fun1 = fun();// fun() === outer() === f ===  function f() {} => fun1	// b = 9
    fun1(); // a = 1; b = 9	// 相当于调用f()函数
    
  • 闭包应用: 实现 数据 的 私有

    • 代码展示:
      // 闭包的应用:统计函数调用的次数
      function fun() {
        let i = 0;
        function fn() {
          i++;
          console.log(`fn()函数被调用了${i}`);
        }
        return fn;
      }
      const f = fun();  // fun() === fn === function fn() {} => f
      f();
      // Global -> fun() === fn === function fn() {} -> f ->  fn() -> i++ -> i
      // 可以查找到i,所以i不会被回收,调用完函数后,i应该被回收,现在没有回收,内存泄漏
      
  • ❗❗ 闭包面试:怎么理解闭包 -> 闭包的作用 -> 闭包可能引起的问题文章来源地址https://www.toymoban.com/news/detail-444621.html

1.6 ❌ 变量提升

  • 它允许在变量声声明之前既被访问(仅存在于var声明变量)
  • 变量提升: 在代码执行之前,检测在 当前作用域所有var 声明的变量,会将所有 用 var声明的变量 提升到 🔺当前作用域🔺最前面
  • 注意:
    • 变量提升出现在 相同作用域 当中
    • letconst 声明的变量 不存在变量提升
    • 只 提升 声明,不 提升 赋值
    • 代码展示:
      console.log(`${num}`);
      var num = 10;
      // 控制台打印: undefined件
      // 上面代码本质上就是下面的代码
      var num;
      console.log(`${num}`);
      num = 10;
      
  • ES6定义变量
    let : 变量
    const : 常量
    
    let/constvar 的区别
      + 预解析
        -> let/const 不会进行预解析(变量提升),必须先定义后使用
        -> var 会进行预解析
      + 变量名
        -> let/const 不能声明重复的变量名
        -> var 可以声明重复的变量名
      + 块级作用域(被代码块限制变量的使用范围)
        -> let/const 有块级作用域  (只要是能书写 {} 的代码块都能限制使用范围)
        -> var 没有块级作用域  (只有函数作用域才能限制使用范围)
    letconst 的区别
      + let 是 变量
        const 是 常量
      + let 在声明的时候可以不进行赋值 (留着以后用)
        const 在声明的时候必须进行赋值 (声明不赋值会报错)
      + let 声明的变量可以被修改                     
        const 声明的常量不可以修改 (修改就会报错)
    

二、函数进阶

2.1 函数提升

  • 代码执行之前,会把所有 函数声明 提升到 当前作用域最前面
  • 预解析 === 声明提升:在每个作用域的 代码执行之前,会把当前作用域代码中的声明部分提升到作用域最前面执行
    • 变量声明提升(var)
    • 函数声明提升(function)
    • ⚠ 注意:先提升var再提升function
  • ⚠🔺 注意:
    • 🔺 只 提升 函数声明,不 提升 函数调用
    • 🔺 函数表达式 必须 先声明 后调用(🔺不存在函数提升🔺)
    • 函数提升 优先级 高于 变量提升
  • 代码展示:
    fn();
    function fn() {
      console.log('函数提升,只提升声明,不提升调用');
    
    // 上面代码本质上就是下面代码
    function fn() {
      console.log('函数提升,只提升声明,不提升调用');
    }
    fn();
    ----------------------------------------------------------------------
    fun();
    var fun = function () { console.log('函数表达式不存在提升现象'); }
    // 报错:Uncaught TypeError(未捕获的类型错误): fun is not a function
    var fun;		// fun ->  
    fun();
    fun = function () { console.log('函数表达式不存在提升现象'); }
    // fun不能提升,
    // 上面代码是在给fun赋值,var声明的变量 只 提升声明,不 提升赋值
    

2.2 函数参数

  • 函数参数默认值 + 动态参数 + 剩余参数
2.2.1 动态参数
  • arguments: 函数内部 内置伪数组变量,包含了 调用 函数时 传入所有实参
  • 只能在普通函数里使用
  • 作用: 动态获取函数的实参
  • 注意:
    • 表示 所有实参集合
    • arguments 是一个 伪数组, 只存在于 函数内部
    • 箭头函数 🔺没有🔺 arguments
    • 🔺 可读写
  • 代码展示:
    // 动态参数 - arguments(伪数组)
    function getSum() {
      let sum = 0;
      for (let i = 0; i < arguments.length; i++) {
        sum += arguments[i];
      }
      return sum;
    }
    console.log(getSum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));	// 55
    
2.2.2 ✔ 剩余参数
  • 允许将一个 不定数量参数 表示为一个 数组
  • 是语法符号,置于 最末 函数形参 之前,用于获取 剩余(多于) 的实参
  • 注意:
    • 借助 获取的 剩余实参,是个真数组
    • 剩余参数放在 最末位
    • 如果 没有剩余的参数,得到的就是一个 空数组
  • 代码展示:
    // 剩余参数 - ...变量名(数组)
    function getSum(...arr) {
      console.log(arr);
    }
    getSum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);	
    // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    ----------------------------------------------------------------------
    // ... 是语法符号,置于 最末 函数形参 之前,用于获取 剩余(多余) 的实参
    function getSum(a, b, c, ...arr) {
      console.log(arr);
    }
    getSum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);	
    // [4, 5, 6, 7, 8, 9, 10]
    
  • 动态参数剩余参数 的区别:
    • 动态参数伪数组
    • 剩余参数真数组
  • 拓展:
    • 展开运算符:
      • 展开运算符将一个 数组对象 展开
        • 不改变原始数组
      • 当你在 函数的实参 或者 数组 或者 对象 里面使用的时候是 展开运算符
      • 展开运算符写在数组前面
      • 应用场景:
        • 用于获取数组的最大值
        • 合并数组
        • 解构赋值(部分场景)
          • 解构赋值展开运算符 只能写在 最后一个元素前面
      • 展开运算符 or 剩余参数
      • 展开运算符: 数组 中使用,数组展开,在 数组内部 使用
      • 剩余参数: 函数参数 使用,得到真数组,在 函数内部 使用
      • 代码展示:
        // 获取数组最大值
        const arr = [3, 45, 2, 89, 54];
        console.log(...arr);	// 3 45 2 89 54
        console.log(Math.max(...arr));	// 89
        // 合并数组
        const arr1 = [1, 2, 4];
        const arr2 = [...arr, ...arr1];	// ...arr1 === 1, 2, 4
        console.log(arr2);	  // [3, 45, 2, 89, 54, 1, 2, 4]
        // 解构赋值
        const [n1, n2, ...n3] = [1, 2, 3, 4, 5];
        console.log(n1, n2, n3);	// 1 2 [3, 4, 5]
        ---------------------------------------------------------------
        const [n1, ...n2, n3] = [1, 2, 3, 4, 5];	// Uncaught SyntaxError: Rest element must be last element
        

2.3 ❗❗❗ 箭头函数

  • ⚠🔺 注意:
    • 箭头函数 没有 arguments,但是 剩余参数
    • 箭头函数 没有 this
    • 箭头函数 替代原本需要 匿名函数 的地方
    • 函数表达式简写方式 (匿名函数)
    • 声明式函数 不能写
2.3.1 基本语法
  • 语法:
    () => {}  // (function () {})
    () : 形参的位置
    => : 箭头函数的标志
    {} : 代码段
    
  • ⚠🔺 特性
    • 1️⃣ 只有 一个形参
      • 可以 省略小括号
      • 代码展示:⬇
    • 2️⃣ 代码段 只有 一行代码并且 返回值
      • 可以 省略 大括号并 自动 做为 返回值 被返回
      • 如果有返回值,可以 省略 return
      • 代码展示:⬇
    • 3️⃣ 加括号函数体 返回 对象字面量表达式(直接返回一个对象)
      • 箭头函数可以返回对象,但是必须用小括号包裹
      • 代码展示:⬇
    • 代码展示:
      // 箭头函数只有一个参数可以省略小括号
      const fn = x => { console.log(x); }
      fn(1);  // 1
       
      // 箭头函数没传递参数小括号还是要写的
      const fn1 = () => console.log('箭头函数没传递参数小括号还是要写的');
      fn1();	// '箭头函数没传递参数小括号还是要写的'
      
      // 箭头函数代码段只有一行代码,可以省略大括号
      const fn2 = x => console.log(x);
      fn2(2);  // 2
      
      // 箭头函数只有一行代码,且有返回值,可以省略return
      const fn3 = x => x + x;
      console.log(fn3(3));	// 6
      
      // 加括号的函数体返回对象字面量表达式(直接返回一个对象)
      // 对象的{} 和 函数的{}有冲突,需要拿个 () 包住对象
      const fn4 = uname => ({ uname: uname });
      console.log(fn4('迪迦奥特曼'));  // {uname: '迪迦奥特曼'}
      
      JavaScript - 进阶+高级(笔记)
  • 注意:
    • ⚠🔺箭头函数 属于 表达式函数,因此 不存在函数提升
2.3.2 箭头函数参数
  • 箭头函数 没有 arguments 动态参数,但是 剩余参数 …args
    • 代码展示:
      const getSum = (...args) => {
      	let sum = 0;
      	args.forEach(item => {
      		sum += item;
      	});
      	return sum;
      }
      console.log(getSum(3, 9));	// 12
      
  • 箭头函数只要你设置默认值,不管多少个形参,都必须要写小括号
    let fn = (a = 100) => { console.log(a); }
    fn();	    // 10
    fn(100);	// 100
    
2.3.3 箭头函数 this
  • 函数 才有this
  • 以前的this指向:
    // 以前this指向:谁调用这个函数 this 指向谁
    console.log(this);    // this -> window
    // window.console.log(this);
    
    // 普通函数的this
    function fn() {
    	console.log(this);
    }
    fn();   // this -> window
    // window.fn();
    
    // 对象方法里面的this
    const obj = {
    	uname: '迪迦奥特曼',
    	sayHi: function () {
    			console.log(this);
    	}
    }
    obj.sayHi();  // this -> obj
    
  • window 是JS中的全局对象,我们 声明 的 变量 或 函数 实际上是给 window 添加 属性 或 方法
  • 箭头函数 不会创建 自己的this,它只会 沿用 自己所在这条作用域链上一层作用域this
    • 箭头函数的 this指向被创建的时候上下文中的this(出生的时候,所在的作用域中的this是谁,以后就都指向谁)
  • 箭头函数里面的 this 任何方法都改变不了
    • 因为箭头函数没有 this (它用的是上一层作用域链的 this )
    • call / apply / bind 不能改变
  • 代码展示:
    // 箭头函数没有this指向,它只会 沿用 自己所在这条作用域链 的 上一层作用域 的 this
    const fn = () => console.log(this); // this -> window
    fn();	
    // 并不是window调用了fn,而是箭头函数没有this,它只会沿用自己所在这条作用域链的上一层作用域的this,上一层作用域是Global,Global的this指向window,所以指向window
    
    const obj = {
    	uname: '迪迦奥特曼',
    	sayHi: () => console.log(this)  // this -> window
    }
    obj.sayHi();  // window.obj.sayHi(); 
    // 箭头函数没有this,它只会沿用自己所在这条作用域链的上一层作用域的this
    // 也就是obj的this,window调用了obj,所以this指向window
    
    const obj1 = {
    	uname: '迪迦奥特曼',
    	sayHi: function () {
    		let i = 10;
    		const count = () => console.log(this);  // this -> obj1
    		count();
    	}
    }
    obj1.sayHi();
    // 箭头函数没有this,它只会沿用自己所在这条作用域链的上一层作用域的this
    // 既function的this,function(普通函数)的this指向函数的调用者obj1这个对象
    

三、❗❗ 解构赋值

3.1 数组解构

3.1.1 基本语法
  • 数组解构: 将数组的 单元值(数组元素) 快速 批量赋值 给 一系列变量 的 简介语法
  • 基本语法:
    • 赋值运算符 = 左侧[] 用于 批量 声明变量右侧数组单元值 将被 赋值给左侧的变量
    • 变量的顺序 对应 数组单元值的位置 依次 进行 赋值操作
  • ⚠🔺 注意: 数组解构 赋值 是按照 索引 赋值
  • 代码展示:
    // 将等号左侧数组里的单元值按顺序赋值给等号右侧的变量
    // 按照索引进行赋值
    const [a, b, c] = [1, 2, 3];
    console.log(a);		// 1
    console.log(b); 	// 2
    console.log(c); 	// 3
    
  • 利用数组解构交换两个变量的值
    let a = 11;
    let b = 22;
    [b, a] = [a, b];
    console.log(a)		// 22
    console.log(b) 	// 11
    ----------------------------------------------------------------------
    let a = 11
    let b = 22
    [b, a] = [a, b]
    console.log(a)
    console.log(b)
    // Uncaught ReferenceError: b is not defined
    // let b = 22 => 后面必须加分号才不会报错
    
3.1.2 特殊情况
  • 1️⃣ 变量多 单元值少
    • 右侧的单元值按顺序对左侧的变量进行赋值,没有赋值的变量则是 undefined
    • 代码展示:
      const [hr, lx, mi, fz, hw] = ['海尔', '联想', '小米', '方正'];
      console.log(hr, lx, mi, fz, hw);
      // 海尔 联想 小米 方正 undefined
      
  • 2️⃣ 变量少 单元值多
    • 右侧的单元值按顺序对左侧的变量进行赋值,剩余的单元值丢弃不用
    • 代码展示:
      const [hr, lx, mi] = ['海尔', '联想', '小米', '方正'];
      console.log(hr, lx, mi);
      // 海尔 联想 小米
      
  • 3️⃣ 利用 剩余参数 解决 变量少 的问题
    • 注意:
      • 剩余参数 返回的是一个真数组
      • 剩余参数 放在 最末位
    • 代码展示:
      const [hr, lx, ...mi] = ['海尔', '联想', '小米', '方正'];
      console.log(hr, lx, mi);
      // 海尔 联想 [小米, 方正]
      
  • 4️⃣ 防止 undefined 传递
    • 设置默认值
    • 代码展示:
      const [hr, lx, mi, fz, hw = '华为'] = ['海尔', '联想', '小米', '方正'];
      console.log(hr, lx, mi, fz, hw);  // 海尔 联想 小米 方正 华为
      
  • 5️⃣ 按需导入,忽略某些值
    • 变量可以忽略,但是位置还是要留的
    • 代码展示:
      const [hr, lx, mi, , hw = '华为'] = ['海尔', '联想', '小米', '方正'];
      console.log(hr, lx, mi, hw);  // 海尔 联想 小米 华为
      
  • 6️⃣ 多维数组解构
    • 代码展示:
      const [a, [b, [c, d, [e]]], f] = [1, [2, [3, 4, [5]]], 6];
      console.log(a, b, c, d, e, f);
      // 1 2 3 4 5 6
      

3.2 对象解构

  • 对象解构: 将对象 属性 和 方法 快速 批量赋值 给一些列 变量 简介语法
3.2.1 基本语法
  • 赋值运算符 = 左侧{} 用于 批量声明变量右侧对象的属性值将被 赋值 给左侧的变量
  • 对象属性的值 将被赋值给 与 对象属性名 相同变量
  • 注意:
    • ⚠🔺 对象解构赋值的时候按照 属性名 进行 赋值
    • 对象解构的变量名不要和外面的变量名冲突否则报错
    • ⚠🔺 对象中 找不到变量名一致的属性名变量值undefined
    • ⚠🔺🔺 变量名对象的属性名 必须一致否则 变量的值 就是 undefined
    • ⛔错误展示:
      const { uname, Age } = { uname: '迪迦奥特曼', age: 22 };
      console.log(uname, Age);	// 迪迦奥特曼 undefined
      
  • 代码展示:
    const {uname, age} = {uname: '迪迦奥特曼', age: 22};
    console.log(uname, age);	// 迪迦奥特曼 22
    // 等价于
    // const uname = obj.uname;
    // const age = obj.age;
    
3.2.2 特殊情况
  • 1️⃣ 对象解构的变量名 可以重新声明
    • 语法: 旧变量名: 新变量名
    • 代码展示:
      const uname = '赛罗奥特曼';
      const { uname: username, age } = { uname: '迪迦奥特曼', age: 22 };
      console.log(username, age);	// 迪迦奥特曼 22
      
  • 2️⃣ 解构数组对象
    • 代码展示:
      const obj = [
        {
          uname: '迪迦奥特曼',
          age: 22
        },
        {
          uname: '赛罗奥特曼',
          age: 23
        }
      ];
      const [{ uname, age }, { uname: userName, age: userAge }] = obj;
      console.log(uname, age, userName, userAge); // 迪迦奥特曼 22 赛罗奥特曼 23
      
  • 3️⃣ 多级对象解构
    • 解构的时候必须写 🔺对象名🔺,不能打印
    • 代码展示:
      const pig = {
        name: '佩奇',
        family: {
          mother: '猪妈妈',
          father: '朱爸爸',
          sister: '乔治'
        },
        age: 6
      };
      const { name, family: { mother, father, sister }, age } = pig;
      console.log(name, mother, father, sister, age); // 佩奇 猪妈妈 朱爸爸 乔治 6 
      
      const person = [
        {
          name: '佩奇',
          family: {
            mother: '猪妈妈',
            father: '朱爸爸',
            sister: '乔治'
          },
          age: 6
        }
      ];
      const [{ name, family: { mother, father, sister }, age }] = person;
      console.log(name, mother, father, sister, age); // 佩奇 猪妈妈 朱爸爸 乔治 6
      
      ✔✔
      // 1. 这是后台传递过来的数据
      const msg = {
        "code": 200,
        "msg": "获取新闻列表成功",
        "data": [
          {
            "id": 1,
            "title": "5G商用自己,三大运用商收入下降",
            "count": 58
          },
          {
            "id": 2,
            "title": "国际媒体头条速览",
            "count": 56
          },
          {
            "id": 3,
            "title": "乌克兰和俄罗斯持续冲突",
            "count": 1669
          },
        ]
      };
      
      // 需求1:请将以上msg对象,采用对象解构的方式,只选出 data 方便后面使用渲染页面
      const { data } = msg;
      console.log(data);
      // 需求2:上面msg是后台传递过来的数据,我们需要把data选出当做参数传递给 函数
      // const { data } = msg;
      // msg 虽然很多属性,但是我们利用解构只要 data值
      function render({ data }) {
        // const { data } = arr;
        // 我们只要 data 数据
        // 内部处理
        console.log(data);
      }
      render(msg);
      
      // 需求3:为了防止msg里面的data名字混淆,要求渲染函数里面的数据名改为 myData
      function render({ data: myData }) {
        // 要求将 获取过来的 data数据 更名为 myData
        // 内部处理
        console.log(myData);
      }
      render(msg);
      
  • 今日案例拓展:
    • :active 伪类选择器
      • 活动链接(点击的时候就是活动链接)
    • draggable 可拖拽的
      • draggable ="false" 禁止拖拽
      • draggable ="true" 可以拖拽

四、对象进阶

4.1 创建对象的三种方式

  • 1️⃣ 字面量 创建
    • const obj = {};
      
  • 2️⃣❌ new Object 创建 (系统的构造函数)
    • const obj = new Object();
      
    • 追加属性
      const obj = new Object({uname: '迪迦奥特曼', age: 22});
      
  • 3️⃣ 工厂函数创建
    • JavaScript - 进阶+高级(笔记)
  • 4️⃣ 构造函数 创建 (自定义的构造函数)
    • JavaScript - 进阶+高级(笔记)
  • new操作符的功能
    • 在函数代码执行之前,隐式的创建一个空对象,把this指向空对象
    • 执行函数代码
    • 在函数代码执行之后,隐式的返回this
  • 操作对象
    • 1️⃣ 点语法 — 操作对象
      // 增 
      o.name = 'Jack'
      o.age = 20
      // 删
      delete o.name
      // 查
      console.log(o.age)
      // 改
      o.age = 22
      
  • 2️⃣ 数组关联语法 — 操作对象
    增
    o['name'] = 'Jack'
    o['age'] = 20
    o['gender'] = '男'
    o['class'] = 1delete o['name']
    查
    o['age']
    改
    o['age'] = 21
    
  • 判断一个成员在不在对象里面 — in
    • 语法:
      属性名 in '对象名'	
      
    • 返回值: true / false
    • 代码展示:
      let obj = {
        name: 'jack',
        age: 18,
        gender: '男',
        score: 100
      };
      console.log('name' in obj);	// true
      

4.2 构造函数(自封装)

  • 是一种 特殊的函数,主要用来 初始化对象
  • 可以用构造函数 创建多个 类似 对象(对公共的部分进行抽取并封装)
  • 规范:
    • 命名以 大写字母 开头
    • ⚠🔺 只能由 new 操作符来执行
  • ⚠🔺 注意:
    • 使用 new 关键字调用函数的行为被称为 实例化
    • 构造函数内部 无需写return返回值 即为 新创建的对象
    • 构造函数内部的 return 返回的值 无效,所以不要写 return
  • ❗❗ 实例化执行的过程: (面试)
    • 1️⃣ 创建新的 空对象
    • 2️⃣ 构造函数 this 指向 新对象
    • 3️⃣ 执行构造函数代码,修改this,添加新属性
    • 4️⃣ 返回新对象
  • 代码展示:
    JavaScript - 进阶+高级(笔记)
    JavaScript - 进阶+高级(笔记)

4.3 实例成员 & 静态成员

4.3.1 实例成员
  • 构造函数 创建的 对象 称为 实例对象
  • 🔺 实例对象属性方法 称为 实例成员
  • 注意:
    • 为 构造函数 传入 参数,动态创建 结构相同值不同 的 对象
    • 构造函数 创建 的 实例对象 彼此独立 互不影响
4.3.2 静态成员
  • 🔺 构造函数属性方法 被称为 静态成员
  • ⚠🔺 静态成员方法 中的 this 指向 构造函数
  • 一般 公共特征 的属性或方法 静态成员设置为静态成员
    JavaScript - 进阶+高级(笔记)

五、内置构造函数

  • 字符串、数值、布尔等基本数据类型也都有专门的构造函数,称为 基本包装类型
  • 引用类型: Object、Array、RegExp、Date 等
  • 包装类型: String、Number、Boolean 等

5.1 Object

  • ❌ 使用内置构造函数创建对象
    const obj = new Object({uname: '迪迦', age: 22});
    
  • 常用的 静态方法
    • 静态方法: 只有 构造函数 Object 才可以 调用 (写在构造函数身上的方法)
    • 1️⃣ ✔ Object.keys()
      • 获取当前对象中的 所有 可枚举的 属性名(键)
      • 语法:
        Object.keys(对象名)
        
      • 返回值: 数组 (数组的元素 = 对象的属性)
      • 代码展示:⬇
    • 2️⃣ ✔Object.values()
      • 获取对象中 所有属性值
      • 语法:
        Object.values(对象名)
        
      • 返回值: 数组(数组的元素 = 对象属性值)
      • 代码展示:⬇
    • 3️⃣ Object.assign()
      • 用于 对象拷贝(浅拷贝)
      • 使用场景: 给对象添加属性
      • 语法:
        Object.assign(b, a)	// 把 a 拷贝给 b
        Object.assign(拷贝者, 被拷贝者);
        
      • 代码展示:⬇
    • 代码展示:
      // 静态方法:只有 构造函数 Object 才可以调用
      const obj = { uname: '迪迦', age: 22 };
      // Object.keys(对象):获取对象的 所有属性名,放在一个数组里面返回
      const arr = Object.keys(obj);
      console.log(arr);   // ['uname', 'age']
      
      // Object.values(对象名):获取对象的 所有属性值,放在一个新数组中返回
      const arr1 = Object.values(obj);
      console.log(arr1);  // ['迪迦', 22]
      
      // Object.assign(拷贝者, 被拷贝者):将对象a拷贝给对象b
      const obj1 = {};
      Object.assign(obj1, obj);
      console.log(obj1);  // { uname: '迪迦', age: 22 }
      // 追加属性
      Object.assign(obj1, {gender: '男'});
      console.log(obj1);	// {uname: '迪迦', age: 22, gender: '男'}
      

5.2 Array

  • ❌使用Array内置构造函数创建数组
    const arr = new Array();							// 创建一个空数组
    const arr = new Array(5);							// 一个参数:数组的长度
    const arr = new Array(1, 2, 3, 4, 5); // 多个参数:[1, 2, 3, 4, 5]
    
5.1 数组常用方法总结
5.1.1 改变原始数组 (7)
  • 1️⃣ push()
    • 描述:一个或多个元素 追加到 数组的末尾 ,并返回 追加元素之后 数组的 length
    • 语法:
      arr.push(elmeent1, ..., elementN);
      
    • 返回值:
      • 追加元素 之后 数组的 length
    • 代码展示:
      const arr = [1, 2, 3];
      const length = arr.push(4, 5);	// 追加元素之后arr的length
      console.log(arr);			// [1, 2, 3, 4, 5]
      console.log(length);	// 5
      
  • 2️⃣ unshift()
    • 描述:一个或多个元素 插入到 数组的开头 ,并返回 插入元素之后 数组的 length
    • 语法:
      arr.unshift(element1, ..., elementN);
      
    • 返回值:
      • 插入元素 之后 数组的 length
    • 代码展示:
      const arr = [3, 4, 5];
      const length = arr.unshift(1, 2);	// 插入元素之后arr的length
      console.log(arr);			// [1, 2, 3, 4, 5]
      console.log(length);	// 5
      
  • 3️⃣ pop()
    • 描述: 删除 数组 最后一个元素,并返回 被删除的元素
      • ⚠ 会 改变 数组的 length
    • 语法:
      arr.pop();
      
    • 返回值:
      • 被删除的元素
    • 代码展示:
      const arr = [1, 2, 3];
      const lastValue = arr.pop();	// 被删除的元素
      console.log(arr);			// [1, 2]
      console.log(lastValue);	// 3
      
  • 4️⃣ shift()
    • 描述: 从数组中删除 第一个元素,并返回 该元素的值
      • 改变数组的长度
    • 语法:
      arr.shift()
      
    • 返回值: 被删除的元素
    • 代码展示:
      const arr = [1, 2, 3];
      const firstValue = arr.shift();	// 被删除的元素
      console.log(arr);			// [2, 3]
      console.log(firstValue);	// 1
      
  • 5️⃣ splice()
    • 描述: 通过 删除替换 现有元素原地添加新元素修改数组,并以 数组的形式返回被修改的内容
    • 语法:
      array.splice(start[, deleteCount[, item1][, ...])
      
    • 返回值:数组的形式 被修改 的 内容
    • 注意:
      • 删除/替换 的时候,包括 开始索引位置
      • 添加 的时候,是添加在 开始索引位置之前
        • 添加 的时候返回的是 空数组
    • 代码展示:
      // 删除
      const arr = [1, 2, 3];
      const Value = arr.splice(1, 1);	// 以数组的形式返回被修改的内容
      console.log(arr);			// [1, 3]
      console.log(Value);		// [2]
      // 替换
      const arr = [1, 2, 3];
      const Value = arr.splice(1, 1, 6);	// 以数组的形式返回被修改的内容
      console.log(arr);			// [1, 6, 3]
      console.log(Value);		// [2]
      // 添加
      const arr = [1, 2, 3];
      const Value = arr.splice(1, 0, 8);	// 
      console.log(arr);			// [1, 8, 2, 3]
      console.log(Value);		// []
      
  • 6️⃣ sort() - 数组排序
    • 描述: 对数组元素进行排序,并返回数组
    • 语法:
      arr.sort()	可以省略参数,升序
      arr.sort(function (a, b) { return a - b } )		升序排列
      arr.sort(function (a, b) { return b - a } )		降序排列
      
    • 返回值: 排序好的数组
    • 代码展示:
      const arr = [23, 45, 78, 10];
      arr.sort();
      console.log(arr);	// [10, 23, 45, 78]
      arr.sort(function (a, b) {return a - b});
      console.log(arr);	// [10, 23, 45, 78]
      arr.sort(function (a, b) {return b - a});
      console.log(arr);	// [78, 45, 23, 10]
      
  • 7️⃣ reverse() - 反转数组
    • 描述: 将数组中的元素的 位置颠倒 ,并返回该数组
    • 语法:
      arr.reverse();
      
    • 返回值: 颠倒顺序后的数组
    • 代码展示:
      const arr = [1, 2, 3, 4, 5];
      arr.reverse();
      console.log(arr);	// [5, 4, 3, 2, 1]
      
5.1.2 不改变原始数组
  • 1️⃣ ✔ forEach - 遍历数组
    • 描述: 适合遍历 数组对象
    • 语法:
      Array.forEach(function (item[, index[, Array]]) {  } )
      
    • 返回值:
    • 代码展示:
      const arr = ['red', 'purple', 'pink'];
      arr.forEach((item, index) => {
        console.log(item);
        console.log(index);
      });
      // red 0 purple 1 pink 2
      
  • 2️⃣ ✔ map() - 迭代数组(映射数组)
    • 描述: 遍历原数组,把原数组中的每一个数据加工改造,形成一个新数组返回
    • JavaScript - 进阶+高级(笔记)
    • 语法:
      Array.map(function (item[, index[, Array]]) {} )
      
    • 返回值: 新数组
      • 将原始数组里面的元素进行处理之后添加到新数组里
      • 新数组length = 旧数组length
    • 代码展示:
      const arr = ['red', 'blue', 'green'];
      const newArr = arr.map(function (item, index, arr) {
        return item + '老师';
      });
      console.lgo(newArr);		// ['red老师', 'blue老师', 'green老师']
      
      const arr1 = [10, 20, 30];
      const newArr1 = arr1.map(function (item, index, arr) {
        return item + 10;
      });
      console.log(newArr1);		// [20, 30, 40]
      
  • 3️⃣ ✔ filter() - 筛选数组
    • 描述: 过滤 原始数组中的数据,把 满足条件 的数据放在一个新数组中
      • 在执行函数返回true的情况下,留下该数据,返回为false去除该数据
    • JavaScript - 进阶+高级(笔记)
    • 语法:
      Array.filter(function (item[, index[, Array]]) {} )
      
    • 返回值: 是一个新数组,里面是所有原始数组中满足条件的数据
    • 代码展示:
      const arr = [20, 1, 34, 60, 49, 90, 200, 288];
      const newArr = arr.filter(item => {
        return item > 100;
      });
      console.log(newArr); // [200, 288]
      -------------------------------------------------------------------
      const arr = [23, 4, 19, 22, 56, 77];
      const newArr = arr.filter(item => {
      	if (item % 2 === 0) {
      		return true;
        } else {
          return false;
        }
      });
      console.log(newArr);
      // 上面代码等价于下面代码
      const newArr = arr.filter(item => item % 2 === 0);
      console.log(newArr);
      
  • 4️⃣ ✔ reduce() - 累计器(求和)
    • 描述: 每一次运行 reduce 会将先前元素的计算结果作为参数传入,最后将其结果汇总为单个返回值
    • JavaScript - 进阶+高级(笔记)
    • 语法:
      Array.reduce(function () {}, 起始值)
      Array.reduce(function (累计值, 当前元素[, 索引号][, 原数组]) {}[, 起始值])
      
    • 参数:
      • 起始值可以省略
        • 如果写就作为第一次累计的起始值
        • 如果没有,就取第一个元素作为起始值(累加的时候从第二个元素开始)
      • 有起始值,则以起始值为准开始累计,累计值 = 起始值
      • 没有起始值,则累计值以数组的第一个元素作为起始值开始累计
      • 后面每次遍历就会用后面的数组元素 累计 到 累计值 里面
    • 返回值: 返回函数累计的处理的结果
    • 代码展示:
      // 数组.reduce(function (累计值, 当前元素) {}, 起始值)
      const arr = [1, 2, 3, 4, 5];
      const sum = arr.reduce((prev, item) => prev + item, 0);
      // prev + item
      // 0 + 1 = 1
      // 1 + 2 = 3
      // 3 + 3 = 6
      // 6 + 4 = 10
      // 10 + 5 = 15
      console.log(sum);		// 15
      const sum = arr.reduce((prev, item) => prev + item);
      // 将数组的第一个元素给prev,从第二个元素开始累加
      // prev + item
      // 1 + 2 = 3
      // 3 + 3 = 6
      // 6 + 4 = 10
      // 10 + 5 = 15
      console.log(sum);		// 15
      
  • 5️⃣ join()
    • 描述: 将一个 数组类数组对象 的所有元素连接成一个 字符串,并返回这个字符串
      • 注意:所有的元素 转换成 字符串分隔符 将这些字符串 连接 起来
    • 语法:
      arr.join([分隔符]);
      
      • 参数: 默认 使用 逗号
    • 返回值: 字符串
    • 代码展示:
      const arr = ['迪迦', '赛罗', '泰罗'];
      let str = arr.join();
      let str1 = arr.join('-');
      console.log(str);		// 迪迦,塞罗,泰罗
      console.log(str1);	// 迪迦-塞罗-泰罗
      console.log(arr);		// ['迪迦', '赛罗', '泰罗']
      
  • 6️⃣ ✔find() - 返回数组中满足条件的 第一个元素
    • 描述: 返回 数组 中 满足条件第一个元素
    • JavaScript - 进阶+高级(笔记)
    • 语法:
      Array.find(function (item, index, arr) {} [, thisArg])
      
    • 返回值:
      • 有 满足条件 元素: 返回 第一个 元素
      • 没有 这个元素: undefined
    • 代码展示:
      const arr = [5, 12, 8, 130, 44];
      const found = arr.find(item => item > 10);
      console.log(found);		// 12
      
  • 7️⃣ some()
    • 描述: 判断 数组中是不是 至少有一个元素 满足条件
    • JavaScript - 进阶+高级(笔记)
    • 语法:
      Array.some(function (item, index[, arr]) {})
      
    • 返回值: 布尔值(true / false)
      • 有 一个 满足 - true
      • 都不 满足 - false
    • 代码展示:
      const arr = [1, 2, 3, 4, 5];
      const value = arr.some(item => item >= 3);
      console.log(value);		// true
      
  • 8️⃣ ✔every()
    • 描述: 判断 数组 里面的 每一个元素 是不是都 满足条件
    • JavaScript - 进阶+高级(笔记)
    • 语法:
      Array.every(function (item, index[, arr]) {})
      
    • 返回值: 布尔值(true / false)
      • 都满足 - true
      • 有 一个 不满足 - false
    • 代码展示:
      const arr = [2, 26, 56, 34, 67];
      let flag = arr.every(item => item > 30);
      console.log(flag);	// false
      
  • 9️⃣ concat() - 合并数组
    • 描述: 合并 两个或多个数组。此方法不会更改现有数组,而是返回一个 新数组
    • 语法:
      const new_array = oldArr.concat(value1[, value2[, ...[, valueN]]])
      
    • 参数:
      • 将参数按顺序追加到数组末尾
    • 返回值: 合并之后新数组
    • 代码展示:
      // 都是数组
      const arr = [1, 2, 3];
      const arr1 = [4, 5, 6];
      const new_array = arr.concat(arr1);
      console.log(new_array);	// [1, 2, 3, 4, 5, 6]
      // 参数不是数组
      const num = 8;
      const new_array = arr.concat(num);
      console.log(new_array);	// [1, 2, 3, 8]
      // 参数是对象
      const obj = {uname: '奥特曼', age: 22};
      const new_array = arr.concat(obj);
      console.log(new_array);	// [1, 2, 3, { uname: "奥特曼", age: 22 }]
      
  • 🔟 slice() - 提取元素
    • 描述: 获取指定的元素
    • 语法:
      Array.slice(开始索引, 结束索引)	
      
      • 参数:
        • 包前不包后
        • 第一个参数不写: 头 ➡ 指定位置
        • 第二个参数不写: 指定位置 ➡ 尾
        • 参数可以是一个负数 -> length + 负数
    • 返回值: 一个含有被提取元素的 新数组
    • 代码展示:
      const arr = [1, 2, 3, 4, 5, 6];
      const newArr = arr.slice(2, 4);		// [3, 4]
      // 第一个参数不写
      const newArr = arr.slice(4);			//  [5, 6]
      // 第二个参数不写
      const newArr = arr.slice(2);			// [3, 4, 5, 6]
      // 参数是负数
      const newArr = arr.slice(2, -2);	//  [3, 4]
      // 不写参数
      const newArr = arr.slice();				// [1, 2, 3, 4, 5, 6]
      console.log(newArr);	
      
  • 数组降维(数组扁平化)
    arr.flat(几维数组);
    arr.flat(Infinity);		// 一劳永逸,不管几维数组都能降到一维数组
    
  • 正向 查看数组里面 指定这个 数据的索引
    Array.indexOf(数据)
    Array.indexOf(数据, 开始索引)	从哪个索引开始向后查找
    返回值: 如果有这个数据,是第一个满足条件的数据的索引
    		如果没有这个数据,就是 -1
    
  • 反向 查看数组里面 指定数据 的索引
    Array.lastIndexOf(数据)
    Array.lastIndexOf(数据, 开始索引)
    注意: 虽然是反向查找,但是索引还是正常索引
    
  • 使用 数组里面的内容 替换 数组里面的内容
    Array.copyWithin(目标位置, 开始索引, 结束索引)
    	->目标位置:当你替换内容的时候,从哪一个索引位置开始替换
        ->开始索引:数组哪一个索引位置开始当作替换内容,默认是 0 
        ->结束索引:数组哪一个索引位置结束当作替换内容,默认是 结尾
    返回值: 是一个新数组(替换后的数组)
    
  • 使用 指定数据区 填充 数组
    • JavaScript - 进阶+高级(笔记)
    Array.fill(要填充的数据, 开始索引, 结束索引)
    前提: 数组要有 length
    返回值: 填充好的数组
    
  • 查看 数组中是不是有 某一个数据
    Array.includes(数据)
    返回值: 一个布尔值
          有这个数据就是 true
          没有这个数据就是 false
    
  • 根据条件找到数组里面满足条件的数据的索引
    • JavaScript - 进阶+高级(笔记)
    Array.findIndex(function (item) {} )
    返回值: 找到满足条件的第一个元素的索引
    
JavaScript - 进阶+高级(笔记)
5.2 伪数组 ➡ 真数组
  • 静态方法:
    • 语法:
      Array.from()
      
    • 代码展示:
    // Array.from(伪数组):将伪数组转换为真数组
    // 返回值:真数组
    const arr = Array.from(document.querySelectorAll('li'));
    arr.pop();
    console.log(arr);
    
    JavaScript - 进阶+高级(笔记)
  • 伪数组(对象)
    • 有索引值
    • 有length属性
    • 索引值 并且 有 length属性对象 就是 伪数组
    • const obj = {
        0: 'zs',
        1: 20,
        2: '小妹',
        length: 3
      }
      console.log(Array.from(obj));	// ['zs', 20, '小妹']
      

5.3 String

  • ❌ 字符串的创建
    字面量创建 let str = 'hello world'
    内置构造函数创建 let str = new String('hello world')
    
5.3.1 字符串方法
  • 都是 实例方法
  • JavaScript - 进阶+高级(笔记)
  • 注意: 所有字符串方法不会改变 原始字符串
  • 1️⃣ ✔ substring() - 截取字符串
    • 语法:
      str.substring(indexStart[, indexEnd])
      
    • 参数:
      • indexStart = indexEnd ➡ 返回一个 空字符串
      • 省略indexEnd ➡ 提取字符 一直字符串末尾
      • 如果任一 参数小于0或为NaN,则被 当作 0
      • 如果任一 参数大与 string Name.length,则被当作 stringName.length
      • 如果 indexStart 大与 indexEnd,则substring 的执行结果就像两个参数调换了一样。
    • 返回值: 指定部分 的 新字符串 (截取的部分)
    • 注意: 包前 不包后
    • 代码展示:
      // 截取字符串
      // str.substring(indexStart[, indexEnd]);
      // 返回截取的部分
      const str = '迪迦奥特曼,塞罗奥特曼,银河奥特曼,艾克斯奥特曼';
      // 正常索引值(包前不包后)
      const newStr = str.substring(4, 9); // 曼,塞罗奥
      // 一个参数
      const newStr = str.substring(6);  // 塞罗奥特曼,银河奥特曼,艾克斯奥特曼
      // indexStart = indexEnd
      const newStr = str.substring(6, 6); // 空字符串
      // 参数小于0或者为NaN
      const newStr = str.substring(-9); // 迪迦奥特曼,塞罗奥特曼,银河奥特曼,艾克斯奥特曼
      const newStr = str.substring(NaN); // 迪迦奥特曼,塞罗奥特曼,银河奥特曼,艾克斯奥特曼
      // 参数大与stringName.length
      const newStr = str.substring(100);	// 空字符串
      // indexStart>indexEnd
      const newStr = str.substring(9, 4); // 曼,塞罗奥
      console.log(newStr);
      
    • ❌ 拓展:也是截取字符串的方法
      • substr()
      • slice()(弃用)
  • 2️⃣ ✔ split() - 将 字符串 拆分成 数组 (分割)
    • 字符串中截取从开始下标到的指定数目的字符
    • 语法:
      str.split(分隔符)
      
    • 参数:
      • 字符串里面用的什么分隔符参数就是什么
      • 没有参数 / 别的字符: 数组里面的 元素 就是 字符串本身
      • 空字符串: 将字符串的每一个 字母数组文字 或 标点符号 都作为一个 数组元素
    • 返回值: 数组
    • 注意: 和 数组的join方法 相反
    • 代码展示:
      // String.split():将字符串转换成数组
      // 字符串用什么分隔参数就是什么
      const str = '迪迦奥特曼,塞罗奥特曼,银河奥特曼,艾克斯奥特曼';
      const arr = str.split(',');
      console.log(arr); // ['迪迦奥特曼', '塞罗奥特曼', '银河奥特曼', '艾克斯奥特曼']
      const str1 = '2022-8-16';
      const arr1 = str1.split('-');
      console.log(arr1);  // ['2022', '8', '16']
      
    • 3️⃣ ✔ startsWith() - 检测是否以某个(某段)字符开头
      • 语法:
        string.startsWith(检测字符串[, 检测位置索引号])
        
      • 参数:
        • 检测索引位置:默认 是以 0 开始
      • 返回值: 布尔值(true / false)
      • 注意:
        • 包前不包后
      • 代码展示:
        // 检测是否以某个(某段)字符开头
        // str.startsWith(检测字符串[, 检测索引的位置])
        // 返回值:true / false
        const str = '迪迦奥特曼,赛罗奥特曼,银河奥特曼,艾克斯奥特曼';
        // const flag = str.startsWith('迪迦奥特曼'); // true
        const flag = str.startsWith('赛罗奥特曼', 6); // true
        console.log(flag);
        
  • 4️⃣ ✔ includes() - 判断一个字符串是否包含在另一个字符串中(返回true / false)(includes - 包含)
    • 语法:
      str.includes(搜索的字符串[, 检测索引位置])
      
    • 返回值: true / false
    • 代码展示:
      // .str.includes(搜索的字符串[, 检测位置的索引]):判断一个字符串是否包含在另一个字符串中
      // 返回值:true / false
      const str = '迪迦奥特曼,赛罗奥特曼,银河奥特曼,艾克斯奥特曼';
      const flag = str.includes('赛罗奥特曼');
      console.log(flag);
      
  • 5️⃣ replace() - 替换字符串中指定的字符
    • 语法:
      字符串.replace(要替换的旧字符, 替换的新字符)
      
    • 返回值: 替换之后 的字符串
    • 注意:
      • 不改变 原始字符串
      • 默认 只替换 匹配到的 第一个字符串
      • 可以 配合 正则表达式(g 和 i) 来 全局匹配 并且 不区分大小写
    • 代码展示:
      let str = '赛文奥特曼真好看,并且赛文奥特曼真厉害!';
      const newStr = str.replace(/赛文/g, '迪迦');
      console.log(newStr);	// 迪迦奥特曼真好看,并且迪迦奥特曼真厉害!
      
JavaScript - 进阶+高级(笔记)

5.4 Number

  • Number内置构造函数,用于创建数值
  • toFixed(保留几位小数) - 保留小数位的长度
  • 具有 四舍五入 功能
  • ⚠🔻 使用该方法之后数字会转换为 字符串
    const price = 12.345;
    const price1 = 12.921;
    // 没有参数
    price.toFixed();	// 12	(string)
    price1.toFixed();	// 13	(string)
    // 有参数
    price.toFixed(2);	// 12.35	(string)
    price1.toFixed(2);	// 12.92	(string)
    

六、编程思想

6.1 面向过程变成

  • 面向过程: 就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次调用就可以了
  • 优点:
    • 性能 比面向对象 ,适合跟硬件联系很紧密的东西,例如单片机
  • 缺点:
    • 没有面向对象易维护、易复用、易扩展

6.2 面向对象编程 (oop)

  • 面向对象: 是把事务分解成为一个个对象,然后由对象之间分工与合作。
    • 面向对象是以 功能 来划分问题的
  • 特性:
    • 封装性
    • 继承性
    • 多态性
  • 优点:
    • 易维护、易复用、易扩展,基于面向对象封装、继承、多态性的特性,可以设计出低耦合的系统,使系统 更加灵活、更加易于维护
  • 缺点:
    • 性能比面向过程低

七、构造函数

  • 构造函数 体现了 面向对象封装特性
  • 构造函数 实例创建 的 对象 彼此独立、互不影响
  • 构造函数存在的问题:浪费内存(可以用原型解决)
  • 代码展示:
    // 构造函数:公共的属性和方法封装到构造函数里面
    function Star(uname, age, gender) {
      this.uname = uname;
      this.age = age;
      this.gender = gender;
      this.sing = function () { console.log('构造函数'); }
    }
    const dj = new Star('迪迦', 22, '男');
    const sl = new Star('赛罗', 23, '女');
    // 通过构造函数创建的对象彼此独立,互不影响
    console.log(dj === sl); // false
    console.log(dj.sing === sl.sing); // false
    // dj 和 sl 的地址不同,所指向的function也不同
    
    JavaScript - 进阶+高级(笔记)

八、❗❗❗ 原型

8.1 原型

  • 原型
    • 是一个 对象,我们称 prototype原型对象
    • 跟随函数一起创建的对象
  • 构造函数 通过 原型 分配的 方法 是 所有对象 所 共享的
  • js规定,每一个 构造函数 都有一个 prototype 属性,指向 另一个对象,所以我们称为 原型对象
  • 这个原型对象可以挂载函数,对象实例化不会多次创建原型上函数,节约内存
    • 可以把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法
  • 作用: 为当前函数创建的实例添加公共的属性和方法(把那些不变的方法挂载在prototype上)
  • ⚠🔺 构造函数原型对象 中的 this 都指向 实例对象
    • 原型上的方法,谁调用就指向谁(实例对象 + 原型自己)
  • 有函数 就有 原型对象
  • 代码展示:
    // 公共的属性写到 构造函数 里面
    function Star(uname, age, gender) {
      this.uname = uname;
      this.age = age;
      this.gender = gender;
    }
    const Dj = new Star('迪迦', 22, '男');
    const Sl = new Star('赛罗', 23, '女');
    // 每个构造函数都有一个prototype(原型)属性,
    // console.dir(Star.prototype);
    // 公共的方法写在原型对象上
    Star.prototype.sing = function () { console.log('原型-prototype'); }
    console.log(Dj.sing === Sl.sing); // true
    // 原型也可以调用自己身上的方法
    Star.prototype.sing(); // this -> Star.prototype
    
    JavaScript - 进阶+高级(笔记)
    // 封装一个求数组最值和和的方法(只要是数字数组就可以使用)
    const arr = [6, 3, 8, 2, 9, 0];
    // const arr = new Array(6, 3, 8, 2, 9);
    Array.prototypeGgetValue = function () {
      return [Math.max(...this), Math.min(...this), this.reduce((prev, item) => prev + item)]
    }
    console.log(arr.GetValue());	// [9, 0, 28]
    

8.2 constructor属性

  • 每个 原型对象 里面都有 constructor 属性(constructor ➡ 构造函数)
  • 作用: 该属性 指向 该原型对象构造函数
    • 简单理解:指向我的爸爸,我是个有爸爸的孩子
  • ⚠🔺 每个 构造函数 都有 prototype属性 ,每个 prototype属性 都有 constructor属性
    • JavaScript - 进阶+高级(笔记)
    • JavaScript - 进阶+高级(笔记)
  • 使用场景:
    • 如果有 多个对象方法,我们可以 原型对象 采用 对象形式 赋值单个方法是追加
    • 但是这样就会 覆盖 构造函数 原型对象 原来内容,这样 修改后原型对象constructor属性不再 指向 当前 构造函数
    • 此时,我们可以在 修改后原型对象 中,添加一个 constructor属性 指向 原来的构造函数
    • 代码展示:
      • 原来的prototype:JavaScript - 进阶+高级(笔记)
      • JavaScript - 进阶+高级(笔记)

8.3 对象原型

  • 对象 都会有一个 __proto__ 指向 构造函数prototype 原型对象,之所以我们对象可以使用构造函数 prototype 原型对象的属性和方法,就是因为对象有 __proto__ 原型的存在
    • 注意:
      • __proto__ 是一个 🔺只读属性🔺 (前后两个杠)
      • Chrome 中 [[prototype]] 和 __proto__ 意义相同
      • 用来表示当前实例对象指向哪个原型对象prototype
      • __proto__ 对象原型 里面也有一个 constructor属性指向 创建 该实例对象 的 构造函数
    • JavaScript - 进阶+高级(笔记)
    • JavaScript - 进阶+高级(笔记)
  • ⚠🔺🔺 注意: (混淆点)
    • 每个 构造函数 都有 prototype(原型对象)
    • 每个 prototype(原型对象) 都有 constructor,指向 该原型对象构造函数
    • 每个 对象 都有 __proto__(对象原型)(属性),指向 构造函数原型对象
    • 每个 对象原型 都有 constructor 属性,指向 创建 该实例构造函数
      JavaScript - 进阶+高级(笔记)
  • 对象都会有一个属性 __proto__ 指向构造函数的prototype原型对象,之所以对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有___proto__
  • 🔺🔺⚠⚠总结:
    • 1️⃣ prototype 是什么?哪里来的
      • 原型(原型对象)
      • 构造函数 都 自动有 原型
    • 2️⃣ constructor属性 在哪里?作用是啥?
      • prototype原型对象原型 __proto__ 里面 都有
      • 都 ➡ 创建 原型 / 实例对象构造函数
    • 3️⃣ __proto__ 属性在哪里?指向谁?
      • 实例对象 里面
      • 原型对象 prototype

8.4 原型继承

  • JS中大多是 借助 原型对象 实现 继承的特性
    • 父构造函数(父类)
    • 子构造函数(子类)
    • 子类的原型 = new 父类
  • 使用构造函数实现继承:
    • 类:创建一类对象的模板
    • 原型链继承:利用原型链的关系,实现继承的效果
      • 在ES6之前,没有继承的语法,可以使用call和原型链来完成继承的效果,子构造函数继承父构造函数
      • 1、子构造函数 创建的实例 拥有 父构造函数 创建的实例 相同 属性结构(call借用父构造函数的代码,修改其中this指向为子构造函数的实例)
      • 2、子构造函数 创建的实例 可以调用 父构造函数 原型对象 的 公共成员(把 子构造函数 的原型对象 的 原型对象 变成,父构造函数的原型对象)

8.5 ❗❗ 原型链

  • 基于 原型对象继承 使得 不同构造函数原型对象 联系在一起,并且这种 关联关系 是一种 链状结构,我们将 原型对象链状结构关系 称为 原型链
    • 从实例出发,以原型对象为连接,形成的一个链式的结构
  • 由原型对象的关系相连,形成的链式结构
    • 实例成员调用的时候,优先调用自身的成员,如果自身没有,继续调用原型对象的成员,如果原型对象也没有,就调用原型对象的原型对象的成员
  • 原型链的作用: 实例调用成员的查找顺序
  • 原型链主要指的是__proto__
  • JavaScript - 进阶+高级(笔记)
  • JavaScript - 进阶+高级(笔记)
  • 🔺🔺⚠⚠总结:
    • 只要是 对象 就有 __proto__ ,指向 原型对象
    • 只要是 原型对象 里面就有 constructor,指向 创建 该原型对象 的 构造函数
    • 实例对象 和 原型对象本身 可以 访问 定义在原型身上的方法或属性
      • 构造函数不行(中间隔着prototype)
      • 构造函数访问不了原型身上的属性或方法,因为中间隔着 prototype
      • 实例对象可以访问原型对象的原型对象身上的属性和方法,中间虽然隔着东西,但能直接访问,因为这是规则
  • 原型链-查找规则
    • 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性
    • 如果没有就查找它的原型(也就是__proto__指向的prototype原型对象)
    • 如果还没有就查找原型对象的原型(Object的原型对象)
    • 依此类推一直找到Object为止(如果 Object.prototype 也没有得到的就是 )
    • __proto__对象原型的意义在于为对象成员查找机制提供一个方向,或者说一条路线
    • 可以使用 instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
      JavaScript - 进阶+高级(笔记)

8.6 instanceof

  • instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
    • 简单理解:
      • 判断 当前构造函数的原型对象 是否出现在 实例的原型链上
      • 检测 实例对象 是否 由 该构造函数 创建 出来的
  • 描述:
    • instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上
  • 语法:
    object instanceof constructor
    // object 属于 constructor 吗?
    // 实例对象 instanceof 构造函数
    
  • 参数:
    • object - 实例对象
    • constructor - 构造函数
  • 代码展示:
    // 定义构造函数
    function C(){}
    function D(){}
    
    var o = new C();
    o instanceof C; // true,因为 Object.getPrototypeOf(o) === C.prototype
    
    o instanceof D; // false,因为 D.prototype 不在 o 的原型链上
    
    o instanceof Object; // true,因为 Object.prototype.isPrototypeOf(o) 返回 true
    C.prototype instanceof Object // true,同上
    
    C.prototype = {};
    var o2 = new C();
    
    o2 instanceof C; // true
    
    o instanceof C; // false,C.prototype 指向了一个空对象,这个空对象不在 o 的原型链上。
    
    D.prototype = new C(); // 继承
    var o3 = new D();
    o3 instanceof D; // true
    o3 instanceof C; // true 因为 C.prototype 现在在 o3 的原型链上
    

肆、❗❗ JavaScript高级

一、深浅拷贝

  • ❗❗ 面试重点
  • 开发中我们经常需要复制一个对象。如果直接用赋值会有下面问题:
    • 变量之间相互赋值的时候,传递的是栈中的值
    • 复杂数据类型 栈中存放的是数据地址,真正的数据存放在堆里面
  • JavaScript - 进阶+高级(笔记)
  • 浅拷贝深拷贝 只针对 引用类型

1.1 浅拷贝

  • 浅拷贝: 拷贝的是 地址
  • 常见方法:
    • 拷贝对象:
      • 1️⃣ Object.assgin() 或者 {...obj}
      • 2️⃣ 使用 for - in 遍历原对象,逐个拷贝属性和属性值
    • 拷贝数组:
      • Array.prototype.concat() 或者 [...arr]
      • 遍历原数组,逐个拷贝元素
  • 浅拷贝存在的问题:
    • 只能拷贝 一层 对象或数组
  • 代码展示:
    // 拷贝对象
    const pink = {
      uname: 'pink老师',
      age: 18
    };
    // 1.展开运算符 - 将pink的属性展开从新加入到这个新对象里
    // const copyPink = { ...pink };
    // 2.静态方法-拷贝对象
    const copyPink = {};
    Object.assign(copyPink, pink);
    console.log(copyPink);  // {uname: 'pink老师', age: 18}
    copyPink.uname = '刘德华';
    console.log(copyPink);  // {uname: '刘德华', age: 18}
    console.log(pink);      // {uname: 'pink老师', age: 18}
    
    // 拷贝数组
    const arr = ['迪迦', '赛罗'];
    // 1.展开运算符
    // const copyArr = [...arr];
    // 2.合并数组的方法
    const copyArr = Array.prototype.concat(arr);
    console.log(copyArr); // ['迪迦', '赛罗']
    copyArr[0] = '迪迦奥特曼';
    console.log(copyArr); // ['迪迦奥特曼', '赛罗']
    console.log(arr);     // ['迪迦', '赛罗']
    
    // 问题
    const obj1 = {
      uname: 'pink老师',
      age: 18,
      family: {
        baby: '小pink'
      }
    };
    const obj2 = { ...obj1 };
    const obj3 = {};
    Object.assign(obj3, obj1);
    obj2.family.baby = '老pink';
    console.log(obj 1, obj2, obj3);
    // {uname: 'pink老师', age: 18, family: { baby: '老pink' }}
    // {uname: 'pink老师', age: 18, family: { baby: '老pink' }}
    // {uname: 'pink老师', age: 18, family: { baby: '老pink' }} 
    
  • 直接赋值 和 浅拷贝 有什么区别?
    • 直接赋值:只要是对象,都会相互影响,因为是将栈里面的地址赋值给变量
    • 浅拷贝:如果只拷贝一层对象,不相互影响,如果出现多层对象拷贝还是会相会影响
  • 浅拷贝怎么理解?
    • 拷贝对象之后,里面的 属性值 是 简单数据类型 直接 拷贝值
    • 如果 属性值 是 引用数据类型 则 拷贝 地址

1.2 ❗❗ 深拷贝

  • 深拷贝: 拷贝的是 对象 ,不是地址
  • 🔺🔺❗❗❗面试必考题
  • 常用方法:
    • 1️⃣ 通过 函数递归 实现深拷贝
      • 递归函数介绍: 如果一个函数在 内部 可以 调用自身,那么这个函数就是递归函数
        • 函数内部自己调用自己
        • 递归函数的作用和循环类似
        • ⚠ 由于 递归 很容易 发生 “栈溢出” 错误,所以 必须加 退出条件 return
        • 递归嵌套太深出现以下错误
          Uncaught RangeError: Maximum call stack size exceeded
          超出最大调用堆栈大小
          
          JavaScript - 进阶+高级(笔记)
        • 代码展示:
          // 利用递归函数实现 setTimeout 模拟 setInterval效果
          function fn() {
            console.log(new Date());
            setTimeout(fn, 1000);
          }
          fn();
          
      • 实现深拷贝:
        const oldObj = {
          uname: '迪迦',
          age: 22,
          hobby: ['打怪兽', '晒日光浴', '揍银河'],
          family: {
            wife: '卡尔蜜拉',
            baby: '小迪迦'
          }
        };
        
        const newObj = {};
        // 使用递归函数实现深拷贝
        function deepCopy(newObj, oldObj) {
          // 1.遍历对象
          for (let k in oldObj) {
            // 1.处理 属性值 = 数组
            if (oldObj[k] instanceof Array) {
              newObj[k] = [];
              deepCopy(newObj[k], oldObj[k]);
            }
            // 2.处理 属性值 = 对象
            else if (oldObj[k] instanceof Object) {
              newObj[k] = {};
              deepCopy(newObj[k], oldObj[k]);
            }
            // 3.对于简单数据类型直接进行赋值操作
            // 新对象.旧对象属性名 = 旧对象属性值
            else {
              newObj[k] = oldObj[k];
            }
          }
        }
        deepCopy(newObj, oldObj);
        newObj.family.twoWife = '丽娜';
        console.log('旧对象:', oldObj);
        console.log('新对象:', newObj);
        // 旧对象:
        // {
        //   uname: '迪迦',
        //   age: 22,
        //   hobby: ['打怪兽', '晒日光浴', '揍银河'],
        //   family: {wife: '卡尔蜜拉', baby: '小迪迦'}
        // }
        // 新对象:
        // {
        //   uname: '迪迦',
        //   age: 22,
        //   hobby: ['打怪兽', '晒日光浴', '揍银河'],
        //   family: {wife: '卡尔蜜拉', baby: '小迪迦', twoWife: '丽娜'}
        // }
        
      • ❗❗ 面试总结: 用递归函数实现过深拷贝吗?
        • 1️⃣ 深拷贝:新对象不会影响旧对象
        • 2️⃣ 实现深拷贝需要用到函数递归
        • 3️⃣ 普通拷贝直接进行赋值操作
          • 如果遇到数组,再次调用递归函数解决数组
          • 如果遇到函数,直接进行赋值操作(函数就是为了实现代码的复用)
          • 如果遇到对象,再次调用递归函数解决对象
          • ⚠ 先解决数组或函数 再解决对象(数组和函数也属于对象)
    • 2️⃣ lodash里面的cloneDeep (JS库)
      • 使用之前要先引入 lodash.js
      • 语法: const newArr / newObj = _.cloneDeep(oldArr / oldObj)
      • 返回值:拷贝完的对象
    • 3️⃣ 通过 JSON.stringify() 实现
      • 语法:
        JSON.parse(JSON.stringify(Obj))
        
      • 原理:
        • 1️⃣ 将 对象 转换成 JSON字符串 ,基本数据类型
        • 2️⃣ 然后再转换为复杂数据类型,再从堆里面开辟一个新的空间,前后两个对象没有任何关系
      • 缺点: (面试)
        • 并不能转换所有的值
        • 不识别 undefined 和 函数

二、异常处理

  • 提升代码的健壮性

2.1 throw抛异常

  • 基本语法:
    throw new Error('要抛出的信息')
    
  • 总结:
    • throw 抛出异常信息,程序也会 终止执行
    • throw后面跟的是错误提示信息
    • Error对象配合throw使用,能够设置更详细的错误信息

2.2 try / catch 捕获异常

  • 捕获错误信息(浏览器提供的)
    • try - 尝试(可能发生错误的代码)
    • catch - 拦住
    • finally - 最后(不管程序是否正确,一定会执行的里面的代码)
    • ⚠ 不会自动中断程序(想要中断需要加return)
  • 总结:
    • try...catch用于捕获错误信息
    • 将预估可能发生错误的代码写在try代码段中
    • 如果try代码段中出现错误后,会执行catch代码段,并截取到错误信息
    • finally不管是否有错误,都会执行
  • 代码展示:
    // 不一定要写在函数里面
    function fn() {
      try {
        const body = document.body;
        body.style.backgroundColor = 'purple';
      } catch(err) {
        // 拦截错误,提示浏览器提供的额错误信息,但是不中断程序
        // message:必须写(属性:错误的详细信息),err是自定义的
        // console.log(err.message);
        // 要想中断程序必须加return
        // return
    
        // 还不如直接写throw
        throw new Error('选择器错误');
      } finally {
        alert('不管程序是否正确,一定会执行的代码');
      }
    }
    fn();
    

2.3 debugger

  • 类似与打断点
  • 调式的时候使用
  • 使用:直接添加到代码对应位置

三、处理this

3.1 this指向

  • 普通函数this
    • 谁调用指向谁
    • 🔺 独立的普通函数(不归属于任何对象)this指向window
      • 代码展示:
        let obj = {
          name: '阿茶',
          hi: function() {
            console.log(this);	// this -> obj
            let fun = () => {
              console.log(this);	// this -> obj
              let fn = () => {
                console.log(this);	// this -> obj
                let count = function () {
                  console.log(this);	// this -> window
                  let amount = () => {
                    console.log(this);	// this -> window
                  }
                  amount();
                }
                count();
              }
              fn();
            }
            fun();
          }
        };
        obj.hi();
        
    • 普通函数 没有 明确调用者 时this指向window
    • ❌ 严格模式下没有调用者时this指向undefined,严格模式写在当前作用域或全局作用域的最前面 ('user strict'
  • 箭头函数的this
    • 箭头函数中的this与普通函数完全不同,也不受调用方式的影响,箭头函数不存在this
    • 箭头函数自己不会创建this,它只会沿用本体所在作用域的上层作用域的this
    • 不适用: 构造函数,原型函数,dom事件函数
      定义: this 是一个使用在 作用域 内部的 关键字(包括函数作用域 和 全局作用域)

3.2 ❗❗ 改变this

  • 1️⃣ call()
    • 使用call方法调用函数,同时 改变 被调用函数 中this的值
    • 语法:
      函数名.call(thisArg, arg1, arg2, ...)
      
    • 参数:
      • thisArg:在函数运行时指定this
      • arg1, arg2:函数的实参
      • ⚠ 注意:如果写入一个简单数据类型替换this,会自动使用 基本包装数据类型简单数据 转换为 复杂数据类型
    • 返回值: 函数的返回值,因为它就是在调用函数
    • 特点:
      • 立即调用 函数(不适合用作 定时器处理函数 和 事件处理函数 )
    • 作用:
      • 调用函数
      • 改变 this 指向
    • 应用场景: 伪数组借用数组方法
    • 代码展示:⬇
  • 2️⃣ ✔ apply()
    • 语法:
      函数名.apply(thisArg[, argsArray]);
      
    • 参数:
      • thisarg:在函数运行时指定的this的值(this指向谁,不改变指向使用null
      • argsArray:函数的实参,必须包含在 数组 里面
    • 返回值: 函数的返回值,因为它就是在调用函数
    • 特点:
      • 立即调用 函数(不适合用作 定时器处理函数 和 事件处理函数 )
    • 作用: 可以以数组的形式给某些 功能函数 传参
    • 使用场景: 求数组的最值(1.for循环遍历 2.展开运算符 3.apply)
    • 代码展示:⬇
      const obj = {uname: '迪迦', age: 22};
      function fun(x, y) { 
        console.log(this); 
        console.log(x + y);
      }
      fun.apply(obj, [3, 4]);	// obj, 7
      
      const max = Math.max.apply(Math, [34, 65, 12, 30]);	// 65
      const min = Math.min.apply(Math, [34, 65, 12, 30]);	// 12
      
    • call 和 apply 的区别:
      • 都是调用函数,都能改变this指向
      • 参数不一样
        • call 传递的是多个值
        • apply 是以数组的形式给函数传递参数
  • 3️⃣ ✔🔺bind()
    • bind()方法 不会调用函数 ,但是能改变函数内部this指向
    • 语法:
      函数名.bind(thisArg, arg1, arg2, ...)
      
    • 参数:
      • thisArg:this指向谁
      • arg1, arg2, …:给函数传递参数
    • 返回值:
      • 由指定的this值和初始化参数改造的 原函数拷贝(新函数)
      • 会返回一个新的函数,一个已经改变好 this 指向的函数
    • 特点: 不会调用函数
    • 只想改变this指向,并不想立即调用函数的时候,可以使用bind(定时器内部的this,注册事件)
    • 代码展示:⬇
  • 代码展示:
    const obj = { uname: '迪迦', age: 22 };
    function fun(x, y) {
      console.log(this);
      console.log(x + y);
    }
    // 1、call()方法:
    // 语法:fun.call(thisArg[, arg1[, arg2 [, ...]]]);
    // 参数:
    //  1)要指向谁
    //  2)多个参数,用来给函数传递实参
    // 返回值:函数的返回值(call本质上就是在调用函数)
    fun.call(obj, 3, 4);  // obj, 7
    
    // 2.apply()方法:
    // 语法:fun.apply(thisArg[, [arg1, arg2, ...]])
    // 参数:
    //  thisArg:this指向谁
    //  第二个参数是 数组 ,用来给函数传递实参
    // 返回值:函数的返回值(apply本质上就是在调用函数)
    // 使用场景:求数组的最值 (1:for循环遍历 2:展开运算符 3:apply)
    fun.apply(obj, [4, 5]); //  obj, 9
    // 最值
    const arr = [23, 67, 10, 89, 20];
    console.log(Math.max.apply(Math, arr)); // 89
    console.log(Math.min.apply(Math, arr)); // 10
    
    // 3.bind()方法:
    // 语法:fun.bind(thisArg, arg1, arg2, ...)
    // 参数:
    //  thisArg:this指向谁
    //  arg1, arg2:用来给函数传递参数
    // 返回值:一个已经改变好this指向的函数(和原函数一样,除了this指向)
    // 特点:不立即调用函数
    const fn = fun.bind(obj, 4, 6);
    console.log(fn);
    fn();
    // bind应用
    const btn = document.querySelector('button');
    btn.addEventListener('click', function () {
      this.disabled = true;
      setTimeout(function () {
        this.disabled = false;
      }.bind(this), 2000);
    });
    

3.3 小结

  • 相同点:
    • 都可以改变函数内部的 this 指向
  • 区别点:
    • callapply立即调用函数,并且改变函数内部的this指向
    • callapply 传递的 参数不一样call传递参数 arg1, arg2, ...形式,apply 必须数组 形式 [arg]
    • bind 不调用函数,可以改变函数内部的this指向
  • 主要应用场景:
    • call 调用函数 并且可以传递参数
    • apply 经常跟 数组 有关,比如借助于数学对象实现数组最值
    • bind 不调用函数,但是还想改变this指向,比如改变 定时器内部的this指向

四、性能优化

4.1 防抖

  • 防抖(debounce)
    • 事件发生一段时间再执行,如果这段时间内继续触发新的事件,那么取消之前的事件,只执行最新的事件;
    • 延迟执行;
  • 使用场景:
    • 搜索框(定时器)
    • 代码展示:
      <input type="text">
      <script>
          const input = document.querySelector('input');
          let timerId = null;
          input.addEventListener('input', () => {
              // 先清除之前的定时器
              clearTimeout(timerId);
              // 在开启当前的定时器
              timerId = setTimeout(() => {
                  console.log(11);
              }, 500);
          });
      </script>
      

4.2 节流

  • 节流(throttle):
    • 一段时间内只执行一次事件,执行结束后才能继续执行新的事件。(限制任务执行频率的一种手段);
    • 节流是一种手段,来限制任务执行频率,规则是当前任务执行结束之前,不接受新任务,直到当前任务结束,才开始执行下一个任务;
  • 使用场景:
    • 轮播图点击效果、鼠标移动、页面尺寸缩放(resize)、滚动条滚动
  • 实现节流:
    • 起始时间 - 当前时间
  • 代码展示:
    <style>
      * {
         margin: 0;
         padding: 0;
       }
    
      .box {
         width: 100px;
         height: 100px;
         background-color: red;
         margin-top: 30px;
         transition: all 1s linear;
       }
    </style>
    
    <body>
      <button>按钮</button>
      <div class="box"></div>
      <script>
      const btn = document.querySelector('button');
          const box = btn.nextElementSibling;
          // 声明变量:作为box的初始宽度
          let width = 100;
          // 声明变量:节流阀
          let flag = true;
          // 事件侦听
          btn.addEventListener('click', () => {
              if (flag) {
                  flag = false;
                  width += 100;
                  box.style.width = width + 'px';
              }
          });
          // 拓展:transitionend - CSS过渡完触发事件
          box.addEventListener('transitionend', () => {
              flag = true;
          });
      </script>
    </body>
    
  • ❗❗❗ 面试 节流 和 防抖 的区别?
    • 节流: 一段时间内只执行一次事件,执行结束后才能继续执行新的事件。(限制任务执行频率的一种手段);
      • 是一种手段,用来限制任务的执行频率,规则是当前任务结束之前,不会接收新任务,直到当前任务结束,才会执行新任务
      • 采用两个时间(事件开始执行时间 - 事件执行结束时间)相减的方式实现,如果相减结果大与指定时间就调用函数
    • 防抖: 事件发生一段时间再执行,如果这段时间内继续触发新的事件,那么取消之前的事件,只执行最新的事件;
      • 定时器方式实现:在事件触发过程中一直清除定时器,当事件在一定时间内不触发就调用函数
    • 使用场景:
      • 节流:轮播图点击切换按钮、鼠标移动、页面尺寸缩放、滚动条滚动
      • 防抖:搜索框
  • 案例拓展:
    • timeupdate 事件在 视频 / 音频 当前的播放位置 发生改变 时触发
    • loadeddata 事件在 当前帧的数据加载完成 且还 没有足够的数据 播放视频 / 音频的下一帧 时触发
    • currentTime 可读写 属性:获取 当前 视频 / 音频时间
    • transitionend 事件在CSS完成过渡后触发

到了这里,关于JavaScript - 进阶+高级(笔记)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Java笔记+踩坑汇总】Java基础+进阶+JavaWeb+SSM+SpringBoot+瑞吉外卖+SpringCloud+黑马旅游+谷粒商城+学成在线+MySQL高级篇+设计模式+常见面试题+源码

    本文是“Java学习路线”专栏的导航文章,目标是为Java工程师提供一套 完整的Java学习路线 。 目录 0.摘要/资料/代码整理 1.Java基础+进阶 2.MySQL,JavaWeb,Mybatis,前端 3.Git 4.SSM(Spring,SpringMVC,Mybatis)框架 5.Maven高级 6.Springboot,MybatisPlus,JPA框架 7.瑞吉外卖、Redis、Nginx、Linux、mysql主从复制

    2024年02月06日
    浏览(69)
  • 【Java笔记+踩坑汇总】Java基础+进阶+JavaWeb+SSM+SpringBoot+瑞吉外卖+SpringCloud+黑马旅游+谷粒商城+学成在线+MySQL高级篇+设计模式+面试题汇总+源码

    本文是“Java学习路线”专栏的导航文章,目标是为Java工程师提供一套 完整的Java学习路线 。 目录 0.摘要/资料/代码整理 1.Java基础+进阶 2.MySQL,JavaWeb,Mybatis,前端 3.Git 4.SSM(Spring,SpringMVC,Mybatis)框架 5.Maven高级 6.Springboot,MybatisPlus,JPA框架 7.瑞吉外卖、Redis、Nginx、Linux、mysql主从复制

    2024年02月16日
    浏览(201)
  • JavaScript高级技巧:深入探索JavaScript语言的高级特性和用法

    当我们谈论JavaScript高级技巧时,以下是一些示例来说明这些概念: 闭包(Closures): 在上面的例子中, innerFunction 是一个闭包,它可以访问外部函数 outerFunction 中的 outerVariable 变量,即使在外部函数执行结束后也可以。 高阶函数(Higher-Order Functions): 在上面的例子中,

    2024年02月08日
    浏览(49)
  • 发掘JavaScript潜力:掌握高级技巧,成为JavaScript编程大师!

    🎬 岸边的 风:个人主页  🔥 个人专栏  :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想,就是为了理想的生活 ! 众所周知, JavaScript  是一种非常流行 🔥 的编程语言,它已经成为了网页开发的必备技能。但是,在我们从事 JavaScript编程 的时候,我们却没有完全发掘和利用它的

    2024年02月14日
    浏览(47)
  • JavaScript高级:闭包与作用域

    在 JavaScript 的世界里,闭包是一个令人着迷且神秘的概念,它为我们提供了一种强大的能力,能够在函数内部捕获并保留外部作用域的变量。本文将详细解释闭包的概念与应用,带你揭开 JavaScript 的神秘面纱,通俗易懂地理解闭包的奥秘。 1. 作用域与闭包的关系 作用域是指

    2024年02月13日
    浏览(39)
  • HiveSQL高级进阶技巧

    掌握下面的技巧,你的SQL水平将有一个质的提升! 正常hive删除操作基本都是覆盖原数据; 更新也是覆盖操作; 思路1: 先通过concat函数把多列数据拼接成一个长的字符串,分割符为逗号,再通过explode函数炸裂成多行,然后使用split函数根据分隔符进行切割; 思路2:使用u

    2024年02月06日
    浏览(38)
  • 高级JavaScript。如何用JavaScript手撸一个富文本编辑器?

    要素过多建议收藏 基本的技术就是在空白 HTML 文件中嵌入一个 iframe 。通过 designMode 属性,可以将这个空白文档变成可以编辑的,实际编辑的则是 body 元素 的 HTML 。 designMode 属性有两个可能的值: \\\"off\\\" (默认值)和 \\\"on\\\" 。设置为 \\\"on\\\" 时,整个文档 都会变成可以编辑的(显示

    2024年01月21日
    浏览(49)
  • JavaScript高级(一)--V8引擎上

     浏览器渲染的原理 主流浏览器及其内核 内核 浏览器 css前缀 备注 Trident IE4-IE11 -ms 最新的Edge已转向Blink Gecko 火狐浏览器 -moz Webkit safari、旧版谷歌 -webkit Blink Google Chrome -webkit Presto opera -o 现在的opera转向了Blink 我们常说的 浏览器内核 指的就是浏览器的 排版引擎 ,排版引擎

    2024年04月15日
    浏览(35)
  • JavaScript高级二、构造函数&常用函数

    1、深入对象 (1)创建对象三种方式 利用对象字面量创建对象 利用 new Object 创建对象 利用构造函数创建对象 案例如下: (2)构造函数 **构造函数 :**是一种特殊的函数,主要用来 快速初始化 类似的 对象 构造函数语法 使用 new 调用函数的行为被称为 实例化 实例化

    2024年02月07日
    浏览(39)
  • HTML 高级进阶试题——附答案

    选择题 问题: HTML 中的 article 元素的主要目的是什么? A. 表示主要内容 B. 定义页面的主体部分 C. 包含一篇文章 问题: 在 HTML 中, data-* 属性的主要作用是什么? A. 存储元素的样式信息 B. 存储元素的自定义数据 C. 指定元素的类名 问题: 在 HTML5 中,如何嵌入音频文件?

    2024年01月25日
    浏览(59)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包