「前端基础」JS数组

1 定义

创建数组

// 标准写法
let arr = new Array(1, 2, 3,)

// 简写
let arr = [1, 2, 3]

//创建长度为3的数组
let arr = new Array(3)

任何类型的数据,都可以放入数组。

2 数组的本质

本质上,数组属于一种特殊的对象。JavaScript其实没有真正的数组(只是用对象模拟数组)。

典型的数组:

  • 元素的数据类型相同
  • 使用连续的内存存储
  • 通过数字下表获取元素

JS的数组:

  • 元素的数据类型可以不同
  • 内存不一定是连续的(对象是随机存储的)
  • 不能通过数字下表, 而是通过字符串下标
typeof([]) // "object"

数组的特殊性体现在,它的键名是按次序排列的一组整数(0,1,2…)。

Object.keys方法返回数组的所有键名

JavaScript 语言规定,对象的键名一律为字符串,所以,数组的键名其实也是字符串。之所以可以用数值读取,是因为非字符串的键名会被转为字符串。

注意,这点在赋值时也成立。一个值总是先转成字符串,再作为键名进行赋值。

var a = [];

a[1.00] = 6;
a[1] // 6

对象有两种读取成员的方法:点结构(object.key)和方括号结构(object[key])。但是,对于数值的键名,不能使用点结构。

var arr = [1, 2, 3];
arr.0 // SyntaxError

3 length属性

JavaScript 使用一个32位整数,保存数组的元素个数。这意味着,数组成员最多只有 4294967295 个( 2 32 2^{32} 232 - 1)个,也就是说length属性的最大值就是 4294967295。

只要是数组,就一定有length属性。该属性是一个动态的值,等于键名中的最大整数加上1

var arr = ['a', 'b'];
arr.length // 2

arr[2] = 'c';
arr.length // 3

arr[9] = 'd';
arr.length // 10

arr[1000] = 'e';
arr.length // 1001

length属性是可写的。如果人为设置一个小于当前成员个数的值,该数组的成员数量会自动减少到length设置的值。

var arr = [ 'a', 'b', 'c' ];
arr.length // 3

arr.length = 2;
arr // ["a", "b"]

清空数组的一个有效方法,就是将length属性设为0。

var arr = [ 'a', 'b', 'c' ];

arr.length = 0;
arr // []

如果人为设置length大于当前元素个数,则数组的成员数量会增加到这个值,新增的位置都是空位empty

var a = ['a'];

a.length = 3;
a // ["a", empty × 2]
a[1] // undefined

如果人为设置length为不合法的值,JavaScript 会报错。

// 设置负值
[].length = -1
// RangeError: Invalid array length

// 数组元素个数大于等于2的32次方
[].length = Math.pow(2, 32)
// RangeError: Invalid array length

// 设置字符串
[].length = 'abc'
// RangeError: Invalid array length

值得注意的是,由于数组本质上是一种对象,所以可以为数组添加属性,但是这不影响length属性的值。

var a = [];

a['p'] = 'abc';
a.length // 0

a[2.1] = 'abc';
a.length // 0

上面代码将数组的键分别设为字符串和小数,结果都不影响length属性。因为,length属性的值就是等于最大的数字键加1,而这个数组没有整数键,所以length属性保持为0。

如果数组的键名是添加超出范围的数值,该键名会自动转为字符串。

var arr = [];
arr[-1] = 'a';
arr[Math.pow(2, 32)] = 'b';

arr.length // 0
arr[-1] // "a"
arr[4294967296] // "b"

4 in 运算符

检查某个键名是否存在的运算符in,适用于对象,也适用于数组。

var arr = [ 'a', 'b', 'c' ];
2 in arr  // true
'2' in arr // true
4 in arr // false

上面代码表明,数组存在键名为2的键。由于键名都是字符串,所以数值2会自动转成字符串。

5 for…in循环和数组的遍历

for...in循环不仅可以遍历对象,也可以遍历数组,毕竟数组只是一种特殊对象。

var a = [1, 2, 3];

for (var i in a) {
  console.log(a[i]);
}
// 1
// 2
// 3

但是,for...in不仅会遍历数组所有的数字键,还会遍历非数字键。

所以,不推荐使用for...in遍历数组。

数组的遍历可以考虑使用for循环或while循环。

var a = [1, 2, 3];

// for循环
for(var i = 0; i < a.length; i++) {
  console.log(a[i]);
}

// while循环
var i = 0;
while (i < a.length) {
  console.log(a[i]);
  i++;
}

var l = a.length;
while (l--) {
  console.log(a[l]);
}

@@@
数组的forEach方法,也可以用来遍历数组,详见《标准库》的 Array 对象一章。
@@需要修改@

var colors = ['red', 'green', 'blue'];
colors.forEach(function (color) {
  console.log(color);
});
// red
// green
// blue

自己实现foreach

function forEach(array, fn){
	for(let i = 0; i<array.length; i++){
		fn(array[i], i, array)
	}
}

