八、数组

suaxi
2024-12-31 / 0 评论 / 45 阅读 / 正在检测是否收录...

1. 简介

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>简介</title>
  </head>
  <body>
    <script>
        /*
        数组(Array):
            - 数组也是一种复合数据类型,可以存储多个不同类型的数据
            - 存储的元素是有序的,每个元素都有自己的唯一索引
            - 创建数组:
                - const arr = new Array()
                - const arr = []
            - 向数组中添加元素:
                - 数组[索引] = 元素
            - 读取数组中的元素:
                - 数组[索引]
                - 使用下标索引读取一个不存在的元素时,会返回undefined
            - length方法:
                - 获取数组的长度
                - 返回结果是当前数组的最大索引 + 1
        */

        const arr = new Array()
        const arr1 = [] // 数组字面量

        arr[0] = 1
        arr1[2] = 3 //数组中间有空元素:非连续数组(实际开发过程中尽量避免)

        console.log('arr', arr)
        console.log('arr1', arr1)

        console.log('typeof arr', typeof arr) //object
        
        console.log('arr1.length', arr1.length)
        
    </script>
  </body>
</html>

2. 数组的遍历

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>数组的遍历</title>
  </head>
  <body>
    <script>
        //const arr = [1, 2, 'a', 3, {name: '孙笑川'}, () => {}]
        //console.log(arr)
        
        //创建数组时,尽量确保元素是相同的数据类型
        const arr = [1, 2, 3]

        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])
        }
    </script>
  </body>
</html>

3. for-of语句

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>for-of语句</title>
  </head>
  <body>
    <script>
        /*
        for-of语句:
            - 可用于遍历可迭代的对象
            - 语法:
                for (变量 of 可迭代的对象) {
                    ......
                }
            - 执行流程:
                - for-of根据可迭代对象中的元素个数来执行循环体
                - 每次执行时都会将当前遍历到的元素赋值给变量
        */

        const arr = [1, 2, 3]
        for (let i of arr) {
            console.log(i)
        }

        for (let i of 'sunxiaochuan') {
            console.log(i)
        }
    </script>
  </body>
</html>

4. 数组的方法(非破坏性)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>数组的方法(非破坏性)</title>
</head>
<body>
    <script>
        /*
        mdn文档:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
        例:
            - indexOf() 获取指定元素在数组中第一次出现的位置(返回值为该位置的下标索引)
            - lastIndexOf() 获取指定元素在数组中最后一次出现的位置
            - join() 将数组中的元素连接为一个字符串,可以指定使用什么连接符(默认为英文逗号 ',')
            - slice() 从指定索引位置截取数组(非破坏性方法,截取之后生成新的返回值,不影响原数组)
                - arr.slice(1) 传一个参数时,从指定索引位置开始截取s
                - arr.slice(1, 3) 传两个参数时为“左闭右开”区间
                - arr.slice(1, -1) 参数为负时,表示从后往前取,其他同理传两个参数时的“左闭右开”区间
                - arr.slice() 参数为空时,可以对数组进行浅拷贝
        */

        const arr = [1, 2, 3]
        console.log(arr.slice(1))
        
    </script>
</body>
</html>

5. 对象的复制

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>对象的复制</title>
  </head>
  <body>
    <script>
        /**/

        const arr = [1, 2, 3]

        // 调用slice()时,会产生一个新的数组,从而完成对数组的复制
        const arr1 = arr.slice()

        console.log(arr)
        console.log(arr1)

        console.log('arr === arr1:', arr === arr1)
        
    </script>
  </body>
</html>

6. 深拷贝和浅拷贝

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>浅拷贝和深拷贝</title>
  </head>
  <body>
    <script>
        /*
        浅拷贝(shallow copy):
            - 一般情况下,对对象的拷贝都是浅拷贝
            - 只进行浅层拷贝(只拷贝一层)
            - 如果对象中存储的数据是原始值(基本类型),此时是否是深拷贝,浅拷贝不重要
            - 浅拷贝只会对对象本身进行复制,不会复制对象中的属性/元素
        深拷贝(deep copy)
            - 深拷贝不仅复制对象本身,还复制对象中的属性/元素
            - 通常情况下不经常使用深拷贝
        */

        const arr = [
            {
                name: '孙笑川',
                age: 33,
                info: {
                    address: '成都',
                    classNo: 1
                }
            },
            {
                name: '药水哥',
                age: 30,
                info: {
                    address: '湖北',
                    classNo: 2
                }
            }
        ]
        console.log('arr', arr)
        

        // 浅拷贝
        const arr1 = arr.slice()
        console.log('arr1', arr1)
        

        // 深拷贝
        const arr2 = structuredClone(arr)
        console.log('arr2', arr2)
    </script>
  </body>
