JavaScript基础五

八. 数组

1. 数组的简介

  • 数组(Array)
    • 数组也是一种复合数据类型, 数组可以存储多个不同类型的数据
    • 数组中存储的是有序的数据, 数组中的每一个数据都有唯一的索引
      • 可以通过索引来操作获取数据
    • 数组中存储的数据叫元素(对象叫属性)
    • 索引(index)是一组大于0的整数
    • 创建数组
      • 通过Array来创建数组, 也可以通过[]来创建数组
        • 向数组中添加元素
          语法:
          数组[索引] = 元素

        • 读取数字中的元素
          语法
          数组[索引]
          - 如果读取了一个不存在的元素, 不会报错而是返回undefined

        • length

          • 获取数组中元素的数量(长度)
          • 获取的实际值就是数组的最大索引 + 1
          • 向数组最后添加元素:
            数组[数组.length0] = 元素
          • length是可以修改的
  const arr = new Array()
    arr[0] = 10
    arr[1] = 22
    arr[2] = 44
    arr[3] = 88
    arr[4] = 99

    // 使用数组是, 应该避免非连续数组, 因为它的性能不好
    // arr[10] = 99
    // const arr2 = [1, 2, 3, 4, 5]  // 数组字面量
    // console.log(arr[1])
    // console.log(arr2)
  
    // console.log(typeof arr)  // object

    // console.log(arr.length)

    arr[arr.length] = 33
    arr[arr.length] = 55
    arr.length = 10
    console.log(arr)

2. 数组的遍历

  • 遍历数组
    • 遍历数组简单理解, 就是获取到数组中的每一个元素
  // 任何类型的值都可以称为数组中的元素
    let arr = [1, "hello", true, null, { name: "熏悟空" }, () => { }]
  // 正着遍历数组
    for(let i = 0; i < arr.length; i++) {
      console.log(arr[i])
    }

  // 倒着遍历数组
    for (let i = arr.length -1; i >= 0; i--) {
      console.log(arr[i])
    }

3. 数组练习

  • 定义一个Person类, 类中有两个属性 name和age
    • 然后创建几个Person对象, 将其添加到一个数字中
      • 遍历数组, 并打印未成年人的信息
     class Person {
      constructor(name, age) {
        this.name = name
        this.age = age
      }
    }

    const personArr = [
      new Person("熏悟空", 18),
      new Person("沙悟净", 38),
      new Person("猪悟能", 28),
      new Person("唐玄奘", 28),
      new Person("小白龙", 18),
      new Person("红孩儿", 8),
      new Person("女儿国国王", 17),
    ]

    for (let i = 0; i < personArr.length; i++) {
      console.log(personArr[i])
    }

    console.log("----")

    for (let i = 0; i < personArr.length; i++) {
       if (personArr[i].age < 18) {
        console.log(personArr[i])
      }
    }

4. for of语句4

  • for-of语句可以用来遍历可迭代对象

    • 语法
      for(变量 of 可迭代对象) {
      语句
      }

    • 执行流程

      • for-of的循环体会执行多次, 数组中有几个元素会执行几次
      • 每次执行时都会将一个元素赋值给变量
   const arr = ["熏悟空", "猪悟能", "沙悟净", "小白龙", "唐玄奘", "女儿国国王"]
    for(value of arr) {
      console.log(value)
    }

    for(value of "hello") {
      console.log(value)
    }

5. 数组方法

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array

  • Array.isArray()

    • 用来检查一个对象是否为数组
  • at()

    • 可以组中的指定根据索引获取数元素
    • at可以接收负索引作为参数 -1 就是白骨精 -2 就是红孩儿倒序
  • concat()

    • 用来链接两个或多个数组
    • 非破坏性方法, 不会影响原数组, 而是返回一个新的数组
    // console.log(Array.isArray({ name: "孙悟空" }))  // false
    // console.log(Array.isArray([1, 2, 3]))  // true

    const arr = ["孙悟空", "猪八戒", "红孩儿", "白骨精"]
    const arr2 = ["蜘蛛精", "玉兔精", "牛魔王", "黑熊精"]
    // console.log(arr.at(-1))
    const result = arr.concat(arr2, ["白龙马", "宝贝袈裟"], ["悟空, 师傅被妖怪抓走了"])
    console.log(result)

