🧑‍💻JavaScript数据结构与算法-HowieCong

务必要熟悉JavaScript使用再来学!

数组

  • 在 JavaScript 中,数组是一种特殊的对象,用于存储多个值的有序集合

    1. 数组的创建

    // 方括号+元素内容
    const arr = [1,2,3]
    
    // 构造函数,等价于 const arr = []
    const arr = new Array();
    
    // 长度为7的数组
    const arr = new Array(7); // length: 7
    
    // 创建一个长度确定、同时每一个元素的值也确定的数组
    // 需求为每个元素的值为6,调用fill方法,给fill一个6
    const arr = (new Array(7)).fill(6) // (7)[6,6,6,6,6,6,6]
    
    // 使用扩展运算符复制数组
    // 扩展运算符(...)可以用于复制一个数组,创建一个浅拷贝的新数组
    const originalArr = [1, 2, 3];
    const copiedArr = [...originalArr]; 
    console.log(copiedArr); // [1, 2, 3]
    
    // 使用Array.from()的映射功能
    // Array.from()除了可以从类数组或可迭代对象创建数组,还可以接受一个映射函数作为第二个参数,对每个元素进行处理
    const arr = Array.from([1, 2, 3], num => num * 2); 
    console.log(arr); // [2, 4, 6]

2. 一维数组的访问和遍历

  • 访问数组的元素,直接在中括号中指定其索引即可:
arr[0] // 访问索引下标为0的元素
  • 遍历数组三种常见方法:

    如果没有特殊需要,统一使用for循环来实现遍历,从性能上看,for循环遍历起来是最快

(1)for循环

  • 最基础的方法 => 通过循环数组的下标 => 依次访问值
const len = arr.lengthh
for(let i = 0;i < len;i++){
    // 输出数组的元素值,输出当前索引
    console.log(arr[i],i)
}

(二)forEach方法

  • 通过foreach方法中传入函数的第一个入参和第二个入参,也可以取到数组每个元素的值及其索引
arr.forEach((item,index) => {
    // 输出数组的元素值,输出当前索引
    console.log(item,index)
})

(三)map方法

  • 和foreach方法差不多,区别在map会根据传入的函数逻辑对数组每个元素进行处理,返回一个全新的数组,map不单是遍历,而是在遍历的基础上再加工。需要对数组内容做批量修改、同时修改的逻辑又要高度一致,就可以调用map来达到我们的目的

```js
const newArr = arr.map((item,index) =>{
    // 输出数组的元素值,输出当前索引
    console.log(item,index)
    // 在当前元素的值基础上加1
    return item + 1
})

// 通过map返回一个全新的数组,数组的每个元素的值都是再其现有元素值的基础上+1后的结果
```

(四)for...of循环

  • for...of 循环是 ES6 引入的一种遍历可迭代对象的语法,数组是可迭代对象,因此可以使用 for...of 循环来遍历数组元素
const arr = [1, 2, 3]; 
for (const item of arr) { 
    console.log(item); 
}

(五)for...in循环

  • 虽然 for...in 循环主要用于遍历对象的可枚举属性,但也可以用于数组。不过需要注意的是,for...in 循环会遍历数组的所有可枚举属性,包括自定义的属性,并且遍历顺序可能不是按照数组索引的顺序
const arr = [1, 2, 3];
arr.customProperty = 'hello'; 
for (const index in arr) { 
    console.log(index, arr[index]); 
}

3. 二维数组

二维数组其实就是数组套数组,就是每个元素都是数组的数组

特点:相对于一维数组像一条线,而二维数组像一个面,在数学中,形如这样长方形排列的复数或实数集合,被称为矩阵,因此二维数组的别名就叫矩阵

const arr = [
    [1,2,3],
    [1,2,3],
    [1,2,3],
    [1,2,3],
    [1,2,3]
]

4. 二维数组的初始化

在for循环(简单且性能还不错)中每次迭代我们都通过[]来创建一个新的数组,这样不会引用指向问题带来的尴尬
const len = arr.length
for(let i = 0;i < length;i++){
    // 将数组的每一个坑位初始化为数组
    arr[i] = []
}

5. fill的局限性(不推荐使用在二维数组)

// 初始一个二维数组
const arr = (new Array(7)).fill([])

// 修改某个坑的值为1
arr[0][0] = 1 // 这一列的元素都设为了1
fill的工作机制,当给fill传入一个入参,如果这个入参的类型是引用类型,那么fill在填充坑位时填充的其实就是入参的引用。上述例子的七个数组对应同一个引用、指向的是同一块内存空间,本质上是同一个数组。因此当改变了第0行的第0个元素的值时,第1-6行的第0个元素的值也都是改变的

6. 二维数组的访问