</html>

7. 对象的复制

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>对象的复制</title>
  </head>
  <body>
    <script>
        const arr = [
            {
                name: '孙笑川',
                age: 33,
                info: {
                    address: '成都',
                    classNo: 1
                }
            },
            {
                name: '药水哥',
                age: 30,
                info: {
                    address: '湖北',
                    classNo: 2
                }
            }
        ]

        // slice() 方法
        const arr1 = arr.slice()
        console.log(arr === arr1)

        // [...xxx] 数组解构,也可以对数组进行复制
        const arr2 = [...arr]
        console.log(arr === arr2)
        
        // Object.assign(目标对象, 被复制的对象)
        const arr3 = Object.assign({}, arr)
        console.log('arr3', arr3)
        
        // 也可以用 ... 展开运算符对对象进行复制
        const arr4 = {...arr}
        console.log('arr4', arr4)

        //注:在进行复制时,如果有同名的属性或方法,前面的值会被后来的值覆盖
    </script>
  </body>
</html>

8. 数组的方法(破坏性)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>数组的方法(破坏性)</title>
  </head>
  <body>
    <script>
        /*
        push()
            - 向数组的末尾添加一个或多个元素,并返回新数组的长度
        pop()
            - 删除数组的最后一个元素,并返被删除的元素
        unshit()
            - 向数组的开头添加一个或多个元素,并返回新数组的长度
        shit()
            - 删除数组的第一个元素,并返被删除的元素
        splice()
            - 可以删除、添加、插入、替换元素
            - 参数:
                1. 删除的起始位置
                2. 删除的数量
            - 返回值:
                被删除的元素
        reverse()
            - 反转数组
        */

        let result
        const arr = [1, 2, 3, 4]
        
        //result = arr.push(1)
        //console.log('push()', arr)
        //console.log('result', result)
        
        //result = arr.pop(1)
        //console.log('pop()', arr)
        //console.log('result', result)

        //result = arr.unshift(1)
        //console.log('unshift()', arr)
        //console.log('result', result)

        //result = arr.shift()
        //console.log('shift()', arr)
        //console.log('result', result)

        // 从下标索引为1的位置开始删除
        //result = arr.splice(1)
        //console.log('splice()', arr)
        //console.log('result', result)

        // 删除下标索引为1,2的元素,并从下标索引为1的位置开始插入元素
        //arr.splice(1, 2, 'abc', {name: '孙笑川'})
        //console.log('splice()', arr)

        // 从下标索引为2的位置开始插入元素
        //arr.splice(2, 0, 'abc', {name: '孙笑川'})
        //console.log('splice()', arr)

        arr.reverse()
        console.log('reverse()', arr)
        
    </script>
  </body>
</html>

9. 排序

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>排序</title>
  </head>
  <body>
    <script>
        const arr = [22, 2 , 5, 10, 3, 6, 9, 7, 13, 8]

        // 冒泡排序
        for (let i = 0; i < arr.length - 1; i++) {
            for (let j = 0; j < arr.length - i - 1; j++) {
                if (arr[j + 1] < arr[j]) {
                    let temp = arr[j]
                    arr[j] = arr[j + 1]
                    arr[j + 1] = temp
                }
            }
        }
        console.log('冒泡排序', arr)
        
        const arr1 = [22, 2 , 5, 10, 3, 6, 9, 7, 13, 8]
        // 选择排序
        for (let i = 0; i < arr1.length - 1; i++) {
            for (let j = i + 1; j < arr1.length - 1; j++) {
                if (arr[i] > arr[j]) {
                    let temp = arr[i]
                    arr[i] = arr[j]
                    arr[j] = temp
                }
            }
        }
        console.log('选择排序', arr1)
    </script>
  </body>
</html>

