前端 - 笔记 - 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️⃣❌ 引用计数法
      • 致命缺陷:嵌套引用
      • 如果两个对象相互引用,尽管他们不再使用,垃圾回收器不会进行回收,导致内存泄漏
      • image-20220819165530801
    • 2️⃣ 标记清除法
      • image-20220819165552870

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();
    
  • 函数套函数 不一定会 产生 闭包
  • 如果 内层函数 用到 外层函数变量 这种情况就 会产生闭包
  • image-20220812214431189
  • 闭包使用注意: 闭包使用的时候,内部的变量因为被外部引用了,所以代码执行完毕不会释放内存 - 内存泄漏(引用计数法)
  • 闭包作用:
    • 实现数据的私有化
    • 外部 可以 访问 函数内部 变量
    • 允许将 函数 与其所操作的 某些数据(环境)关联起来
  • 应用场景
    • 防抖
    • 节流(定时器);
  • 基本格式:
    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应该被回收,现在没有回收,内存泄漏
      
  • ❗❗ 闭包面试:怎么理解闭包 -> 闭包的作用 -> 闭包可能引起的问题

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: '迪迦奥特曼'}
      
      image-20220813174725057
  • 注意:
    • ⚠🔺箭头函数 属于 表达式函数,因此 不存在函数提升

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️⃣ 工厂函数创建
    • image-20220822085923461
  • 4️⃣ 构造函数 创建 (自定义的构造函数)
    • image-20220822090653886
  • 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️⃣ 返回新对象
  • 代码展示:
    image-20220815113826581
image-20220815113843724

4.3 实例成员 & 静态成员

4.3.1 实例成员

  • 构造函数 创建的 对象 称为 实例对象
  • 🔺 实例对象属性方法 称为 实例成员
  • 注意:
    • 为 构造函数 传入 参数,动态创建 结构相同值不同 的 对象
    • 构造函数 创建 的 实例对象 彼此独立 互不影响

4.3.2 静态成员

  • 🔺 构造函数属性方法 被称为 静态成员
  • ⚠🔺 静态成员方法 中的 this 指向 构造函数
  • 一般 公共特征 的属性或方法 静态成员设置为静态成员
    image-20220815195529890

五、内置构造函数 - Object

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

5.1 ❌用内置构造函数创建对象

const obj = new Object({uname: '迪迦', age: 22});

5.2 常用的 静态方法

  • 静态方法: 只有 构造函数 Object 才可以 调用 (写在构造函数身上的方法)

5.2.1 Object.keys()

  • 作用
    • 获取当前对象中的 所有 可枚举的 属性名(键)
  • 语法:
    Object.keys(对象名)
    
  • 返回值:
    • 数组 (数组的元素 = 对象的属性)
  • 代码展示:⬇

5.2.2 Object.values()

  • 作用
    • 获取对象中 所有属性值
  • 语法:
    Object.values(对象名)
    
  • 返回值:
    • 数组(数组的元素 = 对象属性值)
  • 代码展示:⬇

5.2.3 Object.assign()

  • 作用
    • 用于 对象拷贝(浅拷贝)
  • 使用场景:
    • 给对象添加属性;
    • 合并对象;
  • 语法:
    Object.assign(b, a)	// 把 a 拷贝给 b
    Object.assign(拷贝者, 被拷贝者);
    
  • 代码展示:⬇

5.2.4 代码展示

const obj = { uname: '迪迦', age: 22 };

// 静态方法:只有 构造函数 Object 才可以调用
// 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: '男'}

六、内置构造函数 - Array

6.1 ❌使用Array内置构造函数创建数组

const arr = new Array();	// 创建一个空数组
const arr = new Array(5);	// 一个参数:数组的长度
const arr = new Array(1, 2, 3, 4, 5);   // 多个参数:[1, 2, 3, 4, 5]

6.2 数组方法总结 - 改变原始数组 (7)

6.2.1 push()

  • 作用
    • 一个或多个元素 追加到 数组的末尾 ,并返回 追加元素之后 数组的 length
  • 语法
    arr.push(val1, ..., valN);
    
  • 返回值
    • 追加元素 之后 数组的 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
    

6.2.2 unshift()

  • 作用
    • 一个或多个元素 插入到 数组的开头 ,并返回 插入元素之后 数组的 length
  • 语法
    arr.unshift(val1, ..., valN);
    
  • 返回值
    • 插入元素 之后 数组的 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
    

6.2.3 pop()

  • 作用
    • 删除 数组 最后一个元素,并返回 被删除的元素
    • ⚠ 会 改变 数组的 length
  • 语法
    arr.pop();
    
  • 返回值
    • 被删除的元素
  • 代码展示:
    const arr = [1, 2, 3];
    const lastValue = arr.pop();	// 被删除的元素
    console.log(arr);			// [1, 2]
    console.log(lastValue);	// 3
    