forEach([1, 2, 3], function(value, key){
    console.log(key, value)
})

6 数组的空位

当数组的某个位置是空元素,即两个逗号之间没有任何值,我们称该数组存在空位(hole)。

var a = [1, , 1];
a.length // 3

需要注意的是,如果最后一个元素后面有逗号,并不会产生空位。也就是说,有没有这个逗号,结果都是一样的。

var a = [1, 2, 3,];

a.length // 3
a // [1, 2, 3]

数组的空位是可以读取的,返回undefined。

var a = [, , ,];
a // [empty × 3]
a[1] // undefined

var b = []
b.length = 3
b // [empty × 3]
b[1] // undefined

使用delete命令删除一个数组成员,会形成空位,并且不会影响length属性。也就是说,length属性不过滤空位。所以,使用length属性进行数组遍历,一定要非常小心。

var a = [1, 2, 3];
delete a[2];

a[2] // undefined
a.length // 3

数组的某个位置是空位,与某个位置是undefined,是不一样的。如果是空位,使用数组的forEach方法、for...in结构、以及Object.keys方法进行遍历,空位都会被跳过。

var a = [, , ,];

a.forEach(function (x, i) {
  console.log(i + '. ' + x);
})
// 不产生任何输出

for (var i in a) {
  console.log(i);
}
// 不产生任何输出

Object.keys(a)
// []

如果某个位置是undefined,遍历的时候就不会被跳过。

var a = [undefined, undefined, undefined];

a.forEach(function (x, i) {
  console.log(i + '. ' + x);
});
// 0. undefined
// 1. undefined
// 2. undefined

for (var i in a) {
  console.log(i);
}
// 0
// 1
// 2

Object.keys(a)
// ['0', '1', '2']

这就是说,空位就是数组没有这个元素,所以不会被遍历到,而undefined则表示数组有这个元素,值是undefined,所以遍历不会跳过。

7 类数组

如果一个对象的所有键名都是正整数或零,并且有length属性,那么这个对象就很像数组,语法上称为“类似数组的对象”(array-like object),简称类数组。

“类似数组的对象”的根本特征,就是具有length属性。只要有length属性,就可以认为这个对象类似于数组。

但是有一个问题,这种length属性不是动态值,不会随着成员的变化而变化,而且没有数组的共有属性,例如pushpop等。

var obj = {
  length: 0
};
obj[3] = 'd';
obj.length // 0

上面代码为对象obj添加了一个数字键,但是length属性没变。这就说明了obj不是数组。

典型的类数组有:

  • 函数的arguments对象;
  • 大多数DOM元素集;
  • 字符串。
// arguments对象
function args() { return arguments }
var arrayLike = args('a', 'b');

arrayLike[0] // 'a'
arrayLike.length // 2
arrayLike instanceof Array // false

// DOM元素集
var elts = document.getElementsByTagName('h3');
elts.length // 3
elts instanceof Array // false

// 字符串
'abc'[1] // 'b'
'abc'.length // 3
'abc' instanceof Array // false

数组的slice方法可以将类数组变成真正的数组。

var arr = Array.prototype.slice.call(arrayLike)
//等价于
var arr = [].slice.call(arrayLike)

from方法也可以将类数组变成真正的数组。

Array.from('123') // ["1", "2", "3"]
Array.from({0:'a', 1:'b', length:2}) // ["a", "b"]

对于字符串可以使用split方法

'1, 2, 3'.split(',') // ["1", "2", "3"]
'123'.split('') // ["1", "2", "3"]

@@@
另外,你可以使用 bind 。

var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.call.bind(unboundSlice);

function list() {
  return slice(arguments);
}

var list1 = list(1, 2, 3); // [1, 2, 3]

@@不懂@

除了转为真正的数组,类数组还有一个办法可以使用数组的方法,就是通过call()把数组的方法放到对象上面。

function print(value, index) {
  console.log(index + ' : ' + value);
}

Array.prototype.forEach.call(arrayLike, print);

疑问: 类数组的原型链上会有数组的方法吗?
答:没有

下面的例子就是通过这种方法,在arguments对象上面调用forEach方法。字符串也是类似数组的对象,所以也可以.

// forEach 方法
function logArgs() {
  Array.prototype.forEach.call(arguments, function (elem, i) {
    console.log(i + '. ' + elem);
  });
}

// 等同于 for 循环
function logArgs() {
  for (var i = 0; i < arguments.length; i++) {
    console.log(i + '. ' + arguments[i]);
  }
}

注意,这种方法比直接使用数组原生的forEach要慢,所以最好还是先将类数组转为真正的数组,然后再直接调用数组的forEach方法。

var arr = 'abc';
function logArgs(arr) {
  Array.prototype.forEach.call(arr, function (elem, i) {
    console.log(i + '. ' + elem);
  });
}
logArgs(arr)

// 直接先转换为数组后foreach
var arr = Array.prototype.slice.call('abc');
arr.forEach(function (chr) {
  console.log(chr);
});
// a
// b
// c