6. 数组方法

  • indexOf()
    • 获取元素在数组第一次出现的索引

    • 参数:

      1. 要查询的元素
      2. 要查询的起始位置
    • lastIndexOf()

      • 获取元素数组最后一次出现的位置

      • 返回值

        • 找到了返回元素的索引, 没有找到返回-1
    • join()

      • 将一个数组中的元素链接为一个字符串
      • [“小白龙”, “沙悟净”, “唐三藏”, “小白龙”] -> “小白龙”, “沙悟净”, “唐三藏”, “小白龙”
      • 参数:
        • 指定一个字符串作为连接符
    • slice()

      • 用来截取数组(非破坏性方法)
      • 参数
        1. 截取的起始位置(包含该位置),
        2. 截取的结束位置(不包含该位置)
      • 第二个参数可以省略不写, 如果省略就会截取到最后
      • 索引可以是复制
      • 如果将两个参数全都省略, 可以对数组进行浅拷贝,(浅复制)
  let arr = ["孙悟空", "猪悟能", "小白龙", "孙悟空", "小白龙", "沙悟净", "唐三藏", "小白龙"]

    // const result = arr.indexOf("小白龙", 3)
    // const result = arr.lastIndexOf("小白龙", 3)
    // const result = arr.indexOf("至尊宝")

    let result = arr.join("$")

    arr = ["孙悟空", "猪八戒", "沙和尚", "唐玄奘"]
    result = arr.slice(1, -1)
    result = arr.slice()
    // console.log(arr)
    console.log(result)

7. 对象的复制

   const arr = ["孙悟空", "猪八戒", "沙和尚"]
    // const arr2 = arr  // 不是复制
    // arr2[0] = "唐僧"
    // 如何复制一个对象, 复制必须产生新的对象
    // console.log(arr)
    // console.log(arr2)

    
    // 当调用slice时, 会产生一个新的数组对象, 从而完成对数组的复制
    const arr3 = arr.slice()
    arr3[1] = "局八戒"
    console.log(arr === arr3)  // false
    console.log(arr)   // 没有修改
    console.log(arr3)  // 修改了猪八戒为局八戒

8. 深拷贝和浅拷贝

  • 浅拷贝 (shallow copy)

    • 通常对对象的拷贝都是浅拷贝
    • 浅拷贝顾名思义, 只对对象的浅层进行复制(只复制一层)
    • 如果对象中存储的数据是原始值, 那么拷贝的深浅不重要
    • 浅拷贝只会对对象本身进行复制, 不会复制对象中的属性(或元素)
  • 深拷贝 (deep copy)

    • 深拷贝不仅复制对象本身, 还要复制对象中的属性(或元素)
    • 因为性能问题, 通常情况不太使用深拷贝
    • 通常情况下不太使用深拷贝 structuredClone() 专门用来深拷贝的方法
   // 创建一个数组
    const arr = [{name: "孙悟空"}, {name: "猪八戒"}]
    const arr2 = arr.slice()  // 浅拷贝

    const arr3 = structuredClone(arr)  // 专门用来深拷贝的方法

    console.log(arr)
    console.log(arr3)