6.2.4 shift()

  • 作用
    • 从数组中删除 第一个元素,并返回 该元素的值
    • 改变数组的长度
  • 语法
    arr.shift()
    
  • 返回值
    • 被删除的元素
  • 代码展示:
    const arr = [1, 2, 3];
    const firstValue = arr.shift();	// 被删除的元素
    console.log(arr);			// [2, 3]
    console.log(firstValue);	// 1
    

6.2.5 splice()

  • 作用
    • 通过 删除替换 现有元素原地添加新元素修改数组,并以 数组的形式返回被修改的内容
  • 语法
    array.splice(startIndex[, deleteCount[, items]])
    
  • 返回值
    • 数组的形式 返回 被修改 的 内容
  • 注意
    • 删除/替换 的时候,包括 开始索引位置
    • 添加 的时候,是添加在 开始索引位置之前
    • 添加 的时候返回的是 空数组
  • 代码展示:
    // 删除
    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.2.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]
    

6.2.7 reverse() - 反转数组

  • 作用
    • 将数组中的元素的 位置颠倒 ,并返回该数组
  • 语法
    arr.reverse();
    
  • 返回值
    • 颠倒顺序后的数组
  • 代码展示:
    const arr = [1, 2, 3, 4, 5];
    arr.reverse();
    console.log(arr);	// [5, 4, 3, 2, 1]
    

6.3 数组方法总结 - 不改变原始数组

6.3.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
    

6.3.2 map() - 迭代数组(映射数组)

  • 作用
    • 遍历原数组,把原数组中的每一个数据加工改造,形成一个新数组返回
  • image-20220815114702965
  • 语法:
    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]
    

6.3.3 filter() - 筛选数组

  • 作用
    • 过滤 原始数组中的数据,把 满足条件 的数据放在一个新数组中
  • image-20220815114354741
  • 语法
    Array.filter(function (item[, index[, Array]]) {} )
    
  • 返回值
    • 是一个新数组,里面是所有原始数组中满足条件的数据
      注意
    • 在执行函数返回true的情况下,留下该数据,返回为false 去除该数据
  • 代码展示:
    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);
    

6.3.4 reduce() - 累计器(求和)

  • 作用
    • 每一次运行 reduce 会将先前元素的计算结果作为参数传入,最后将其结果汇总为单个返回值
  • image-20220815210033254
  • 语法
    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
    

6.3.5 join()

  • 作用
    • 将一个 数组类数组对象 的所有元素连接成一个 字符串,并返回这个字符串
  • 注意
    • 所有的元素 转换成 字符串分隔符 将这些字符串 连接 起来
  • 语法
    arr.join([分隔符]);
    
  • 参数
    • 默认 使用 逗号
  • 返回值
    • 字符串
  • 代码展示:
    const arr = ['迪迦', '赛罗', '泰罗'];
    let str = arr.join();
    let str1 = arr.join('-');
    console.log(str);		// 迪迦,塞罗,泰罗
    console.log(str1);	// 迪迦-塞罗-泰罗
    console.log(arr);		// ['迪迦', '赛罗', '泰罗']
    

6.3.6 find() - 返回数组中满足条件的 第一个元素

  • 作用
    • 返回 数组 中 满足条件第一个元素
  • image-20220816155818235
  • 语法
    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
    

6.3.7 some()

  • 作用
    • 判断 数组中是不是 至少有一个元素 满足条件
  • image-20220816155750249
  • 语法
    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
    

6.3.8 every()

  • 描述: 判断 数组 里面的 每一个元素 是不是都 满足条件
  • image-20220816155732177
  • 语法:
    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
    

6.3.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 }]
    

6.3.10 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);	
    

6.3.11 flat() - 数组降维(数组扁平化)

arr.flat(几维数组);
arr.flat(Infinity);		// 一劳永逸,不管几维数组都能降到一维数组

6.3.12 indexOf() - 正向 查看数组里面 指定数据 的 索引

Array.indexOf(数据)
Array.indexOf(数据, 开始索引)	从哪个索引开始向后查找
返回值: 如果有这个数据,是第一个满足条件的数据的索引
            如果没有这个数据,就是 -1

6.3.13 lastIndexOf() - 反向 查看数组里面 指定数据 的 索引

Array.lastIndexOf(数据)
Array.lastIndexOf(数据, 开始索引)
注意: 虽然是反向查找,但是索引还是正常索引

