JavaScript(六):数组

140 阅读4分钟

数组是一种特殊的对象。

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,会导致数组元素丢失。
  • 如果要删除头部或尾部的元素,建议使用shiftpop方法
  • 如果要删除中间的元素,可以使用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关键字支持breakcontinue,而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)

数组变换

直白的图解(哈哈哈)

1649854459(1).png

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方法很强大(弄懂reducesplice,数组就把玩的差不多了)

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方法可以取代filtermap方法,体现reduce方法的强大。

A tricky problem:

1649856140(1).png

// 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: []})

最后需要强调的是,数组变换不会改变数组本身!!!