9. 对象的复制

  const arr = ["孙悟空", "猪八戒", "沙和尚"]
    const arr2 = arr.slice()

    // console.log(arr === arr2)
  • … (展开运算符)
    • 可以将一个数组中的元素展开到另一个数组中或者作为函数的参数
    • 通过它也可以对数组进行浅拷贝
    // const arr3 = [arr[0], arr[1], arr[2]]
    const arr3 = [...arr]
    // const arr3 = ["唐僧", ...arr, "白骨精"]
    // console.log(arr)
    // console.log(arr3)

    function sum(a, b, c) {
      return a + b + c
    }
    
    const arr4 = [10, 20, 30]
    let result = sum(...arr4)
    // console.log(result)
  • 对象的复制
    • Object.assign(目标对象, 被复制的对象)
    • 将被复制对象中的属性复制到目标对象, 并将目标对象返回
    • 重复的属性会被覆盖
    • 可以使用展开运算符对对象复制
   const obj = {name: "孙悟空", age: 18}
    const obj2 = {address: "花果山", age: 28, name: "局八戒"}
    Object.assign(obj2, obj)
    // const obj2 = Object.assign({}, obj)
    // console.log(obj2)

    const obj3 = {address: "高老庄", name: "局八戒",...obj, age: 28}  // 将obj的属性在新对象中展开
    console.log(obj3)
  • 都是破坏性方法
    • push()

      • 向数组的末尾添加一个或者多个元素, 并返回新数组的长度
    • pop()

      • 删除并返回数组最后一个元素, 返回值删除的是谁就是谁
    • unshift()

      • 向数组的最前面添加一个或者多个元素, 并返回新数组的长度
    • shift()

      • 删除并返回数组最前面一个元素, 返回值删除的是谁就是谁
    • splice()

      • 可以删除, 插入, 替换数组中的元素

      • 参数

        1. 删除的起始位置(索引)
        2. 删除的个数(数量)
        3. 要插入的元素
        • 返回值
        • 返回被删除的元素
      • reverse()

        • 反转数组
let arr = ["孙悟空", "猪八戒", "沙悟净"]
    let result = arr.push("唐僧", "白骨精", "女儿国国王")
    // console.log(arr)

    arr.pop()
    // arr.pop()
    // result = arr.pop()

    reuslt = arr.unshift("牛魔王")
    result = arr.shift()

    // console.log(result)

    arr = ["孙悟空", "猪八戒", "沙悟净", "唐僧"]
    // result = arr.splice(0, 0, "牛魔王", "铁扇公主", "红孩儿")
    // console.log(arr)
    // console.log(result)  // 返回值, 返回被删除的元素, 因为删除的什么都没有, 所以空数组


    arr = ["a", "b", "c", "d", "e", "f", "g"]
    arr.reverse()
    console.log(arr)

11. 数组去重

   let arr = [1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 3, 3, 3, 3, 2, 2, 2, 2, 2, 4, 5, 5, 6, 7]

    // 编写代码, 祛除数组重复的元素
    // 分别获取数组中的元素
    for (let i = 0; i < arr.length; i++) {
      // console.log(arr[i])
      // 获取当前值后面的所有值
      for (let j = i + 1; j < arr.length; j++) {
        // console.log("--", arr[j])
        // 判断两个数是否相等
        if (arr[i] === arr[j]) {
          arr.splice(j, 1)

          /* 
            当arr[i]和arr[j]相同时, 它会自动的删除j位置的元素, 然后j + 1 位置的元素会变成j + 1 位置的元素
            而j位置的元素已经比较过了, 不会重复比较, 所以会出现漏比较严重的情况

            解决办法, 当删除一个元素, 需要将该位置的元素在比较
          */
          j--
        }
      }
    }
    console.log(arr)

12. 数组去重

  let arr = [1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 3, 3, 3, 3, 2, 2, 2, 2, 2, 4, 5, 5, 6, 7]

    // 获取数组中的元素
    for (let i = 0; i < arr.length; i++) {
      let index = arr.indexOf(arr[i], i + 1)
      if(index != -1) {
        // 出现重复内容
        arr.splice(index, 1)
        i--
      }
    }
    console.log(arr)

      const newArr = []
      for(ele of arr) {
        if(newArr.indexOf(ele) === -1) {
          newArr.push(ele)
        }
      }
      console.log(newArr)

