JavaScript权威指南(第7版) 笔记 - 第 7 章 数组

能用代码说清楚的,绝不多废话!!!!!!

Linux创始人Linus的名言:Talk is cheap,show me the code ! ,博主技术博文会精心给出能说明问题的范例代码!!!!!

⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️

第7章 数组

  • 数组是值的有序集合,同一数组的数组元素可以是不同的类型;

例:

let arr = [1,'2',{a:3},function(){}];
  • JavaScript数组是动态的,它们会按需增大或缩小;
let arr = [];// 空数组
arr[0] = 0; // 增大
arr[1] = 1; // 增大
arr.length = 1; // 缩小
delete arr[0]; // 缩小
  • JavaScript数组可以是稀疏的,即元素不一定具有连续的索引,中间可能有空位;
    例:
let data = [7, 8, 9];
data[10] = '';// 动态的、稀疏的

索引3~9都是空位。我们在浏览器中debug的截图如下
在这里插入图片描述

JavaScript数组是一种特殊的JavaScript对象,因此数组的索引更像是属性名,只不过这个属性名是整数而已;(看了上图我们更能深刻体会这句话)

console.log(9 in data);      // => false
  • JavaScript字符串的行为类似字母数组
console.log("abc"[2]); // => c

可以通过 [] 读取字符串中的字符

  • ES6增加了一批新的数组类,统称为“定型数组”,定型数组具有固定长度和固定的数值元素类型。定型数组具有极高的性能

TODO

参考博主其他博文:定型数组

7.1 创建数组

创建数组有4中方式:
1、数组字面量;
2、对可迭代对象使用…扩展操作符;
3、Array() 构造函数;
4、工厂方法 Array.of()和 Array.from();

7.1.1 数组字面量

(1)元素可以是常量

let empty = [];                 // 空数组
let primes = [2, 3, 5, 7, 11];  // 5个数值元素的数组
let misc = [1.1, true, "a",];   // 不同类型元素的数组,末尾逗号会被忽略misc的length为3

⚠️ 数组中末尾的逗号会被忽略。

(2)元素可以是变量或表达式

let base = 1024;
let table = [base, base + 1, base + 2, base + 3];

(3)逗号之间没有值的情况

let count = [1, , 3]; // 索引0、2处有元素 length为3,
let undefs = [, ,];  // 忽略末尾逗号,length为2

在这里插入图片描述
在这里插入图片描述

⭐️⭐️ 把数组看作对象、索引看作对象的属性(属性不需要数字连续),上面数组的表现就很容易理解了。

7.1.2 扩展操作符

let a = [1, 2, 3];
let b = [0, ...a, 4];  // b == [0, 1, 2, 3, 4]

...a 想象成代表数组a的所有元素

扩展操作符

(1)扩展操作符创建数组(浅)副本

let original = [1, 2, 3];
let copy = [...original];
copy[0] = 0;  // 修改copy中的元素值,不影响original中元素值
original[0]   // => 1

(2)扩展操作符适用于任何可迭代对象

可迭代对象:数组、字符串、Set、Map

let arr =[];
// 1 字符串
arr = [..."abc"];
console.log(arr);// ['a', 'b', 'c']
// 2、数组
let other_arr = [1,2,3];
arr = [...other_arr];
console.log(arr);// [1, 2, 3]// 3、Set
let set = new Set().add(1).add(2).add(3);
arr = [...set];
console.log(arr);// [1, 2, 3]// 4、Map
let map = new Map().set("one",1).set("tow",2).set("three",3);
arr = [...map];
console.log(arr);// [['one', 1],['tow', 2],['three', 3]]

(3)应用Set数组去重

let letters = [..."hello world"];
letters = [...new Set(letters)]  // => ["h","e","l","o"," ","w","r","d"]

7.1.3 Array()构造函数

let a1 = new Array();               // 等价于 let a1 = [];
let a2 = new Array(10);             // 等价于 let a2 = [];a2.length = 10;
let a3 = new Array(3, 2, 1, "t,t"); // 等价于 let a3 = [3, 2, 1, "t,t"];

各个数组内部状态如下图:
在这里插入图片描述
字面量创建数组更简单,且完全替代构造函数创建数组。

⚠️ ⚠️ new Array(n) 这种方式创建数组,从上图中看,并不会预先分配空间。但对JavaScript引擎可能是有用的,可以根据n的大小来决定分配在内存的哪块区域 (根据程序访问的局部性原理,连续的内存可能会带来性能优势)。

在JavaScript中,数组的内存并不需要连续的内存区域。事实上,JavaScript的数组在内存中的实现与许多其他编程语言中的数组有本质的区别。

在JavaScript中,数组实际上是一种特殊的对象,用于存储有序的元素集合。这些元素可以是任何数据类型,并且数组的大小可以动态地增长和缩小。JavaScript的数组底层实现通常依赖于一种称为“散列表”或“哈希表”的数据结构,而不是传统的连续内存块。

使用散列表或哈希表作为底层数据结构意味着数组的每个元素都可以通过其索引(键)直接访问,而不需要遍历整个数组。这种实现方式提供了高效的插入、删除和查找操作,但也意味着数组的元素在内存中的位置可能并不连续。

因此,JavaScript的数组并不要求连续的内存区域来存储元素。相反,它们可以根据需要动态地分配和释放内存,以适应不同大小的数组和不同的操作需求。这种灵活性使得JavaScript的数组在处理各种复杂的数据结构和算法时非常有用。

需要注意的是,虽然JavaScript的数组不需要连续的内存区域,但在某些情况下,连续的内存访问可能会带来性能优势。然而,这通常是由JavaScript引擎的优化机制来处理的,而不是由开发者直接控制的。开发者主要关注的是如何使用数组来组织和操作数据,而引擎则会负责底层内存管理的细节。

7.1.4 Array.of()

