数组是一种特殊的对象。
JS没有真正的数组,JS是通过对象去模拟数组。
JS数组与传统高级语言里的数组的区别在于:
典型的数组
- 元素的数据类型相同
- 使用连续的内存存储
- 通过数字下标获取元素 而JS的数组不这样:
- 元素的数据类型可以不同
- 内存不一定连续
- 不能通过数字下标,只能通过字符串下标,这也意味着数组可以有任何
key。例如:
let arr = [1, 2, 3];
arr['xxx'] = 1;
Object.keys(arr); // 返回 ['0', '1', '2', 'xxx']
创建一个数组
// 新建
let arr = [1, 2, 3];
let arr = new Array(1, 2, 3);
let arr = new Array(3); // 创建长度为3的空数组
// 转化
let arr = '1,2,3'.split(',')
let arr = '123'.split('')
Array.from('123') // 把不是数组的东西尝试转化成数组
一定要注意区分伪数组,伪数组的原型链中并没有数组的原型 (代码经验多了,报错看多了,这个很容易能分辨出来)
合并
arr1.concat(arr2) // 返回一个新数组
切片
arr.slice(1) // 从第一个元素开始切片arr.slice(0) // 可以用来复制数组,因为ES6不提供数组复制方法,更别提深拷贝和浅拷贝
CRUD(create, retrieve, update, delete)
删元素
let arr = ['a', 'b', 'c'];
delete arr[0];
- 数组删除元素后,数组长度并不会变,被删除的元素的位置都会被
undefined取代。 - 如果直接减小数组的
length,会导致数组元素丢失。 - 如果要删除头部或尾部的元素,建议使用
shift和pop方法 - 如果要删除中间的元素,可以使用
arr.splice()方法,代码如下
arr.splice(index, 3) // 删除index开始的三个元素
arr.splice(index, 1, 'x') // 删除index的一个元素,并在删除位置添加'x'
arr.splice(index, 1, 'x', 'y') // 并在删除位置添加'x', 'y'
查元素
因为数组也是一种对象,所以可以用传统方法,遍历属性名和属性值,但这种方法并不可靠。
Object.keys(arr);
Object.values(arr);
推荐用下面方法:
for(let i = 0; i < arr.length; i++){
console.log('${i}: ${arr[i]}');
}
// -------------------------------------------
// 数组原型提供了forEach方法
arr.forEach(function(item, index){
console.log('${index}: ${item}')
})
上述两种方法的区别是什么?
for关键字支持break和continue,而forEach不能for是关键字,块级作用域;forEach是函数,函数作用域
数组越界,arr[arr.length]会返回undefined。数组对象不支持倒叙索引,arr[-1]同样会返回undefined。
/*查看单个属性*/
arr.indexOf(item); // 存在返回索引,否则返回-1
arr.find(item => item % 2 === 0); // 使用条件查找元素,这里查找第一个偶数。条件查找只要找到一个符合条件的值,就会返回该value
arr.findIndex(item => item % 2 === 0) // 找第一个偶数的索引。该条件查找方法返回索引
增加元素
增加元素不要使用如下方法,因为数组可能变成稀疏数组,造成内存浪费。
let arr = [1, 2, 3, 4, 5];
arr[100] = 101;
console.log(arr);
// 数组长度为101,索引5到99上的值为undefined
正确的方法:
arr.push(value); // 尾部添加元素
arr.push(val1, val2, ...);
arr.unshift(val1, val2, ...); // 头部添加元素
arr.splice(index, 0, val) // 在index上开始,一个元素都不删,添加val
arr.splice(index, 1, val) // 修改index上的值,改为val
arr[index] = val // 这种方法修改更方便
反转数组:
arr.reverse()
// -----------------------
/*反转一个字符串*/
let s = 'abcde';
s.split('').reverse().join('');
数组排序
arr.sort() // 返回一个由小到大排列的数组
// -----------------------
/*sort自定义排序*/
let arr = {
{name: '小明', score: 99},
{name: '小红', score: 95},
{name: '大黄', scoreL 100}
}
arr.sort(function(a, b){
if(a.score > b.score) return 1;
else if(a.score === b.score) return 0;
else return -1
})
// 更简单的方法
arr.sort((a, b) => a.score - b.score)
数组变换
直白的图解(哈哈哈)
map
map方法对数组进行elementwise的映射。
let arr = [1, 2, 3, 4, 5, 6];
arr.map(item => item * item) // 元素一一变成平方数
filter
翻译过来就是“过滤”
arr.filter(item => item % 2 === 0 ? true: false) // 选取偶数元素,可简写为
arr.filter(item => item % 2 === 0)
reduce
reduce方法很强大(弄懂reduce和splice,数组就把玩的差不多了)
arr.reduce((sum, item)=>{return sum + item}, 0) // 获取元素的累加值sum,第二个参数为初始值。因为要获取累加值,所以sum初始值应该为0
// reduce方法使数组元素变为平方数
arr.reduce((result, item) => {return result.concat(item*item)}, [])
// reduce方法选取偶数元素
arr.reduce((result, item) => {return result.concat(item % 2 === 0 ? item : [])}, [])
由上述代码可见reduce方法可以取代filter和map方法,体现reduce方法的强大。
A tricky problem:
// solution
arr.reduce((result, item) => {
if(item.parent === null){
result.id = item.id;
result['名称'] = item['名称'];
} else{
result.children.push(item);
delete item.parent;
item.children = null;
}
return result
}, {id: null, children: []})
最后需要强调的是,数组变换不会改变数组本身!!!