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的倍数元素,没找到返回undefined

let 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层放入_arr
                    for (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 提取

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

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

let arr = [0, 1, 2, 3, 4];
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~倒数第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));      // => -1

console.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 return
        len = a.length,          // The length of the array to be searched
        pos = 0;                 // The position to search from
    while (pos < len) {           // While more elements to search...
        pos = a.indexOf(x, pos); // Search
        if (pos === -1) break;   // If nothing found, we're done.
        results.push(pos);       // Otherwise, store index in array
        pos = 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)); // true

console.log(arr.indexOf([]));  // -1
console.log(arr.includes([])); // false

console.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 类数组对象仍然是false

console.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.mfbz.cn/a/551458.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

安装 k8s集群的问题:默认容器运行时从 Docker 改为 Containerd

安装 k8s集群的问题&#xff1a;默认容器运行时从 Docker 改为 Containerd 1、背景2、容器运行时从 Docker 改为 Containerd2.1、安装 Containerd&#xff1a;2.2、生成 Containerd 的配置文件2.3 、创建 /etc/crictl.yaml 文件2.4 、配置 Containerd 服务开机自启 &#x1f49…

算法与数据结构要点速学——排序算法

排序算法 所有主要的编程语言都有一个内置的排序方法。假设并说排序成本为 O(n*log n)&#xff0c;通常是正确的&#xff0c;其中 n 是要排序的元素数。为了完整起见&#xff0c;这里有一个图表&#xff0c;列出了许多常见的排序算法及其完整性。编程语言实现的算法各不相同&a…

【GDB调试技巧】提高gdb的调试效率

目录 &#x1f31e;gdb的启动 &#x1f31e;gdb技巧 &#x1f33c;1. gdb小技巧汇总 &#x1f33c;2. 打印输出指定地址的值 &#x1f33c;3. 查看当前执行到哪行代码代码内容 3.1 方式一&#xff1a;info line 结合 list 。 3.2 方式二&#xff1a;f 3.3 方式三&#…

WebGIS面试题(第五期)

WebGIS面试题&#xff08;第五期&#xff09; 以下题目仅为部分题目&#xff0c;全部题目在公众号{GISer世界}&#xff0c;答案仅供参考 1、Cesium的核心组件有哪些&#xff1f; Cesium的核心组件包括Viewer、Scene、Model、Geometry、Material和Camera等。其中&#xff0c;…

Latex(从入门到入土)1

第一章&#xff1a;初识Latex 1、安装Latex&#xff0c;当然可以安装官方的开放版本&#xff0c;也可以去找找别人发的资源。我这里只介绍我的学习经过。如果想下载最新的软件资源&#xff0c;我这里推荐微信公众号&#xff1a;软件智库&#xff0c;通过号主提供的网址是可以下…

基于大数据的全国热门景点数据可视化分析系统

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长 QQ 名片 :) 1. 项目简介 本文将介绍如何使用Python中的Pandas库进行数据挖掘&#xff0c;并结合Flask Web框架实现一个旅游景点数据分析系统。该系统将包括以下功能模块&#xff1a;热门景点概况、景点星级与评分分析、景…

Docker 学习笔记(十):Centos7 中 Docker 部署 Redis 集群,打包 SpringBoot 微服务

一、前言 记录时间 [2024-4-17] 系列文章简摘&#xff1a; Docker 学习笔记&#xff08;六&#xff09;&#xff1a;挑战容器数据卷技术一文通&#xff0c;实战多个 MySQL 数据同步&#xff0c;能懂会用&#xff0c;初学必备 Docker 学习笔记&#xff08;七&#xff09;&#x…

基于Copula函数的风光功率联合场景生成_任意修改生成的场景数目(附带Matlab代码)

基于Copula函数的风光功率联合场景生成 削减为6个场景 部分展示削减为5个场景 部分展示 风光等可再生能源出力的不确定性和相关性给系统的设计带来了极大的复杂性&#xff0c;若忽略这些因素&#xff0c;势必会在系统规划阶段引入次优决策风险。因此&#xff0c;在确定系统最佳…

Linux sort/uniq/wc

文章目录 1. sort 排序将线程ID从大到小排序 2.uniq 临近去重3.wc word cnt 统计 1. sort 排序 将线程ID从大到小排序 grep -v是反向筛选&#xff0c;利用USER&#xff0c;排除掉首行 awk是打印第1 2列 sort -n是代码以数值大小做排序&#xff0c;不加的话会以字符排序。 -k是…