6.3.14 copyWithin() - 使用 数组里面的内容 替换 数组里面的内容

Array.copyWithin(目标位置, 开始索引, 结束索引)
    ->目标位置:当你替换内容的时候,从哪一个索引位置开始替换
  ->开始索引:数组哪一个索引位置开始当作替换内容,默认是 0 
  ->结束索引:数组哪一个索引位置结束当作替换内容,默认是 结尾
返回值: 是一个新数组(替换后的数组)

6.3.15 fill() - 使用 指定数据区 填充 数组

  • image-20220816155906720
Array.fill(要填充的数据, 开始索引, 结束索引)
前提: 数组要有 length
返回值:
    填充好的数组

6.3.16 includes() - 查看 数组中是不是有 某一个数据

Array.includes(数据)
返回值: 一个布尔值
    有这个数据就是 true
    没有这个数据就是 false

6.3.17 findIndex() - 根据条件找到数组里面满足条件的数据的索引

  • image-20220816155843416
Array.findIndex(function (item) {} )
返回值:
    找到满足条件的第一个元素的索引
image-20220815210652899

6.4 伪数组 ➡ 真数组

  • 静态方法:
    • 语法:
      Array.from()
      
    • 代码展示:
    // Array.from(伪数组):将伪数组转换为真数组
    // 返回值:真数组
    const arr = Array.from(document.querySelectorAll('li'));
    arr.pop();
    console.log(arr);
    
    image-20220815234811144
  • 伪数组(对象)
    • 有索引值
    • 有length属性
    • 索引值 并且 有 length属性对象 就是 伪数组
    • const obj = {
        0: 'zs',
        1: 20,
        2: '小妹',
        length: 3
      }
      console.log(Array.from(obj));	// ['zs', 20, '小妹']
      

七、 内置构造函数 - String

7.1 ❌字符串的创建

// 字面量创建
let str = 'hello world'

// 内置构造函数创建
let str = new String('hello world')

7.2 字符串方法

  • 都是 实例方法
  • image-20220816160128364
  • 注意
    • 所有字符串方法不会改变 原始字符串

7.2.1 substring() - 截取字符串

  • 语法:
    str.substring(indexStart[, indexEnd])
    
  • 参数:
    • indexStart = indexEnd ➡ 返回一个 空字符串
    • 省略indexEnd ➡ 提取字符 开始索引 一直字符串末尾
    • 如果任一 参数小于0或为NaN,则被 当作 0
    • 如果任一 参数大与 stringName.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()(弃用)

7.2.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']
    

7.2.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);
    

7.2.4 includes() - 判断一个字符串是否包含在另一个字符串中(返回true / false)(includes - 包含)

  • 语法:
    str.includes(搜索的字符串[, 检测索引位置])
    
  • 返回值: true / false
  • 代码展示:
    // .str.includes(搜索的字符串[, 检测位置的索引]):判断一个字符串是否包含在另一个字符串中
    // 返回值:true / false
    const str = '迪迦奥特曼,赛罗奥特曼,银河奥特曼,艾克斯奥特曼';
    const flag = str.includes('赛罗奥特曼');
    console.log(flag);
    

7.2.5 replace() - 替换字符串中指定的字符

  • 语法:
    字符串.replace(要替换的旧字符, 替换的新字符)
    
  • 返回值: 替换之后 的字符串
  • 注意:
    • 不改变 原始字符串
    • 默认 只替换 匹配到的 第一个字符串
    • 可以 配合 正则表达式(g 和 i) 来 全局匹配 并且 不区分大小写
  • 代码展示:
    let str = '赛文奥特曼真好看,并且赛文奥特曼真厉害!';
    const newStr = str.replace(/赛文/g, '迪迦');
    console.log(newStr);	// 迪迦奥特曼真好看,并且迪迦奥特曼真厉害!
    
image-20220822172410240

八、内置构造函数 - Number

  • Number内置构造函数,用于创建数值
  • Number.toFixed(保留几位小数) - 保留小数位的长度
    • 具有 四舍五入 功能;
  • Number.isInterger(参数) - 判断给定的参数是否为整数
    • 返回值
      • 布尔值 - true / false
  • ⚠🔻 使用该方法之后数字会转换为 字符串
    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)
    

九、编程思想

9.1 面向过程变成

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

9.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也不同
    
    image-20220817143524608

十一、❗❗❗ 原型

11.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
    
    image-20220817144414694.png
    // 封装一个求数组最值和和的方法(只要是数字数组就可以使用)
    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]
    

11.2 constructor属性

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

11.3 对象原型

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

11.4 原型继承

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

11.5 ❗❗ 原型链

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

11.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 的原型链上
    
  • 21
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值