和一维数组差不多,区别在于我们现在需要的是两层循环
// 缓存外部数组的长度
const outerLen = arr.length
for(let i = 0; i < outterLen; i++){
    // 缓存内部数组的长度
    const innerLen = arr[i].length
    for(let j = 0; j < innerLen; j++){
        // 输出数组的值,输出数组的索引
        console.log(arr[i][j],i,j)
    }
}
一维数组用for数组只需一层循环,二维数组就是两层,三维就是三层,以此类推,n维就是需要n层来遍历

7. 二维数组的浅拷贝和深拷贝

浅拷贝只会复制数组的一层,而深拷贝会递归地复制数组的所有层级
//浅拷贝
const original2DArr = [[1,2],[3,4]];
const shallow = [...original2DArr];
shallowCopy[0][0] = 10;
console.log(shallowCopy[0][0]); // 10

// 深拷贝
function deepCopy(arr){
    return JSON.parse(JSON.stiringify(arr));
}
const deepCopyArr = deepCopy(original2DArr);
deepCopyArr[0][0] = 20;
console.log(original2DArr[0][0]);// 10

8. 数组API合集(记忆)

以上是数组的基础知识,数组还有很多特性和技巧,在后续这个系列文章会罗列的,记忆API是个痛苦的事!下面是一些常用API,按照不同功能的对这些API进行分类:
8.1 数组创建与转换
  • Array.from()

    • 作用:从一个类数组对象或可迭代对象创建一个新的、浅拷贝的数组实例
    const str = 'hello';
    const arr = Array.from(str);
    console.log(arr); // ['h', 'e', 'l', 'l', 'o']
  • Array.of()

    • 作用:创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型
const arr = Array.of(1, 2, 3);
console.log(arr); // [1, 2, 3]
8.2 数组元素访问与修改
  • at()

    • 作用:接收一个整数值并返回该索引对应的元素,允许使用负整数从数组末尾开始计数
const arr = [10, 20, 30];
console.log(arr.at(-1)); // 30
  • copyWithin()

    • 作用:浅复制数组的一部分到同一数组中的另一个位置,并返回它,不会改变原数组的长度
const arr = [1, 2, 3, 4, 5];
arr.copyWithin(0, 3);
console.log(arr); // [4, 5, 3, 4, 5]
  • fill()

    • 作用:用一个固定值填充一个数组中从起始索引到终止索引内的全部元素
const arr = new Array(3);
arr.fill(1);
console.log(arr); // [1, 1, 1]
8.3 数组迭代
  • entries()

    • 作用:返回一个新的 Array Iterator 对象,该对象包含数组中每个索引的键 / 值对
const arr = ['a', 'b', 'c'];
const iterator = arr.entries();
for (const [index, value] of iterator) {
    console.log(index, value);
}
  • every()

    • 作用:测试一个数组内的所有元素是否都能通过某个指定函数的测试。
const arr = [1, 2, 3, 4];
const isAllPositive = arr.every(num => num > 0);
console.log(isAllPositive); // true
  • filter()

    • 作用:创建一个新数组,其包含通过所提供函数实现的测试的所有元素
const arr = [1, 2, 3, 4];
const evenNumbers = arr.filter(num => num % 2 === 0);
console.log(evenNumbers); // [2, 4]
  • find()

    • 作用:返回数组中满足提供的测试函数的第一个元素的值,否则返回 undefined
const arr = [1, 2, 3, 4];
const firstEven = arr.find(num => num % 2 === 0);
console.log(firstEven); // 2
  • findIndex()

    • 作用:返回数组中满足提供的测试函数的第一个元素的索引,若没有找到则返回 -1
const arr = [1, 2, 3, 4];
const index = arr.findIndex(num => num % 2 === 0);
console.log(index); // 1
  • flat()

    • 作用:创建一个新数组,所有子数组元素递归地连接到该数组中,直到达到指定的深度
const arr = [1, [2, [3]]];
const flattened = arr.flat(2);
console.log(flattened); // [1, 2, 3]
  • flatMap()

    • 作用:首先使用映射函数映射每个元素,然后将结果压缩成一个新数组
const arr = [1, 2, 3];
const result = arr.flatMap(num => [num * 2]);
console.log(result); // [2, 4, 6]
  • forEach()

    • 作用:对数组的每个元素执行一次提供的函数。
const arr = [1, 2, 3];
arr.forEach(num => console.log(num));
  • includes()

    • 作用:判断一个数组是否包含一个指定的值,根据情况返回 true 或 false
const arr = [1, 2, 3];
const hasTwo = arr.includes(2);
console.log(hasTwo); // true
  • indexOf()

    • 作用:返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回 -1
const arr = [1, 2, 3];
const index = arr.indexOf(2);
console.log(index); // 1
  • join()

    • 作用:将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串