13. 冒泡排序

  • 思路一:

    • 9,1,3,2,8,0,5,7,6,4

    • 比较相邻的两个元素, 然后根据大小来决定是否交换它们的位置

    • 这种排序方式, 被称为冒泡排序, 冒泡排序是最慢的排序方式, 数字少还可以凑合用, 不适用于数据量较大的排序

  • 思路二:

    • 取出一个元素, 将其他元素和该元素进行比较, 如果其他元素比该元素小则交换两个元素的位置
    • 例子:
      • 0 9 3 2 8 1 5 7 6 4
      • 0 1 9 3 8 2 5 7 6 4
      • 0 1 2 9 8 3 5 7 6 4
    • 选择排序
   let arr = [9, 1, 3, 2, 8, 0, 5, 7, 6, 4]
    // for (let j = 0; j < arr.length - 1; j++) {
    //   for (let i = 0; i < arr.length - 1; i++) {
    //     //  arr[i]是前面的元素 arr[i+1]是后面的元素
    //     if (arr[i] > arr[i + 1]) {
    //       // 大数在前, 小数在后, 需要交换两个数的位置
    //       let temp = arr[i]  // 临时变量用存储arr[i]的值
    //       arr[i] = arr[i + 1]  // arr[i + 1]赋值给arr[i]
    //       arr[i + 1] = temp // 修改arr[i + 1]的值
    //     }
    //   }
    // }
    // console.log(arr)

14. 选择排序

  • 选择排序
    • 取出一个元素, 然后将其他元素和该元素进行比较, 如果其他元素比该元素小, 则交换两个元素的位置
    for(let i = 0; i < arr.length; i++) {
      for(let j = i + 1; j < arr.length; j++) {
        if(arr[i] > arr[j]) {
          // 交换两个元素位置
          let temp = arr[i]
          arr[i] = arr[j]
          arr[j] = temp
        }
      }
    }
    console.log(first)

15. 函数

    class Person {
      constructor(name, age) {
        this.name = name
        this.age = age
      }
    }

    const personArr = [
      new Person("熏悟空", 18),
      new Person("沙悟净", 38),
      new Person("猪悟能", 28),
      new Person("唐玄奘", 28),
      new Person("小白龙", 18),
      new Person("红孩儿", 8),
      new Person("女儿国国王", 17),
    ]

    // filter()函数用来对数组进行过滤
    function filter(arr) {
      const newArr = []

      for (let i = 0; i < arr.length; i++) {
        if (arr[i].age < 18) {
          newArr.push(arr[i])
        }
      }
      return newArr
    }

    let result = filter(personArr)
    console.log(result)
问题

目前我们的函数只能过滤出数组中age属性小于18的对象,
我们希望过滤更加灵活:
比如: 过滤数组中大于18的对象
age大于60的对象
age大于n的对象
过滤数组中name为xxx的对象
过滤数组中的偶数

一个函数的参数也可以是函数
  如果将函数作为参数, 那么我们就称这个函数为回调函数(callback)
    function filter(arr, cb) {
      const newArr = []
      // console.log(cb)
      // cb()
      for (let i = 0; i < arr.length; i++) {
        // arr[i].age >= 18
        // arr[i].age > 60
        // arr[i].age > n
        // arr[i].age  === "xxx"
        // arr[i] % 2 === 0
        if (cb(arr[i])) {
          newArr.push(arr[i])
        }
      }
      return newArr
    }

    function fn(a) {
      return a.age < 18
    }

    let result = filter(personArr, fn)
    console.log(result)

16. 高阶函数

  • 高阶函数
    • 如果一个函数的参数或返回值是函数, 则这个函数就称为高阶函数
    • 为什么要将函数作为参数传递? (回调函数有什么作用)
    • 将函数作为参数, 意味着可以对另一个函数动态的传递代码
   class Person {
      constructor(name, age) {
        this.name = name
        this.age = age
      }
    }

    const personArr = [
      new Person("熏悟空", 18),
      new Person("沙悟净", 38),
      new Person("猪悟能", 28),
      new Person("唐玄奘", 28),
      new Person("小白龙", 18),
      new Person("红孩儿", 8),
      new Person("女儿国国王", 17),
    ]

    function filter(arr, cb) {
      const newArr = []

      for (let i = 0; i < arr.length; i++) {
        if (cb(arr[i])) {
          newArr.push(arr[i])
        }
      }
      return newArr
    }

   // 我们这种定义回调函数的形式比较少见, 通常回调函数都是匿名函数
    function fn(a) {
      return a.age < 18
    }

     result = filter(personArr, a => a.name === "熏悟空")
    result = filter(personArr, a => a.age >= 18)

    const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    result = filter(arr, a => a % 2 === 0)
    console.log(result)