数组元素传递给Array.of(),来创建并返回新数组。

Array.of()        // => []; 创建一个空数组
Array.of(10)      // => [10]; 
Array.of(1,2,3)   // => [1, 2, 3]

7.1.5 Array.from()

(1)类数组对象转数组

所谓类数组对象就是具有length属性的对象。

需要注意的是,对象中 “整数类型” 属性对应的值才能转为数组的元素。

let arrayLike = { 0: 'a', '1': 'b', 'c': 'c', length: '5' }; 
// 转换为数组  
let arr = Array.from(arrayLike);

在这里插入图片描述

⚠️ ⚠️
1、类数组对象的length需要是数值或数值字符串( 例如 “5.8”,JavaScript会转化为整数5)

2、类数组对象的属性必须是整数或整数字符串才能转为数组元素。(例如: 属性"5",JavaScript会将其转化为整数5) ;

3、Array.from转换时,如果length大于元素个数,JavaScript会将空位部分置为undefined

例:

let obj = {1: 1,"2": function fun() { console.log("fun"); },"4": 4,"4.9": 4.9,  // 属性不是整数,不能转换为数组元素length: 5.8, // 转换为整数5
};
let arr = Array.from(obj);
console.log(arr);// [undefined, 1, ƒ, undefined, 4]

⚠️ Array.from()方法内部会使用length对数组进行裁剪(见下面的程序),我们将obj中的length改为2,输出:[undefined, 1],以length为准。

let obj = {....length: 2,
};
let arr = Array.from(obj);
console.log(arr);// [undefined, 1]

🔗 通过设置length删除数组元素参考7.4节

(2)可迭代对象转数组

可迭代对象包括:1 字符串、2 数组、3 Set、4 Map

let arr =[];
// 1 字符串
arr = Array.from("abc");
console.log(arr);//  ['a', 'b', 'c']
// 2、数组
arr =Array.from([1,2,3]);
console.log(arr);//[1, 2, 3]// 3、Set
let set = new Set().add(1).add(2).add(3);
arr =Array.from(set);
console.log(arr);//[1, 2, 3]// 4、Map
let map = new Map().set("one",1).set("tow",2).set("three",3);
arr =Array.from(map);
console.log(arr);//[['one', 1],['tow', 2],['three', 3]

(3)Array.from()的第2个参数:处理函数

处理函数接收两个参数:(数组元素,索引)

let arr1 = [1,3,5,7,9];
let arr2 = Array.from(arr1,(e,i)=>{return e>5?e:null;
})
console.log(arr2);// [-1, -1, -1, 7, 9]

稀疏数组,例:

let arr1 = [1,,5,,9];
let arr2 = Array.from(arr1,(e,i)=>{return e>5?e:null;
})
console.log(arr2);//  [-1, -1, -1, -1, 9]

在这里插入图片描述
⭐️ 从出的输出结果可以看出,稀疏数组的空位会被遍历。

7.2 读写数组元素

可以使用[]操作符读写数组元素,方括号里的内容称为数组的索引值,合法的索引值是0 ~ 4294967294 ( 2 32 2^{32} 232 -2) 。

(1) 合法的索引值-读写数组

  • 下面是几个常规的写数组元素示例:
let arr = [];
arr[0] = 0;
let one = 1;
arr[0 + one] = 1; // []中可以使用变量、表达式
  • 合法的整数字符串作为索引,与该整数作为索引是等价的,JavaScript会自动将整数字符串转为整数
let arr = [];
arr[0] = 0;
arr["1"] = 1;// 与arr[1] = 1等价for (let k of arr.keys()){console.log(k,typeof k);// 0 'number';1 'number'
}

⚠️ 对象正好相反,会将数值类型的属性转换为字符串类型

let o = {};    // Create a plain object
o[1] = "one";
let keys = Object.keys(o);
for (const k of keys) {console.log(k,typeof k);// 1 string
}
  • 合法的索引会让JavaScript自动更新数组的length属性,length = 最大索引 + 1
let arr = [];
arr[100] = 100;// length为101(见下图)

在这里插入图片描述

(2) 非法的索引值- 添加属性&值

  • 如果索引值是非法的,JavaScript就会将其作为普通的对象的属性处理,此时该数组就是一个普通的对象。

示例:

let arr = [];
arr[-1] = -1; // 超出了合法值范围
arr[4294967295] = 4294967295;// 超出了合法值范围 
arr["one"] = "one"; // 索引值非法

在这里插入图片描述

从上图看出length值为0,arr被当成了一个普通对象,添加了3个普通对象的属性(“-1”、“one”、“4294967295”)

7.3 稀疏数组

  • 正常稠密数组是从0开始的连续索引数组,而稀疏数组是一个元素不连续索引的数组。所以稀疏数组的length值是大于数组元素个数的。

中文书中这句话翻译的不对:“稀疏数组就是其元素没有从0开始的索引的数组
A sparse array is one in which the elements do not have contiguous indexes starting at 0。
稀疏数组是一个元素不连续索引的数组,正常数组是从0开始的连续索引数组。

let a = new Array(5); // 创建一个空数组,length设置为5,数组中并没有元素
a = [];               // a引用一个空数组,length设置为0
a[1000] = 0;		  // 数组a的1000索引位置赋值为0,length=1001(索引最大值+1)

逐条执行3条语句a的状态见下面3个图:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

⚠️⚠️ 稀疏数组的空位JavaScript并不会将其设置为undefined/null,用户访问不存在的元素时显示undefined是因为访问了对象不存在的属性。

7.4 数组长度

length可以用来删除数组元素

let a = [1,2,3,4,5]; // 创建一个有5个元素的数组,length自动设置为5
a.length = 3; // length被设置为0,JavaScript只保留前3个元素,变成了[1,2,3]
a.length = 0; // length被设置为0,JavaScript只保留前0个元素,变成了[]空数组,
a.length = 5; // length被设置为5, 数组中并没有元素