10. 闭包

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>闭包</title>
  </head>
  <body>
    <script>
        /*
        闭包:
            - 能访问到外部函数作用域中变量的函数
            - 什么时候使用:
                - 当需要隐藏一些不想被外部访问的内容时
            - 构成闭包的条件:
                1. 函数的嵌套
                2. 内部函数要引用外部函数的变量
                3. 内部函数要作为返回值返回
        */

        function test() {
            let num = 0
            return () => {
                num++
                console.log('第', num, '次调用函数...')
            }
        }

        const testFn = test()
        testFn()
    </script>
  </body>
</html>

11. 闭包的原理

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>闭包的原理</title>
  </head>
  <body>
    <script>
        let a = '全局变量a'

        /*
        函数作用域,在函数创建时就已经确定(词法作用域)
            - 和函数的调用位置无关
        闭包利用的就是词法作用域
        */
        function test() {
            console.log(a)
        }

        function test1() {
            let a = 'test1中的局部变量a'

            test()
        }
        //test1() // 打印结果:全局变量a

        function test2() {
            let a = 'test2中的局部变量a'

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

            return test3
        }
        let result = test2()
        result() // 打印结果:test2中的局部变量a
    </script>
  </body>
</html>

12. 闭包的注意事项

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>闭包的注意事项</title>
  </head>
  <body>
    <script>
        /*
        闭包的生命周期:
            1. 在外部函数调用时产生,调用一次产生一个新的闭包
            2. 在内部函数丢失时,闭包会消失(即内部函数被垃圾回收了)
        注意事项:
            - 用于隐藏一些不想被外部访问的内容
            - 闭包需要占用一定的内存空间
            - 类可以使用原型,而闭包不行
                - 执行次数较少时,使用闭包
                - 需要大量创建实例时,使用类
        */

        function test() {
            let num = 1
            return () => {
                num++
                console.log(num)
            }
        }

        let testFn = test() // 独立闭包
        let testFn1 = test() // 独立闭包

        testFn()
        testFn1()

        testFn = null
        testFn1 = null
    </script>
  </body>
</html>

13. 递归

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>递归</title>
  </head>
  <body>
    <script>
        /*
        递归:
            - 调用自身的函数称为递归(自己调用自己)
            - 构成条件:
                1. 基线条件(递归的终止条件)
                2. 递归条件(如何对问题进行拆分)
        */

        function recursion(num) {
            // 基线条件
            if (num === 1) {
                return 1
            }

            // 递归条件
            return recursion(num - 1) * num
        }

        let num = 5
        let result = recursion(num)
        console.log('recursion(', num, ')', result)
        
    </script>
  </body>
</html>

14. 数组的方法

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>数组的方法</title>
  </head>
  <body>
    <script>
        let arr = ['h', 'a', 'd', 'b', 'f', 'j', 'c']
        console.log('arr', arr)
        
        /*
        sort()
            - 对数组进行排序,会改变原数组(破坏性的)
            - 默认进行升序排列,且按照Unicode编码进行排序
            - 参数:
                - 可以传递一个回调函数,用于指定排序规则
                    - (a, b) => a - b) 升序
                    - (a, b) => b - a) 降序
        */
        arr = [1, 3, 9, 2, 5, 8, 22, 13] //阿拉伯数字排序时需指定排序规则
        console.log('arr.sort()', arr.sort((a, b) => a - b))

        /*
        foreach() 
            - 遍历数组
            - 需要一个回调函数作为参数,数组中有几个元素,回调函数就执行几次
                - 回调函数中有三个参数:
                    1. element 当前元素
                    2. index 当前元素的下标索引
                    3. array 被遍历的数组
        */
       arr = ['孙笑川', '药水哥', 'Giao哥']
       arr.forEach((element, index, array) => {
        console.log('index:', index, ',element:', element, ',array:', array)
       })

       /*
       filter()
           - 将数组中符合条件的元素保存到一个新数组中(过滤)
           - 需要一个回调函数作为参数,根据回调函数的条件判断当前元素是否符合条件
       */
       // 筛选出数组中的偶数
       arr = [1, 3, 9, 2, 5, 8, 22, 13]
       console.log('arr.filter()', arr.filter(item => item % 2 === 0))
       
       /*
       map()
            - 根据当前数组,生成一个新的数组(非破坏性的)
            - 需要一个回调函数作为参数,回调函数的返回值,会成为新数组中的元素
       */
       // 所有元素扩大两倍
       console.log('arr.map()', arr.map(item => item * 2))

       /*
       reduce()
            - 可以将数组中的所有元素整合为一个值(归约)
            - 参数:
                1. 回调函数,指定元素的合并规则
                2. 初始值(可选)
       */
       console.log('arr.reduce()', arr.reduce((a, b) => a + b)) // 63
       console.log('arr.reduce()', arr.reduce((a, b) => a + b, 1)) //64
    </script>
  </body>