17. 高阶函数

  • 希望someFn()函数在执行时, 可以记录一条日志
  • 在不修改原函数的基础上, 为其增加记录日志的功能
  • 可以通过高阶函数, 来动态的生成一个新函数
   function someFn() {
      console.log(111)
      return "hello"
    }

    function outer(cb) {
      let a = 1
      return () => {
        console.log("记录", a++)
        const result =  cb()
        return result
      }
    }

    let result = outer(someFn)
    // console.log(result)

    function test() {
      console.log("test")
      return "test"
    }
    let newTest = outer(test)    
    newTest()

18. 函数

  • 创建一个函数, 第一次调用时, 打印1, 第二次调用打印二, 以此类推

    • 可以利用函数来隐藏不希望被外部访问到的变量
  • 闭包:

    • 闭包就是能访问到外部函数作用域中变量的函数
    • 什么时候使用闭包
      • 当我们需要隐藏一些不希望被别人访问的内容时就可以使用闭包
    • 构成闭包的要件
      1. 函数的嵌套
      2. 内部函数要引用外部函数的变量
      3. 内部函数要作为返回值返回
   function outer() {
      let num = 0  // 位于函数作用域中

      return () => {
        num++
        console.log(num)
      }
    }

    const newFn = outer()

19. 闭包的原理

  • 函数的外层的作用域, 在函数创建时就已经确定了(词法作用域)

  • 和调用的位置无关

  • 闭包利用的就是词法作用域

 let a = "全局变量a"

    function fn() {
      console.log(a)
    }

    function fn2() {
      let a = "fn2中的a"
      // console.log(a)

      fn()
      // console.log(a)
    }

    // fn2()

    function fn3() {
      let a = "fn3中的a"

      function fn4() {
        console.log(a)
      }
      return fn4
    }
    let result = fn3()
    result()

20. 闭包的注意事项

  • 闭包的生命周期:

    1. 闭包在外部函数调用时产生, 外部函数每次调用都会产生全新的闭包
    2. 在内部函数丢失时销毁(内部函数被垃圾回收了, 闭包才会消失)
  • 注意事项:

    • 闭包主要用来隐藏一些不希望被外部访问的内容

    • 这就意味着闭包需要占用一定的内存空间

    • 相较于类来说, 闭包比较浪费内存空间(类可以使用原型而闭包不能)

    • 需要执行次数较少时, 使用闭包

    • 需要大量创建实例时, 使用类

   function outer2() {
      let num = 0
      return () => {
        num++
        console.log(num)
      }
    }
    let fne = outer2()  // 独立闭包
    let fnee = outer2() // 独立闭包

    fne()
    fnee()

21. 递归

  • 递归:

    • 调用自身的函数我们称为递归
    • 递归的作用和循环基本一致
  • 1! 1

    • 2! 2 x 1 = 2
    • 3! 1 x 2 x 3 = 6
    • 4! 1 x 2 x 3 x 4 = 24
    • 10! 1 x 2 x 3 x 4 x 5 x 6 x 7 x 8 x 9 x 10 = xxx
  • 如何用递归来解决阶乘

    • 5! = 4! x 5
    • 4! = 3! x 4
    • 3! = 2! x 3
    • 2! = 1! x 2
    • 1! = 1
      • 递归的核心思想就是将一个大的问题拆分成一个个小的问题, 小的问题解决了, 大的问题也就解决了
  • 编写递归函数, 一定要包含两个要件:

    1. 基线条件 – 递归的终止条件
    2. 递归条件 – 如何对问题进行拆分
    • 递归的作用和循环是一致的, 不同点在于, 递归思路比较清晰简洁, 循环的执行性能比较好
    • 在开发中一般的问题都用循环解决, 尽量使用循环, 少用递归
    • 只在一些使用循环解决比较麻烦的情况场景下, 才使用递归
  function jieCheng(num) {
      // 创建一个变量用来记录结果
      let result = 1

      for (let i = 2; i <= num; i++) {
        result *= i
      }
      return result
    }

    let result = jieCheng(5)
    // console.log(result)

    function jieCheng2(num) {
      // 基线条件
      if (num === 1) {
        return 1
      }
      // 递归条件
      // num! = (num-1)! x num
      return jieCheng2(num - 1) * num
    }

    result = jieCheng2(10)
    console.log(result)