7.5 添加和删除数组元素

(1)索引添加

let a = [];      // Start with an empty array.
a[0] = "zero";   // 索引0位置放入一个字符串zero
a[1] = "one"; 	 // 索引1位置放入一个字符串one

在这里插入图片描述

(2)delete置成空位变稀疏(length不变)

let a = [1,2,3];
delete a[1];   // a now has no element at index 2
1 in a         // => false: 索引1位置未定义
a.length       // => 3: delete一个元素不影响数组length,数组变稀疏

在这里插入图片描述

7.6 迭代数组

7.6.1 for-of 迭代数组

(1)迭代数组

let arr = [0,1,2];
arr[10] = 10;
for (const e of arr) {console.log(e);
}

for/of使用数组内置的迭代器,按照升序逐个遍历数组的元素。
在这里插入图片描述

(2)迭代数组的entries()和解构赋值

可以使用数组的entries()方法和解构赋值

let arr = [1, 2, 3];
for (const [index, element] of arr.entries()) {console.log(typeof index, index, ':', element);
}

输出:
number 0 : 1
number 1 : 2
number 2 : 3

7.6.2 forEach()迭代数组

需要一个函数作为forEach的参数,该函数有3个形参(数组元素,索引,原数组)

let letters = [..."Hello world"];
let uppercase = "";
letters.forEach((element, index,arr) => {  // Note arrow function syntax here arr[index] = element.toUpperCase();//console.log(arr);
});
console.log(letters);// ['H', 'E', 'L', 'L', 'O', ' ', 'W', 'O', 'R', 'L', 'D']

7.7 多维数组

JavaScript并不支持真正的多维数组,但我们可以使用数组的数组来模拟

let arrarr = [];
arrarr[0] = [11,12,13];
arrarr[1] = [21,22,23];
console.log(arrarr[1][1]);// 22

7.8 数组方法

7.8.1 数组迭代器方法

本节介绍的方法:
forEach() 、map() 、find()、findIndex()、ervery()、 some()、reduce()、reduceRight()
这些方法会按照顺序,把数组的每个元素传给我们提供的函数。

(1)forEach()

  • 方法的参数是一个函数,函数接收3个参数 (数组元素,索引,原数组),不修改原数组
let data = [1, 2, 3, 4, 5], sum = 0;
// 计算数组元素之和
data.forEach(e=> { sum += e; }); 
console.log("sum:", sum);// sum:15

forEach()方法本身不修改数组,但传递给方法的函数可以修改原数组。

let data = [1, 2, 3, 4, 5], sum = 0;
//递增每个元素的值
data.forEach(function (e, i, a) { a[i] = e + 1;}); // data == [2,3,4,5,6]
console.log(data);// => [2, 3, 4, 5, 6]

(2)map()

map 方法的参数是一个函数,函数接收3个参数 (数组元素,索引,原数组),不修改原数组。

  • map方法会遍历数组中的元素,循环调用传递给map方法的函数,map()返回一个和原数组 “结构相同” 的新数组,但不修改调用它的数组;

  • “结构相同” 是指:“如果原数组是稀疏的,返回的新数组仍然是稀疏的,长度与原数组相同,缺失的元素也相同”

let arr1 = [0, 1, 2];
// map的参数是个函数,该函数接受3个参数(数组元素,索引值,原数组)
let arr2 = arr1.map(function (element, index, array) {// map方法会遍历数组中的元素,循环调用本函数console.log(element, index, array);return element * element;// 返回值作为新数组中的元素
});
console.log(arr1);// [0, 1, 2]
console.log(arr2);// [0, 1, 4]

稀疏数组:

let arr1 = [0, 1, 2];
arr1[5] = 5;// [0, 1, 2, , ,5]
let arr2 = arr1.map(function (element) {return element * element;
});
console.log(arr1);//[0, 1, 2, empty × 2, 5]
console.log(arr2);//[0, 1, 4, empty × 2, 25]

在这里插入图片描述

(3)filter()

filter方法的参数是一个函数,函数接收3个参数 (数组元素,索引,原数组),不修改原数组。

filter方法会遍历数组中的元素,循环调用传递给filter方法的函数,filter()返回一个 “ 结构可能不同 ” 的新数组。

let arr = [5, 4, 3, 2, 1];
let arr1 = arr.filter(e => e < 3) // 筛选小于3的元素
console.log(arr);  // [5, 4, 3, 2, 1],fitler并不改变原数组
console.log(arr1); // [2, 1]; let arr2 = arr.filter((e, i) => i % 2 === 0)// 筛选索引为偶数
console.log(arr2);// [5, 3, 1]

(4)find()与 findIndex()

find和findeIndex两个方法的参数都是一个函数,函数接收3个参数 (数组元素,索引,原数组),不修改原数组。

  • find方法会遍历数组中的元素,循环调用传递给find方法的函数,找到第一个就会停止,返回第一个匹配上的元素

  • findIndex 和find唯一不同就是返回值,find返回第一个匹配上的元素,findIndex返回第一个匹配上的索引

// 1、找出数组中第一个能被5整除的数
let arr = [1, 20, 3, 40, 3];
let e1 = arr.find(function(e,i,arr){return e % 5 === 0;
});
console.log(e1);// 元素值是5的倍数,第一个是20// 2、找出数组中第一个能被7整除的数
let e2 = arr.find(e => e % 7 === 0);
console.log(e2);// undefined,数组中没有元素值是7的倍数元素,没找到返回undefinedlet idx1 = arr.findIndex(e => e === 3)  
console.log(idx1);// 2,第一个等于3的元素索引是2
let idx2 = arr.findIndex(e => e < 0)    
console.log(idx2); // -1; 数组中没有小于0的元素,没找到返回-1

⚠️ 没找到,find返回undefined,findIndex返回-1

