JavaScript:JavaScript数组探究
JavaScript数组探究
数组作为最基础的数据结构,我们不仅要熟练掌握它,还要了解它的许多特性
js的数组很灵活,和其他语言的数组不一样,js的数组可以存储不同类型的值
创建一维数组的两种方法
1.数组字面量法
需要注意的是:使用数组字面量法创建数组并不会调用Array构造函数
let arr1 = [1, 2, 3, 4, 5, 11, 13, 24];
2.使用Array的构造函数,new一个数组
let arr2 = new Array();
// 指定数组长度
let arr3 = new Array(8);
// 指定长度,且填充1
let arr4 = new Array(4).fill(24);
// new操作符可以省略,一个长度为二的数组
let arr6 = Array(2, 3);
console.log("arr6:", arr6);
// 当参数只有一个数字的时候,就是创建一个长度为传入参数的数组
let arr7 = Array(3);
console.log("arr7:", arr7);
// 遍历数组
arr4.forEach((item, index) => {
console.log(item, index);
})
// 还可以用map来遍历数组
// map 方法在调用形式上与 forEach 无异,
// 区别在于 map 方法会根据你传入的函数逻辑对数组中每个元素进行处理、进而返回一个全新的数组。
console.log("map遍历");
let arr5 = arr4.map((item, index) => {
console.log(item, index);
return item + 11;
})·
二维数组的创建
一维数组可以使用数组字面量和使用Array的构造函数创建
那么二维数组呢?
下面来看一下这样初始化对不对
let arr1 = new Array(3).fill([]);
// 现在我们来改变一下二维数组里面的值
arr1[0][0] = 35;
console.log(arr1); // 这里发现里面的值全都变成了35,这是为什么呢
这里是因为 fill 的工作机制导致的。 当给 fill 传递一个入参时,如果这个入参的类型是引用类型,那么 fill 在填充坑位时填充的其实就是入参的引用。所以fill里面的数组是同一个引用、指向的是同一块内存空间,它们本质上是同一个数组所以应该用for循环来初始化一个二维数组
for (let i = 0; i < 4; i++) {
arr2[i] = [];
}
数组的一些方法
pop/push, shift/unshift 方法
push
在末端添加一个元素pop
从末端取出一个元素unshift
在队列首端添加一个元素,整个队列往后移一位shift
取出队列首端的一个元素,整个队列往前移一位
这里会有一个性能问题,那就是push
和 pop
的性能会比shift
和unshift
高,因为shift
和unshift
会移动整个数组,这样很浪费时间。
遍历方式
数组的遍历方式有三种
let arr = ["beiyep", "kobe", "james"];
// 用数组的长度遍历
for(let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
// 用for of遍历
for(let item of arr) {
console.log(item);
}
// 用for in遍历
for(let item in arr) {
console.log(item);
}
console.log("pause");
需要说明的是,for in返回的是数组的下标值,并且要注意:
for..in
循环会遍历 所有属性,不仅仅是这些数字属性。在浏览器和其它环境中有一种称为“类数组”的对象,它们 看似是数组。也就是说,它们有
length
和索引属性,但是也可能有其它的非数字的属性和方法,这通常是我们不需要的。for..in
循环会把它们都列出来。所以如果我们需要处理类数组对象,这些“额外”的属性就会存在问题。来看下面的🌰:
Array.prototype.myfun = function() { alert('myfun'); } var arr2 = [0,1,2,3]; for (var i in arr2) { console.log(arr2[i]); } console.log(Array.prototype)
运行结果如下:
上面的例子很好的反映了for…in…循环的缺点,原本只想循环取出该数组的数据,但是由于之前给数组添加了原型函数,导致循环的结果多了一个函数
for..in
循环适用于普通对象,并且做了对应的优化。但是不适用于数组,因此速度要慢 10-100 倍。当然即使是这样也依然非常快。只有在遇到瓶颈时可能会有问题。但是我们仍然应该了解这其中的不同。
数组中的搜索
- 严格相等
js提供了indexOf/lastIndexOf 和 includes三种严格相等的搜索方法。其中
arr.indexOf(item, from)
从索引from
开始搜索item
,如果找到则返回索引,否则返回-1
。arr.lastIndexOf(item, from)
—— 和上面相同,只是从右向左搜索。arr.includes(item, from)
—— 从索引from
开始搜索item
,如果找到则返回true
,如果没找到,则返回false
请注意,这些方法使用的是严格相等 ===
比较。所以如果我们搜索 false
,会精确到的确是 false
而不是数字 0
。
let arr = [1, 11, 24, 35, 13, false, true];
// 从索引2开始搜索1
console.log(arr.indexOf(1, 2)); // -1
// 从索引3开始搜索13
console.log(arr.indexOf(13, 3)); // 3
// 从零开始搜索true
console.log(arr.indexOf(true)); // 6
console.log(arr.indexOf(0)); // -1
如果我们想检查是否包含某个元素,并且不想知道确切的索引,那么 arr.includes
是首选。
此外,includes
的一个非常小的差别是它能正确处理NaN
,而不像 indexOf/lastIndexOf
let arr1 = [NaN]
console.log(arr1.indexOf(NaN)); // -1
console.log(arr1.includes(NaN)); // true
- 断言函数搜索
arr.find和arr.findIndex是js提供的断言函数搜索,每个索引都会调用这个函数。
arr.find的语法如下:
let result = arr.find(function(item, index, array) {
// 如果返回 true,则返回 item 并停止迭代
// 对于假值(false)的情况,则返回 undefined
});
arr.findIndex 方法(与 arr.find
方法)基本上是一样的,但它返回找到元素的索引,而不是元素本身。并且在未找到任何内容时返回 -1
。
Array.isArray
由于数组是基于对象的,不构成单独的语言类型,所以 typeof 不能帮助从数组中区分出普通对象:
console.log(typeof {}); // object
console.log(typeof []); // object
为了解决这个问题,Array.isArray应运而生,如果 value
是一个数组,则返回 true
;否则返回 false
。
console.log(Array.isArray({})); // false
console.log(Array.isArray([])); // true
数组中方法的补充说明
几乎所有调用函数的数组方法 —— 比如 find
,filter
,map
,除了 sort
是一个特例,都接受一个可选的附加参数 thisArg
。
比如:
arr.find(func, thisArg);
arr.filter(func, thisArg);
arr.map(func, thisArg);
// ...
// thisArg 是可选的最后一个参数
举个🌰:
let army = {
minAge: 18,
maxAge: 27,
canJoin(user) {
return user.age >= this.minAge && user.age < this.maxAge;
}
};
let users = [
{age: 16},
{age: 20},
{age: 23},
{age: 30}
];
// 找到 army.canJoin 返回 true 的 user
let soldiers = users.filter(army.canJoin, army);
// let soldiers = users.filter(army.canJoin); // 错误
console.log(soldiers.length); // 2
console.log(soldiers[0].age); // 20
console.log(soldiers[1].age); // 23
如果在上面的示例中我们使用了 users.filter(army.canJoin)
,那么 army.canJoin
将被作为独立函数调用,并且这时 this=undefined
,从而会导致即时错误。所以我们这里一般都用剪头函数。
数组的本质
数组是一种特殊的对象,比如数组访问元素的方式是arr[0]和对象obj[key]很相似,其中arr是对象,数字用作键。数组和类相似,也是引用类型。但是数组又拓展了对象,因为数组可以处理有序的数据集合。