Go 单元测试之HTTP请求与API测试

文章目录 一、httptest1.1 前置代码准备1.2 介绍1.3 基本用法 二、gock2.1介绍2.2 安装2.3 基本使用2.4 举个例子2.4.1 前置代码2.4.2 测试用例 一、httptest 1.1 前置代码准备 假设我们的业务逻辑是搭建一个http server端&#xff0c;对外提供HTTP服务。用来处理用户登录请求…

腾讯实验室推出类似 Sora 的长视频生成Mira;阿里巴巴推出强大的代码生成模型CodeQwen1.5

✨ 1: Mira 腾讯PCG ARC实验室推出Mira&#xff1a;类似 Sora 的长视频生成 Mira&#xff08;Mini-Sora&#xff09;&#xff0c;是一个尝试生成高质量、长时视频的初步探索项目&#xff0c;以Sora风格进行长视频生成。与现有的文本到视频&#xff08;Text-to-Video, T2V&a…

2024年【高处安装、维护、拆除】试题及解析及高处安装、维护、拆除模拟试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年【高处安装、维护、拆除】试题及解析及高处安装、维护、拆除模拟试题&#xff0c;包含高处安装、维护、拆除试题及解析答案和解析及高处安装、维护、拆除模拟试题练习。安全生产模拟考试一点通结合国家高处安装…

基于SSM+Jsp+Mysql的贝儿米幼儿教育管理系统

开发语言&#xff1a;Java框架&#xff1a;ssm技术&#xff1a;JSPJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包…

如何简单下载指定版本的jdk

Oracle 官方提供的 Java Development Kit (JDK) 的归档站点。它主要用于存放历史版本的 JDK&#xff0c;供开发者下载和使用。 附上站点地址&#xff1a;Archived OpenJDK GA Releases 在这个站点可以找到各版本的jdk&#xff0c;简单实用~ 找到版本&#xff0c;点击tar.gz进…

研究表明,全球互联网流量竟有一半来自机器人

据Cyber News消息&#xff0c;Thales Imperva Bad Bot近期做了一份报告&#xff0c;显示在2023年有49.6%的互联网流量竟来自机器人&#xff0c;比上一年增长 2%&#xff0c;达到自2013年以来观察到的最高水平。 报告称&#xff0c;这一趋势正对企业组织产生负面影响&#xff0c…

Qwen1.5大语言模型微调实践

在人工智能领域&#xff0c;大语言模型&#xff08;Large Language Model&#xff0c;LLM&#xff09;的兴起和广泛应用&#xff0c;为自然语言处理&#xff08;NLP&#xff09;带来了前所未有的变革。Qwen1.5大语言模型作为其中的佼佼者&#xff0c;不仅拥有强大的语言生成和理…

Seata

Seata是一款开源的分布式事务解决方案&#xff0c;致力于提供高性能和简单易用的分布式事务服务。Seata将为用户提供了AT、TCC、SAGA和XA事务模式&#xff0c;为用户打造一站式的分布式解决方案。 1.1 Seata的三大角色 在 Seata 的架构中&#xff0c;一共有三个角色&#xff1…

解决 MSYS2 Qt 6.7 默认 stylesheet 在 windows 11 下的显示故障

项目场景&#xff1a; MSYS2 升级到 Qt6.7.0&#xff0c;发现显示故障&#xff0c;所有Qt6程序以及 QtCreator的SpinBox都显示不全&#xff0c;Combox的底色不对。 问题描述 2024年4月1日&#xff0c;pacman升级MSYS2后&#xff0c;Qt6遇到风格错误。如果使用官方的 Qt onlin…

C++ ─── 类和对象(拷贝构造函数)

目录 拷贝构造函数 特征 结论&#xff1a; 拷贝构造函数 拷贝构造函数&#xff1a;只有单个形参&#xff0c;该形参是对本类类型对象的引用(一般常用const修饰)&#xff0c;在用已存 在的类类型对象创建新对象时由编译器自动调用。 特征 拷贝构造函数也是特殊的成员函数&…

【Redis 神秘大陆】004 高可用集群

四、Redis 高可用和集群 当你发现这些内容对你有帮助时&#xff0c;为了支持我的工作&#xff0c;不妨给一个免费的⭐Star&#xff0c;这将是对我最大的鼓励&#xff01;感谢你的陪伴与支持&#xff01;一起在技术的路上共同成长吧&#xff01;点击链接&#xff1a;GitHub | G…
最新文章