(5)every()与some()

every()和some()方法是数组断言函数,方法值接收一个断言函数,函数的参数:(数组元素)。两个方法的返回值都是boolean类型值(true/false;

every()和some()方法内部会调用用户传递过来的断言函数。

(5.1)every()

every()方法会将数组元素依次传递给断言函数。全部为true则返回true(有一个false则立刻返回false)。

let a = [1, 2, 3, 4, 5];
console.log(a.every(x => x < 10));      // => true: 所有元素的只都小于10
console.log(a.every(x => x % 2 === 0)); // => false: 不是所有的元素都是偶数let r = a.every(function(e){console.log(e);//依次输出'1,2,3'return e<3;// 遇到3则返回false,后面的元素不用判断
}); 
console.log(r); // false

(5.2)some()

将数组元素依次传递给断言函数,有一个为true则返回true(有一个false则立刻返回false)。

let a = [1, 2, 3, 4, 5];
console.log(a.some(x => x % 2 === 0)); // => true;  有偶数的数组元素
console.log(a.some(isNaN));            // => false; 没有非数字的数组元素
console.log(a.some(largeThan3));       // => true ; 有大于4的数组元素function largeThan3(v) {console.log(v);// //依次输出'1,2,3,4'return v > 3; // 遇到4则返回true,后面的元素不用判断
}

(6)reduce()与reduceRight()

reduce()与reduceRight()方法只用我们使用指定的函数,归并数组元素,最终产生一个值

reduce()与reduceRight()方法的参数 : (归并函数 , [初始值])

(6.1)reduce()有初始值

let a = [1, 2, 3, 4];
console.log(a.reduce((x, y) => x + y, 0));// => 15; the sum of the values
/*** 参数传递、计算过程:* 0,1  -> reduce -> 1  ;  0是初始值作为第1个参数,1数组的第1个元素值,和为1* 1,2  -> reduce -> 3  ;  1是上一次计算的结果,2是数组的第2个元素值,和为3* 3,3  -> reduce -> 6  ;  3是上一次计算的结果,3是数组的第3个元素值,和为6* 6,4  -> reduce -> 10 ;  6是上一次计算的结果,4是数组的第4个元素值,和为10* 10,5 -> reduce -> 15 ; 10是上一次计算的想·想··想·结果,5是是数组的第4个元素值,和为15*/console.log(a.reduce((x, y) => x * y, 1)); // => 120; the product of the values
/*** 参数传递、计算过程:* 1,1  -> reduce -> 1   ;  1是初始值作为第1个参数,1是数组的第1个元素值,积为1* 1,2  -> reduce -> 2   ;  1是上一次计算的结果,2是数组的第2个元素值,积为2* 2,3  -> reduce -> 6   ;  2是上一次计算的结果,3是数组的第3个元素值,积为6* 6,4  -> reduce -> 24  ;  6是上一次计算的结果,4是数组的第4个元素值,积为24* 24,5 -> reduce -> 120 ; 24是上一次计算的结果,5是数组的第4个元素值,积为120*/

(6.2)reduce()无初始值

let a = [1, 2, 3, 4, 5];
console.log(a.reduce((x, y) => (x > y) ? x : y));// => 5; 
/*** 参数传递、计算过程:* 1,2  -> reduce -> 2  ;  1是数组的第1个元素值,2是数组的第2个元素值,大的数为2* 2,3  -> reduce -> 3  ;  2是上一次比较的结果,3是数组的第3个元素值,大的数为3* 3,4  -> reduce -> 4  ;  3是上一次比较的结果,4是数组的第4个元素值,大的数为4* 4,5  -> reduce -> 5  ;  4是上一次比较的结果,5是数组的第5个元素值,大的数为5*/console.log(a.reduce((x, y) => x + y)); // => 15; the sum of the values
/*** 参数传递、计算过程:* 1,2  -> reduce -> 3  ;  1是数组的第1个元素值,2是数组的第2个元素值,和为3* 3,3  -> reduce -> 6  ;  3是上一次计算的结果,3是数组的第3个元素值,和为6* 6,4  -> reduce -> 10 ;  6是上一次计算的结果,4是数组的第4个元素值,和为10* 10,5 -> reduce -> 15 ; 10是上一次计算的结果,5是数组的第5个元素值,和为15*/

(6.3)reduceRight()有初始值

reduceRight()与reduce()类似,只不过reduceRight是从高索引向低索引处理数组

let a = [1, 2, 3];
let val = a.reduceRight(function (acc, val) {console.log(acc, val);return Math.pow(acc, val)
}, 4); // => 4096
console.log(val);
/*** 参数传递、计算过程:*    4,3  -> reduce -> 64   ; 4是初始值作为第1个参数,3数组的倒数第1个元素值,4的3次方为64*   64,2  -> reduce -> 4096 ; 64是上一次计算的结果,2是数组的倒数第2个元素值,64的3次方为4096* 4096,1  -> reduce -> 4096 ; 4096是上一次计算的结果,1是数组的倒数第3个元素值,4096的1次方为4096*/

(6.3)reduceRight()无初始值

reduceRight()与reduce()类似,只不过reduceRight是从高索引向低索引处理数组

let a = [1, 2, 3];
let val = a.reduceRight(function (acc, val) {console.log(acc, val);return Math.pow(acc, val)
}) // => 9
console.log(val);
/*** 参数传递、计算过程:*    3,2  -> reduce -> 9 ; 3是数组的倒数第1个元素值,2是数组的倒数第2个元素值,3的2次方为9*    9,1  -> reduce -> 9 ; 9是上一次计算的结果,1是数组的倒数第3个元素值,9的1次方为9*/

7.8.2 使用flat()和flatMap()打平数组

(1)flat

打平数组中的数组类型的元素,例:

let arr = [1, [2, [3]]].flat()
console.log(arr);// => [1, 2, [3]]