const arr = ['a', 'b', 'c'];
const str = arr.join('-');
console.log(str); // 'a-b-c'
  • lastIndexOf()

    • 作用:返回指定元素在数组中的最后一个的索引,如果不存在则返回 -1
const arr = [1, 2, 3, 2];
const lastIndex = arr.lastIndexOf(2);
console.log(lastIndex); // 3
  • map()

    • 作用:创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果
const arr = [1, 2, 3];
const squared = arr.map(num => num * num);
console.log(squared); // [1, 4, 9]
  • reduce()

    • 作用:对数组中的每个元素执行一个由您提供的 reducer 函数(升序执行),将其结果汇总为单个返回值。
const arr = [1, 2, 3];
const sum = arr.reduce((acc, cur) => acc + cur, 0);
console.log(sum); // 6
  • reduceRight()

    • 作用:接受一个函数作为累加器(reducer)和数组的每个值(从右到左)将其减少为单个值
const arr = [1, 2, 3];
const result = arr.reduceRight((acc, cur) => acc - cur);
console.log(result); // 0
  • reverse()

    • 作用:将数组中元素的位置颠倒,并返回该数组
const arr = [1, 2, 3];
arr.reverse();
console.log(arr); // [3, 2, 1]
  • some()

    • 作用:测试数组中是不是至少有 1 个元素通过了被提供的函数测试
const arr = [1, 2, 3];
const hasEven = arr.some(num => num % 2 === 0);
console.log(hasEven); // true
  • sort()

    • 作用:对数组的元素进行排序,并返回数组。
const arr = [3, 1, 2];
arr.sort((a, b) => a - b);
console.log(arr); // [1, 2, 3]
  • toLocaleString()

    • 作用:返回一个字符串表示数组中的元素。数组中的元素将使用各自的 toLocaleString 方法转成字符串,这些字符串将使用一个特定语言环境的字符串(例如一个逗号 ",")隔开
const arr = [1, 'a', new Date()];
const str = arr.toLocaleString();
console.log(str); 
  • toString()

    • 作用:返回一个字符串,表示指定的数组及其元素
const arr = [1, 2, 3];
const str = arr.toString();
console.log(str); // '1,2,3'
8.4 数组元素添加与删除
  • pop()

    • 作用:从数组中删除最后一个元素,并返回该元素的值。此方法会更改数组的长度。
const arr = [1, 2, 3];
const last = arr.pop();
console.log(last); // 3
console.log(arr); // [1, 2]
  • push()

    • 作用:将一个或多个元素添加到数组的末尾,并返回该数组的新长度
const arr = [1, 2];
const newLength = arr.push(3);
console.log(newLength); // 3
console.log(arr); // [1, 2, 3]
  • shift()

    • 作用:从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度
const arr = [1, 2, 3];
const first = arr.shift();
console.log(first); // 1
console.log(arr); // [2, 3]
  • splice()

    • 作用:通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容
const arr = [1, 2, 3];
const removed = arr.splice(1, 1, 4);
console.log(removed); // [2]
console.log(arr); // [1, 4, 3]
  • unshift()

    • 作用:将一个或多个元素添加到数组的开头,并返回该数组的新长度
const arr = [2, 3];
const newLength = arr.unshift(1);
console.log(newLength); // 3
console.log(arr); // [1, 2, 3]
8.5 数组拼接与切片
  • concat()

    • 作用:用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组
const arr1 = [1, 2];
const arr2 = [3, 4];
const newArr = arr1.concat(arr2);
console.log(newArr); // [1, 2, 3, 4]
  • slice()

    • 作用:返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括 end)。原始数组不会被改变
const arr = [1, 2, 3, 4];
const sliced = arr.slice(1, 3);
console.log(sliced); // [2, 3]

❓其他

1. 疑问与作者HowieCong声明

  • 如有疑问、出错的知识,请及时点击下方链接添加作者HowieCong的其中一种联系方式或发送邮件到下方邮箱告知作者HowieCong
  • 若想让作者更新哪些方面的技术文章或补充更多知识在这篇文章,请及时点击下方链接添加里面其中一种联系方式或发送邮件到下方邮箱告知作者HowieCong
  • 声明:作者HowieCong目前只是一个前端开发小菜鸟,写文章的初衷只是全面提高自身能力和见识;如果对此篇文章喜欢或能帮助到你,麻烦给作者HowieCong点个关注/给这篇文章点个赞/收藏这篇文章/在评论区留下你的想法吧,欢迎大家来交流!

2. 作者社交媒体/邮箱-HowieCong


HowieCong
1 声望0 粉丝

大前端开发 => AI 小菜鸡!虚心好学!欢迎一起交流!每篇文章尾部有HowieCong联系方式!(Wechat|Instagram|Feishu|Juejin|Segmentfault...)