22. 菲波那切数列

  // 求斐波那契数列第n个数
    function fib(n) {
      
      // 确定基线条件
      if(n < 3) {
        return 1
      }

      // 设置递归条件
      // 第n个数 = 第n个数-1 + 第n-2
      return fib(n - 1) + fib(n - 2)
    }
    let result = fib(10)
    console.log(result)

23. 数组的方法

  • sort()

    • sort用来对数组进行排序, (会改变原数组)
    • sort默认会将数组升序排列
    • 注意: sort默认会按照Unicode编码进行排序, 如果直接通过sort对数字进行排列,
    • 可能会得到一个不正确的结果
    • 参数:
      • 可以传递一个回调函数作为参数, 通过回调函数指定排序的规则
      • sort((a, b) => a - b) 升序排列
      • sort((a, b) => b - a) 降序排列
  • forEach()

    • 用来遍历数组
    • 它需要一个回调函数作为参数, 这个回调函数会被调用多次
      • 数组中有几个元素, 回调函数就会调用几次
      • 每次调用都会将数组中的数据当参数传
    • 回调函数中有三个参数:
      • element 当前的元素
      • index 当前元素的索引
      • array 被遍历的数组
  • filter()

    • 将数组中符合条件的元素保存到一个新数组中返回
    • 也是需要一个回调函数作为参数, 会为每一个元素去调用回调函数 并根据返回值来决定是否将元素添加到新数组中
    • 非破坏性方法, 不会影响原数组
    • 和上面forEach一样三个回调函数
  • map()

    • 根据当前数组生成一个新数组
    • 需要回调函数作为参数 和上面forEach一样三个回调函数
      • 回调函数的返回值会成为新数组中的元素
      • 非破坏性方法, 不会影响原数组
  • reduce()

    • 可以用来将一个数组中的所有元素整合为一个值
    • 参数:
      1. 回调函数, 通过回调函数来指定合并的规则
      2. 可选的参数, 初始值
  let arr = ["a", "c", "b", "e", "d", "g", "f"]

    arr = [3, 2, 1, 9, 0, 4, 5, 6, 7, 8, 10]
    // console.log(arr.sort())
    arr.sort((a, b) => b - a)

    // console.log(arr)

    arr = ["孙悟空", "猪八戒", "沙悟净", "唐僧", "小白龙"]
    // arr.forEach((element, index, array) => {
    //   console.log(array)
    // })

    arr = [1, 2, 3, 4, 5, 6, 7, 8]
    // 获取数组中的所有偶数

    // 这个和上面也是当前元素
    let result = arr.filter(element => element % 2 === 0)

    // ele是当前数组 当前数组 * 2   ele * 2就是当前数组的二倍
    result = arr.map(ele => ele * 2)

    arr = ["孙悟空", "猪八戒", "沙和尚"]
    result = arr.map(ele => "<li>" + ele + "</li>")

    arr = [1, 2, 3, 4, 5, 6, 7, 8]
    result = arr.reduce((a, b) => {
      /* 
        a, b
        1, 2, 
        3, 3,
        6, 4,
        10, 5,
      */
      // console.log(a, b)
      return a % b
    })

    // result = arr.reduce((a, b) => a + b, 10)
    /* 
      a,  b
      10, 1
    */

    console.log(result)