从输出看出,数组中的数组[2, [3]] ,只被 “剥”开了1层,也称打平了1层。

通过参数可以设定打平的层数,例

let arr = [1, [2, [3]]].flat(2);
console.log(arr);// => [1, 2, 3] : 元素是数组的最深只有2层arr = [1, [2, [3]]].flat(3);
console.log(arr);// => [1, 2, 3] : 元素是数组的最深只有2层,参数>2效果和2一样

(1.1)空数组打平后就消失了

let arr1 = [[],[]]
console.log(arr1.length);// 2
let arr2 = arr1.flat();
console.log(arr2.length);// 0

(2)模拟flat实现

下面模拟flat实现的代码实现可以参考一下,体会一下数组元素中存在数组 && 还没有达到用户设定的打平层数,则继续打平1层

let _arr = new _Array([1, [2, [3]]]);
console.log(_arr._flat());// [1, 2, [3]]
console.log(_arr._flat(2)); // [1, 2, 3]function _Array(vals) {this.vals = vals;this._flat = function (n) {let existsElementIsArray = false, flat_counter = 1, flatArr = _flat1(this.vals);/* 数组元素中存在数组 && 还没有达到用户设定的打平层数,则继续打平1层 */while (existsElementIsArray && flat_counter <= n) {flatArr = _flat1(flatArr);}return flatArr;/* 打平一层 */function _flat1(arr) {flat_counter++;let _arr = []for (const e of arr) {if (Array.isArray(e)) {//打平1层放入_arrfor (const _e of e) {existsElementIsArray = Array.isArray(_e);_arr.push(_e)}} else {_arr.push(e)}}return _arr;}}return this;
};

(2)flatMap

这个方法名叫做mapFlat更合适,该方法其实是先map再flat,且flat只打平1层。

(1)先map再flat

let phrases = ["hello world", "the definitive guide"];
let words = phrases.flatMap(phrase => phrase.split(" "));
console.log(words); // => ["hello", "world", "the", "definitive", "guide"];

(2)空数组打平后就消失了

let arr = [-2, -1, 1, 2].flatMap(x => x < 0 ? [] : Math.sqrt(x));
console.log(arr);// [1, 1.4142135623730951]

7.8.3 使用concat()添加数组

concat(新元素1 , .... , 新元素n),原数组拼接参数中给出的新元素,如果新元素是数组,则打平1层后再进行拼接。

let arr = [1, 2, 3];
arr.concat(4, 5)          // => [1,2,3,4,5]
arr.concat([4, 5], [6, 7])   // => [1,2,3,4,5,6,7]; 拼接的是数组,打平1层进行拼接
arr.concat(4, [5, [6, 7]])  // => [1,2,3,4,5,[6,7]]; 拼接的是数组,打平1层进行拼接
console.log(arr); // [1, 2, 3] ,原数组没有改变

7.8.4 push(), pop(),shift()和unshift()实现栈和队列操作

push(), pop(),shift()和unshift()改变原数组

添加删除
数组尾部操作push()pop()
数组头部操作unshift()shift()

在这里插入图片描述

console.log("---------push---------");
let arr = [];
console.log(arr.push(1,2));      //=> 2
console.log(arr);                //=> [1, 2]
console.log(arr.push(3,4));      //=> 4
console.log(arr);                //=> [1, 2, 3, 4]console.log("--------- pop --------");
console.log(arr.pop());          //=> 4, 返回pop前的数组长度
console.log(arr.length);         //=> 3
console.log(arr);                //=> [1, 2, 3]console.log("------- unshift ------");
arr = [];
console.log(arr.unshift(1,2));   //=> 2
console.log(arr);                //=> [1, 2]
console.log(arr.unshift(3,4));   //=> 4
console.log(arr);                //=> [3, 4, 1, 2]console.log("-------- shift -------");
console.log(arr.shift());        //=> 3, 返回shift后的数组长度
console.log(arr);                //=> [4, 1, 2]

4个操作都会返回数组的长度;
⭐️ 比较特殊的是pop()函数返回的是pop前的数组长度,其他三个函数都是返回操作后的数组长度

7.8.5 使用slice()、splice()、fill()、copyWithin()

slice /slaɪs/ v. 把……切成薄片;切
splice /splaɪs/ v. 拼接

(1)slice([开始索引(0)],[结束索引(length)])

⭐️ 不修改原数组。
说明:slice([开始索引(0)],[结束索引(length)]) ,提取参数指定位置范围内元素组成的数组。
返回:返回指定位置范围内元素组成的数组

如果指定的索引是负数,则表示倒数的位置。

let arr = [0, 1, 2, 3, 4];
console.log(arr.slice());       // [0, 1, 2, 3, 4],返回0~length-1
console.log(arr.slice(0, 3));   // [0, 1, 2],返回索引位置"0~2"的元素组成的数组
console.log(arr.slice(3));      // [3, 4],返回索引位置"3~最后"的元素组成的数组
console.log(arr.slice(1, -1));  // [1, 2, 3],返回索引位置"1~倒数第1前"的元素组成的数组
console.log(arr.slice(-3, -2)); // [2] ,返回索引位置"倒数第3~倒数第3(倒数第2前)"的元素组成的数组
console.log(arr);               // [0, 1, 2, 3, 4] ,不影响原先的数组

(2)splice 删除&插入

⭐️ splice 修改了原数组。

说明:splice(删除起始索引,[删除的长度]), splice删除参数指定范围的数组元素。如果省略删除长度,则从起始索引开始到数组末尾的元素都会被删除。

返回:返回被删除元素组成的数组。

(2.1)指定位置、指定长度删除

splice(删除起始索引,[删除的长度(默认值length)])