其他操作

from方法

console.log(Array.from([1, 2, 3], x => x + x)); //[2, 4, 6]
Array.from({length: 5}, (v, i) => i); // [0, 1, 2, 3, 4]

@@@
数组去重并合并

function combine(){ 
    let arr = [].concat.apply([], arguments);  //没有去重复的新数组 
    return Array.from(new Set(arr));
} 

var m = [1, 2, 2], n = [2,3,3]; 
console.log(combine(m,n));                     // [1, 2, 3]

@@需要仔细看看@

  • 合并两个数组

concat返回一个新数组不会影响原来的两个数组。

let arr11 = [1]
let arr22 = [2]
arr11.concat(arr22) // [1, 2]
arr11 // [1]
arr22 // [2]
  • 截取数组

slice返回一个新数组不会影响原数组

let arr = [1, 2, 3]
arr.slice(1) // [2, 3]
arr // [1]

//复制一个数组
arr.slice(0) // [1, 2, 3]

JS只提供浅拷贝

删除数组
delete和直接修改length不是删除数组的方法。

删数组元素:

  • 删除头部的元素
    arr.shift(), arr被修改, 并返回被删除的元素
  • 删除尾部的元素
    arr.pop(), arr被修改, 并返回被删除的元素
  • 删除中间的元素
//删除index的一个元素, 1表示删除长度
arr.splice(index, 1)
//并在删除位置添加'x'
arr.splice(index, 1, 'x')
//并在删除位置添加'x', 'y'
arr.splice(index, 1, 'x', 'y')

查看单个属性:

  • 索引越界
arr[arr.length] === undefined
arr[-1] === undefined
  • 查看某个元素是否在数组里
    arr.indexOf(item), 存在返回索引, 否则返回-1

  • 使用条件查找元素
    arr.find(item=> item%2 === 0), 找到第一个偶数, 否则返回undefined

  • 使用条件查找元素的索引
    arr.findIndex(item=> item%2 === 0), 找到第一个偶数索引, 否则返回-1

  • 在尾部加元素

push() 方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度。

//修改arr, 返回新长度
arr.push(newItem)
//修改arr, 返回新长度
arr.push(item1,item2)
  • 在头部加元素
    unshift() 方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度。
//修改arr, 返回新长度
arr.unshift(newItem)
//修改arr, 返回新长度
arr.unshift(item1,item2)
  • 在中间添加元素

splice() 方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。

//在index处插入'x'
arr.splice(index, 0, 'x')
arr.splice(index, 0, 'x', 'y')
  • 反转顺序
    arr.reverse(), 修改原数组
  • 自定义顺序

arr.sort()默认是从小到大排序,不对。

var array2 = [1, 30, 4, 21, 100000]
array2.sort()
// [1, 100000, 21, 30, 4]

如果要求从大到小排序呢?
可以使用sort和reverse来实现
但是我就是要用sort来实现呢
原始代码:

//返回1:左边大右边小,返回-1:左边小右边大
arr.sort(function(a, b){
	if(a < b){
		return -1
	}else if(a === b){
		return 0
	}else{
		return 1
	}	
})

简写代码:
arr.sort((a, b)=> a-b),因为只需要确定返回值是正数,负数和0,即可。

指定对象属性排序

var arr = [
    {
    	'name': 'a1',
    	'score': 91
    },
    {
    	'name': 'a2',
    	'score': 71
    },
    {
    	'name': 'a3',
    	'score': 81
    }
]

arr.sort((a, b) => a.sorce - b.sorce)

数组变换
在这里插入图片描述

  • map

对数组的每一项进行操作(n变n)

let arr = [1, 2, 3]
arr.map(item => item * item)
// [1, 4, 9]
  • filter

对数组的元素进行过滤(n变少)

let arr = [1, 2, 3]
arr.filter(item => item % 2 === 0)
// [2]

let arr = [1, 2, 3]
arr.map(item => item % 2 === 0)
// [false, true, false]
  • reduce

对数组整合成一个(n边1)

let arr = [1, 2, 3]
arr.reduce((sum, item) => sum+item, 0)
// 6

reduce实现平方(不建议)

let arr = [1, 2, 3]
arr.reduce((result, item) => result.concat(item*item), [])
// [1, 4, 9]

通过reduce来实现过滤出偶数
法一:

let arr = [1, 2, 3]
arr.reduce((result, item) =>{
  if(item % 2 === 0){
    return result.concat(item)
  }else{
    return result
  }
}, [])
// [2]

法二:

let arr = [1, 2, 3]
arr.reduce((result, item) => item % 2 === 0 ? result.concat(item) : result, [])
// [2]

法三:通过拼接一个空的[]

let arr = [1, 2, 3]
arr.reduce((result, item) => result.concat(item % 2 ===0 ? item : []), [])
// 2

把数字变为星期

let arr = [0, 1, 3, 5, 3]
arr.map(i => ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'][i])
// ["星期日", "星期一", "星期三", "星期五", "星期三"]
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值