24. 可变参数

  • arguments
    • arguments是函数中的又一个隐含参数
    • arguments是类数组对象(伪数组)
      • 和数组类似, 可以通过索引来读取元素, 也可以通过for循环遍历, 但是它不会一个数组元素, 不能调用数组元素
      • arguments用来存储函数的实参
        • 无论用户是否定义形参, 实参都会存储到argument对象中
        • 可以通过该对象直接访问实参
  // console.log(arguments[2])
      // console.log(Array.isArray(arguments))  // false
      // for(let i = 0; i < arguments.length; i++) {
      //   console.log(arguments[i])
      // }

      // for(v of arguments) {
      //   console.log(v)
      // }


      // 报错 因为不是数组不能调用数组方法
      arguments.forEach((ele) => {
        console.log(ele)
      })

    }

    // fn(1, 10, 33)

    // 定义一个函数, 可以求任意数值的和
    function sum() {
      // 通过arguments, 可以不受参数数量的限制更加灵活的创建函数
      let result = 0
      for (let num of arguments) {
        result += num
      }
      return result
    }
    // sum()
  • 可变参数, 在定义函数时可以将参数指定为可变参数
    • 可变参数可以接收任意实参, 并将它们统一存储到一个数组中
    • 可变参数的作用和arguments的作用基本是一样的, 但是它也有不同点
      1. 可变参数的名字可以自己指定
      2. 可变参数就是数组, 可以直接使用数组的方法
      3. 可变参数可以配合其他参数一起使用
   function fn2(...args) {
      console.log(args)
    }

    function sum2(...num) {
      return num.reduce((a, b) => a + b, 0) 
    }
    

    // 当可变参数和其他参数一起使用时, 要将可变参数放在最后
    function fn3(a, b, ...args) {
      // for(let v of arguments) {
      //   console.log(v)
      // }
      console.log(args)
    }

    fn3(123, 456, 789, "hello", true, false)

25. 函数

  • 根据函数调用方式不同this指向不同
    1. 以函数形式调用, this是window
    2. 以方法形式调用. this是调用方法的对象
    3. 构造函数中, this是新建的对象
    4. 箭头函数没有自己的this, 由外层作用域决定
    5. 通过call和apply调用的函数, 它们的第一个参数就是函数的this
    6. 通过bind返回的函数, this由bind第一个参数决定(无法修改)
   function fn() {
      console.log("函数执行了", this)
    }

    const obj = {
      name: '孙悟空',
      fn
    }

    // obj.fn()
  • 调用函数除了函数()这种情况外, 还可以通过其他方式来调用函数
  • 比如: 我们可以通过调用函数的call()和apply()两个方法来调用函数
    • 函数.call()
    • 函数.apply()
      • call和apply除了可以调用函数, 还可以用来指定函数中的this
      • call和apply的第一个参数将会成为函数的this
      • 通过call方法调用函数, 函数的实参直接在第一个参数后面一个一个列出来
      • 通过apply方法调用函数, 函数的实参需要用一个数组传递
  function sum(a, b, c, d) {
      console.log("a = ", a , "b = ", b, this)
    }

    // sum(123, 456)
    // sum.call(obj, 1, 2)
    sum.apply(obj, ["hellow", true, 123, "你好"])

26. bind

  • 根据函数调用方式不同this指向不同

    1. 以函数形式调用, this是window
    2. 以方法形式调用. this是调用方法的对象
    3. 构造函数中, this是新建的对象
    4. 箭头函数没有自己的this, 由外层作用域决定
    5. 通过call和apply调用的函数, 它们的第一个参数就是函数的this
    6. 通过bind返回的函数, this由bind第一个参数决定(无法修改)
  • bind()是函数的方法, 可以用来创建新的函数

    • bind可以为新函数绑定this
    • bind也可以为新函数绑定参数
  • 箭头函数没有自身的this, 它的this由外层作用域决定,

    • 也无法通过call, apply, bind修改它的this
    • 箭头函数当中没有arguments
  function fn(a, b, c) {
      console.log("fn执行了", this)
      console.log(a, b, c)
    }

    const obj = { name: "孙悟空" }
    const newFn = fn.bind(obj, 10)
    newFn()


    const arrowFn = () => {
      console.log(arguments)
    }
    arrowFn()  // 不能调用了有问题

    // const arr = arrowFn.bind(obj)
    // arr()

    class Myclass {
      fn = () => {
        console.log(this)
      }
    }

    const mc = new Myclass()
    mc.fn.call(window)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

coderyhh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值