let arr1 = [1, 2, 3, 4, 5, 6, 7, 8];
console.log(arr1.splice(4)); // [5, 6, 7, 8]:默认长度数组的length
console.log(arr1); // [1, 2, 3, 4]
console.log(arr1.splice(1, 2)); // [2,3]
console.log(arr1); // [1,4]
console.log(arr1.splice(1, 1));// [4]
console.log(arr1);// [1]

(2.2)删除再插入

splice(删除和插入的起始索引,删除的长度 , ...插入的数据), 插入位置和插入位置后面的数组元素会后移。

let arr2 = [1, 2, 3, 4, 5];
console.log(arr2.splice(2, 0, "a", "b"));// []: 删除长度为0-未删除任何元素
console.log(arr2);// [1,2,"a","b",3,4,5]console.log(arr2.splice(2, 2, [1, 2], 3));// ["a","b"]
console.log(arr2);// [1,2,[1,2],3,3,4,5]

(3)fill(指定的值,[起始索引(0)],[结束索引(length)])

⭐️ fill 修改了原数组。

说明:fill(指定的值,[起始索引(0)],[结束索引(length)]),使用指定的值,填充数组指定位置的内容。

返回:返回修改后的数组。

let a = new Array(5);
a.fill(0)               // => [0,0,0,0,0]; 未指定起始、结束索引,则整个数组元素被设置为0
a.fill(9, 1)            // => [0,9,9,9,9]; 未指定结束索引,则从起始索引到末尾都被设置为9
a.fill(8, 2, -1)        // => [0,9,8,8,9]; 索引2位置到倒数第1前的数组元素被设置为8

(4)copyWithin(被覆盖起始索引,[复制起始索(0)],[复制终止索引(length)])

⭐️ copyWithin 修改了原数组。

说明:copyWithin(被覆盖起始索引,[复制起始索(0)],[复制终止索引(length)]),复制“起始索引” 到 “终止索引” 前的元素被复制,从被覆盖起始索引处开始覆盖原数组元素。

返回:修改后的数组

例1:

let arr = [1, 2, 3, 4, 5];
let _arr = arr.copyWithin(2, 3, 5);
console.log(_arr == arr);// true
console.log(arr);//[1,2,4,5,5]

在这里插入图片描述

例2:

let a = [1,2,3,4,5];
a.copyWithin(1); // => [1,1,2,3,4]: 起始索引默认0,终止索引默认值为length
a.copyWithin(2, 3, 5); // => [1,1,3,4,4]
a.copyWithin(0, -2)   // => [4,4,3,4,4]

a.copyWithin(1) 超出原有数组长度的部分会被忽略
在这里插入图片描述

7.8.6 查找与排序

(1)indexOf

⭐️ indexOf不修改原数组。

说明: indexOf(搜索内容 , [开始索引]), 从前到后在数组中搜索参数指定的内容
返回: 第一个匹配到的元素在数组中的索引值。未找到返回-1

let a = [0, 1, 2, 1, 0];
console.log(a.indexOf(0));      // => 0
console.log(a.indexOf(3));      // => -1console.log(a.indexOf(0,1));    // => 4,

(2)lastIndexOf

⭐️ lastIndexOf不修改原数组。

说明: lastIndexOf(搜索内容 , [开始索引]) , 从后向前在数组中搜索参数指定的内容

返回: 第一个匹配到的元素在数组中的索引值。

let a = [0, 1, 2, 1, 0];
console.log(a.lastIndexOf(0));    // => 4: 由后向前,第一个为0的是a[4]
console.log(a.lastIndexOf(0,-2)); // => 0: 由后向前,从倒数第2个开始找,第一个为0的是a[0]

(3)indexOf的应用 - 元素在数组中的所有位置索引

let a = [,2,3,2];
function findall(a, x) {let results = [],            // The array of indexes we'll returnlen = a.length,          // The length of the array to be searchedpos = 0;                 // The position to search fromwhile (pos < len) {           // While more elements to search...pos = a.indexOf(x, pos); // Searchif (pos === -1) break;   // If nothing found, we're done.results.push(pos);       // Otherwise, store index in arraypos = pos + 1;           // And start next search at next element}return results;              // Return array of indexes
}
console.log(findall(a,2));

简化了一下:

let a = [,2,3,2];
function _findall(a, x) {let results = [], pos = 0;while (true) {pos = a.indexOf(x, pos);if (pos === -1) break;results.push(pos);pos++;}return results;
}
console.log(_findall(a,2)); //

(3)includes

⭐️ includes不修改原数组。

说明: includes(搜索内容) ,是否包含搜索的内容

返回: true/false

例:

let arr1 = ["hello"];
let arr2 = arr1;
let arr = [[],[],arr1,[],NaN];
console.log(arr.indexOf(arr2));  // 2
console.log(arr.includes(arr2)); // trueconsole.log(arr.indexOf([]));  // -1
console.log(arr.includes([])); // falseconsole.log(arr.indexOf(NaN));   // -1  , indexOf相等性测试使用的是`===`,无法判断NaN
console.log(arr.includes(NaN));  // true, includes可以测试是否包含NaN

注意NaN

(4)sort(function(a,b){…}) 排序

⭐️ sort 修改了原数组。

说明: sort(function(a,b){...;return 正数/负数}) 对数组进行排序,正序排序则比较函数返回值使用 return a-b; ,倒序排序则比较函数返回值使用return b-a;。排序是稳定的,即相等的两个元素顺序不改变。
返回: 排序后的数组

let arr = [1,4,,36,2]
console.log(arr);// [1, 4, empty, 36, 2]
arr.sort();
// 按字母顺序排列,未定义的元素,它们会被排到数组末尾
console.log(arr);// [1, 2, 36, 4, empty]
arr.sort((a,b)=>{return a-b;
});
console.log(arr);//  [1, 2, 4, 36, empty]

(5)sort排序算法