</html>

15. 可变参数

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>可变参数</title>
  </head>
  <body>
    <script>
        /*
        arguments:
            - 是函数中的有一个隐含参数
            - 是一个类数组对象(伪数组)
            - 用来存储函数的实参
                - 无论当前函数是否定义形参,传入的实参都会存储到arguments对象中
        */

        function test() {
            //for (let i = 0; i < arguments.length; i++) {
            //    console.log(arguments[i])
            //}
            
            //for (const element of arguments) {
            //    console.log(element)
            //}

            // 报错,arguments是类数组对象,并不是一个数组对象
            //arguments.forEach(element => console.log(element))
        }
        test(1, '孙笑川', 'abc', () => {})

        function sum() {
            let result = 0
            for (let i = 0; i < arguments.length; i++) {
                result += arguments[i]
            }
            return result
        }

        let result = sum(1, 2, 3)
        console.log('sum(1, 2, 3)=', sum(1, 2, 3))
        
        /*
        可变参数:
            - 可以接收任意数量的实参,并将它们存储到一个数组中
            - 可变参数和普通参数一起使用时,可变参数需写到最后的位置
        */
       function test1(...args) {
            console.log('args', args)
       }
       test1()

       function test2(a, b, ...args) {
            console.log('args', args)
       }
       test2(1, 'a', () => {}, 'test2', 123)

       function sum1(...args) {
            return args.reduce((a, b) => a + b)
       }
       console.log('sum1(1, 2, 3)=', sum(1, 2, 3))
    </script>
  </body>
</html>

16. call和apply

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>call和apply</title>
  </head>
  <body>
    <script>
        /*
        根据函数的调用方式不同,this也不同:
            1. 以函数形式调用,this是window
            2. 以方法的形式调用,this是调用方法的对象
            3. 在构造函数中,this是新建的对象
            4. 箭头函数没有自己的this,由外层作用域决定
            5. 通过call()/apply()调用的函数,传入的第一个参数就是函数的this
        */

        function test() {
            console.log('函数执行了...')
            console.log(this)
            
        }

        /*
        call()/apply:
            - 调用函数
            - 指定函数中的this(第一个参数)
        */
        test.call({name: '孙笑川'})
        test.apply(123)
        console.log('-----------------分割线-------------------')
        
        function test1(a, b) {
            console.log('a:', a, 'b:', b, 'this:', this)
        }
        test1(123, 456)

        // 通过call()方法调用时,函数的实参需跟在第一个参数之后
        test1.call({name: '孙笑川'}, 123, 456)

        // 通过apply()方法调用时,函数的实参需要通过一个数组传递
        test1.apply({name: '孙笑川'}, [123, 456])
    </script>
  </body>
</html>

17. bind

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>bind</title>
  </head>
  <body>
    <script>
        /*
        根据函数的调用方式不同,this也不同:
            1. 以函数形式调用,this是window
            2. 以方法的形式调用,this是调用方法的对象
            3. 在构造函数中,this是新建的对象
            4. 箭头函数没有自己的this,由外层作用域决定
            5. 通过call()/apply()调用的函数,传入的第一个参数就是函数的this
            6. 通过bind返回的函数,this由bind的第一个参数指定(且指定之后无法修改,不会因为调用方式的不同而不同)
        */
        /*
        bind():
            - 用于创建一个新的函数
            - 可以为新函数绑定this
        */

        function test(a, b, c) {
            console.log('函数执行了...', this)
            console.log('a:', a, 'b:', b, 'c:', c)
        }

        //由bind绑定的this,实参绑定之后无法修改(仅针对返回的新函数)
        const newFn = test.bind({name: '孙笑川'}, 1, 2, 3)
        newFn()

        // 注:箭头函数的this也无法通过call(),apply(),bind()方法指定,只能通过外层作用域决定
    </script>
  </body>
</html>
0

评论 (0)

取消