排序算法采用的是快速排序,把数组的后一个元素传递给比较参数的第一个参数,前一个元素传递给比较函数的第二个参数,如果返回值是正数则不交换位置,负数则交换位置。


(6)reverse 反转数组顺序

let arr = [1,3,5,2,4,6];
console.log(arr.reverse());// [6, 4, 2, 5, 3, 1]
console.log(arr); // [6, 4, 2, 5, 3, 1]

7.8.7 数组到字符串的转换

Array类定义了 3个把数组转换为字符串的方法:join、toString、toLocalString

(1)join

把数组中所有的元素转换为字符串,按顺序依次拼接成一个字符串。

参数: 1个可选参数,用来分隔拼接的字符串。(默认值为逗号 “,”
返回值:拼接后的字符串
原数组:不变,改变

let arr = [1, 2, 3];
arr.join()               // => "1,2,3"
arr.join(" ")            // => "1 2 3"
arr.join("")             // => "123"

⚠️稀疏数组的空位会以空字符串参与拼接

let arr = new Array(5);
console.log(arr.join("-"));// => "----" 4个分隔符(n个元素有n-1个分隔符)
console.log(arr.join());   // => ",,,,"

(1)toString()

无参数,与没有参数的join方法一样(采用逗号分隔)

(1)toLocalString()

无参数,考虑本地化的格式

let arr = [new Date(),"hello"];
console.log(arr.toString());// Sun Apr 07 2024 21:59:12 GMT+0800,hello
console.log(arr.toLocaleString());// 2024/4/7 21:59:12,hello

7.8.8 静态数组函数

Array有 3个静态函数:Array.of()、Array.from()、Array.isArray()

console.log(Array.isArray([]));  // true
console.log(Array.isArray({}));  // false
console.log(Array.isArray(1));   // false
console.log(Array.isArray("1")); // false
console.log(Array.isArray({length:1,a:"a"})); // false 类数组对象仍然是falseconsole.log(typeof []);          // object

⚠️ typeof 并不能判断一个对象是否是数组

非原始类型(如对象、数组、函数、null等),typeof 都会返回 “object”

7.9 类数组对象

let obj = {1: 1,"2": 2,"f": function fun() { },length: 5,
};
let arr = Array.from(obj);
console.log(arr);// [undefined, 1, 2, undefined, undefined]

7.10 作为数组的字符串

  • 字符串不是数组;
  • 字符串可以使用[]来获取某个字符,可完全替代chatAt方法
let s = "test";
console.log(s['0']);// => "t",可以使用整数字符串
console.log(s[1]);// => "e"
  • 字符串与数组的行为类似,也意味着我们可以对字符串使用泛型的字符串方法
let str = Array.prototype.join.call("JavaScript", "-")
console.log(str);//J-a-v-a-S-c-r-i-p-t
str = Array.prototype.join.call("JavaScript")
console.log(str);//J,a,v,a,S,c,r,i,p,t

7.11 小结

数组比普通对象特殊的地方:

  • 1、数据按索引值升序排列
  • 2、自动维护length值,
  • 3、可以通过设置length值来删除元素

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/1899.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt实现XYModem协议(五)

1 概述 XMODEM协议是一种使用拨号调制解调器的个人计算机通信中广泛使用的异步文件运输协议。这种协议以128字节块的形式传输数据&#xff0c;并且每个块都使用一个校验和过程来进行错误检测。使用循环冗余校验的与XMODEM相应的一种协议称为XMODEM-CRC。还有一种是XMODEM-1K&am…

35K的鸿蒙音视频开发岗位面经分享~

一个月前&#xff0c;阿里云在官网音视频终端 SDK 栏目发布适配 HarmonyOS NEXT 的操作文档和 SDK&#xff0c;官宣 MediaBox 音视频终端 SDK 全面适配 HarmonyOS NEXT。 此外&#xff0c;阿里云播放器 SDK 也在华为开发者联盟官网鸿蒙生态伙伴 SDK 专区同步上线&#xff0c;面…

面向对象设计与分析40讲(25)中介模式、代理模式、门面模式、桥接模式、适配器模式

文章目录 门面模式代理模式中介模式 之所以把这几个模式放到一起写&#xff0c;是因为它们的界限比较模糊&#xff0c;结构上没有明显的差别&#xff0c;差别只是语义上。 这几种模式在结构上都类似&#xff1a; 代理将原本A–>C的直接调用变成&#xff1a; A–>B–>…

负采样重要吗?它的理论与应用综述

Does Negative Sampling Matter? A Review with Insights into its Theory and Applications 负采样重要吗&#xff1f;它的理论与应用综述 Does Negative Sampling Matter? A Review with Insights into its Theory and Applications Zhen Yang, Ming Ding, Tinglin Huang,…

基于python实现web漏洞挖掘技术的研究(django)

基于python实现web漏洞挖掘技术的研究(django) 开发语言:Python 数据库&#xff1a;MySQL所用到的知识&#xff1a;网络爬虫&#xff0c;SQL注入&#xff0c;XSS漏洞工具&#xff1a;pycharm、Navicat、Maven 系统的实现与漏洞挖掘 系统的首页面 此次的系统首页面是登录的页…

BootstrapAdmin Net7:基于RBAC的后台管理框架,实现精细化权限管理与多站点单点登录

BootstrapAdmin Net7&#xff1a;基于RBAC的后台管理框架,实现精细化权限管理与多站点单点登录 摘要 随着企业信息化建设的不断深入&#xff0c;后台管理系统在企业运营中扮演着越来越重要的角色。本文介绍了一款基于RBAC&#xff08;Role-Based Access Control&#xff09;的…

291个地级市资源错配指数、劳动和资本相对扭曲指数(2006-2021年)

01、数据介绍 资源错配指数&#xff08;Misallocation Index&#xff09;是一个用于衡量资源配置效率的指标&#xff0c;它衡量的是生产要素的配置是否合理&#xff0c;是否达到了最优的状态。资源错配指数越高&#xff0c;资源的利用效率越低。资源错配指数主要用于衡量各种生…

企业实施定制鞋厂ERP软件需要注意哪些问题?

企业实施定制鞋厂ERP软件是个复杂的管理系统工程&#xff0c;为了成功地为企业定制实施ERP软件&#xff0c;需要注意和解决几个关键的问题&#xff1a; . 确立ERP系统实施和定制的决策者&#xff1b;. 做好前期咨询与调研工作&#xff1b;. 做好系统产品或项目迭代规划&#x…

Uds诊断协议的请求和响应的寻址

一根总线上挂载着很多ECU&#xff0c;那么基于CAN协议UDS的诊断请求报文&#xff0c;诊断仪是如何发给ECU的&#xff1f;如何精准的找到想要诊断的那个ECU&#xff1f;ECU又是如何将诊断响应的报文返回给诊断仪&#xff1f; 在UDS协议中&#xff0c;规定了诊断请求和响应报文发…

记录:阿里云服务器网站搭建(3)

Docker安装配置Tomcat 拉取镜像 docker pull tomcat:8启动一个tomcat容器用于拷贝配置文件 docker run -d -p 8080:8080 --name tomcat tomcat:8拷贝容器内tomcat配置文件和日志到本地准备映射 docker cp tomcat:/usr/local/tomcat/conf /mydata/tomcat/confdocker cp tomca…

WebStorm2024安装包(亲测可用)

目录 一、软件简介 二、软件下载 一、软件简介 WebStorm是一款由JetBrains公司开发的强大的集成开发环境&#xff08;IDE&#xff09;&#xff0c;专门用于前端开发。它提供了丰富的功能和工具&#xff0c;包括代码编辑器、调试器、版本控制集成等&#xff0c;使开发人员能够更…

LocalAi,Ollama+AnythingLLM搭建部署本地大模型AI知识库,汉化版本

AnythingLLM 是一个全栈应用程序&#xff0c;您可以使用商业现成的 LLM 或流行的开源 LLM 和 vectorDB 解决方案来构建私有 ChatGPT&#xff0c;无需任何妥协&#xff0c;您可以在本地运行&#xff0c;也可以远程托管并能够智能聊天以及您提供的任何文件。 AnythingLLM 将您的文…

人工智能大模型培训老师叶梓 探索知识库问答中的查询图生成:处理多跳复杂问题的新方法

在人工智能领域&#xff0c;基于知识库的问答&#xff08;KBQA&#xff09;技术正变得越来越重要。它使得机器能够理解自然语言问题&#xff0c;并从结构化的知识库中检索答案。然而&#xff0c;面对多跳复杂问题&#xff0c;传统的KBQA方法往往力不从心。近期&#xff0c;研究…

芒果超媒的“乘风破浪”,差了一点市场海浪的反馈

4月21日晚间&#xff0c;芒果超媒发布了2023年度&2024一季度报告。 芒果超媒2023年实现营业收入146.28亿元&#xff0c;同比增长4.66%&#xff1b;净利润35.56亿元&#xff0c;同比增长90.73%&#xff1b;基本每股收益1.90元。公司拟每10股派发现金红利1.8元。2024年第一季…

数据可视化(八):Pandas时间序列——动态绘图,重采样,自相关图,偏相关图等高级操作

Tips&#xff1a;"分享是快乐的源泉&#x1f4a7;&#xff0c;在我的博客里&#xff0c;不仅有知识的海洋&#x1f30a;&#xff0c;还有满满的正能量加持&#x1f4aa;&#xff0c;快来和我一起分享这份快乐吧&#x1f60a;&#xff01; 喜欢我的博客的话&#xff0c;记得…

腾讯云服务器价格明细表2024年最新(CPU内存/带宽/磁盘)

腾讯云服务器价格明细表2024年最新&#xff08;CPU内存/带宽/磁盘&#xff09;腾讯云服务器租用优惠价格表&#xff1a;轻量应用服务器2核2G3M价格61元一年&#xff0c;2核2G4M价格99元一年、135元15个月、540元三年&#xff0c;2核4G5M带宽165元一年、252元15个月、756元3年&a…

2024 应届校招经验分享(非互联网篇)

&#x1f4e2;&#x1f4e2;&#x1f4e2; 2024年应届毕业生&#xff0c;浙江大学计算机研究生。自23年秋招开始至今&#xff0c;博主先后拿到了互联网行业、机械制造行业、国企央企、高校体制等多家企业和单位的数十个Offer&#xff0c;了解多行业中的计算机求职方向&#xff…

Java转go,我用了12小时,10小时在解决环境问题

Part1 问题背景 作为一个资深的Java开发者&#xff0c;我深知面向对象的高级语言&#xff0c;语法是不用学的。需要的时候搜索就可以了&#xff0c;甚至可以用ChatGPT来写。 之前我做一个安全多因素校验服务。因为是临时服务&#xff0c;扩展性上基本没有要求&#xff0c;为了快…

Vulnhub靶机 DC-6 打靶实战 详细渗透测试过程

Vulnhub靶机 DC-6 详细渗透流程 打靶实战 目录 Vulnhub靶机 DC-6 详细渗透流程 打靶实战一、将靶机导入到虚拟机当中二、渗透测试主机发现端口扫描信息探测web渗透目录爆破爆破后台密码反弹shell搜集有价值信息SSH远程登录提权反弹jens用户权限的shell 提权利用 一、将靶机导入…

stable diffusion本地部署@win10

一键无脑安装stable-diffusion-webui stable diffusion是当前非常出色的文生图模型&#xff0c;要优于以前gan文生图模型。现在有了stable-diffusion-webui软件&#xff0c;可以一键安装&#xff0c;大大简化了操作难度。本文档就是stable-diffusion-webui在windows 10上的安装…