上一篇我们学习了JavaScript的对象数据类型,对象是一组键-值对,很多时候我们需要一组连续的数据集合,比如从后台获取博客列表,这个时候存储这个列表用数组是最合适的。
数组在JavaScript中是很常用的数据类型,数组有很多操作方法,包括添加删除,查找索引,排序,遍历等等,掌握了这些方法,才能玩转数组,本篇我们来一起学一学JavaScript中的Array
数据类型及一些常用的数组方法。
const skills = [];//定义一个空数组
skills[0] = 'html';//添加元素
skills[1] = 'css';//添加元素
skills[2] = 'javascript';//添加元素
也可以在定义时就初始化一些元素:
const skills = ['html', 'css', 'javascript'];
当我们定义一个数组,JavaScript引擎会在内存中分配一段连续的内存空间,因此数组元素是按顺序存储的,这样我们访问数组元素就可以通过索引来访问,索引从0开始
console.log(skills[0], skills[1], skills[2]);//html css javascript
给数组添加一个元素也很简单:
skills[3] = 'jquery';
修改数组元素:
skills[3] = 'lodash';
查看数组大小
skills.length;//4
通过Array构造函数来创建:
const skills = new Array();//定义一个空数组
skills[0] = 'html';//添加元素
skills[1] = 'css';//添加元素
skills[2] = 'javascript';//添加元素
skills[3] = 'jquery';//添加元素
skills[3] = 'lodash';//修改元素
console.log(skills.length);//4
console.log(skills);//['html', 'css', 'javascript', 'lodash']
Array()构造函数可以接收参数
const skills = new Array(3); //定义一个长度为3的数组
console.log(skills.length);//3
如果构造函数的参数大于1个,那么相当于是创建了一个以这些参数为元素的数组
const skills = new Array(3, 4);
console.log(skills);//[3, 4]
上面我们通过索引的方式添加元素,其实JavaScript有内置的数组方法供我们在头/尾直接添加/取出元素:
在数组末尾
添加元素
const skills = [];
skills.push('html');
skills.push('css');
skills.push('javascript');
console.log(skills);//['html', 'css', 'javascript']
取数组末尾
元素
console.log(skills.pop());//javascript
console.log(skills.pop());//css
console.log(skills.pop());//html
console.log(skills);//[],数组元素已经全部取走,现在是空数组
这组方法和push/pop相反,unshift/shift是在数组首位
添加/删除元素
const skills = [];
skills.unshift('html');
skills.unshift('css');
skills.unshift('javascript');
console.log(skills);//['javascript', 'css', 'html']
console.log(skills.shift());//javascript
console.log(skills.shift());//css
console.log(skills.shift());//html
console.log(skills);//[],数组元素已经全部取走,现在是空数组
注意这里数组元素添加/删除的顺序
和Object
一样,数组也是引用类型,将一个数组赋值给另一个变量,二者引用的是同一个地址
const skills = ['html', 'css', 'javascript'];
const myskills = skills;
myskills[2] = 'jquery';//修改myskills的元素
console.log(skills);//[ 'html', 'css', 'jquery' ]
console.log(myskills);//[ 'html', 'css', 'jquery' ]
可以看到,二者是相互影响的,下面看看数组的拷贝方法
slice()
:将一个数组拷贝至另一个数组
const skills = ['html', 'css', 'javascript'];
const myskills = skills.slice();
myskills[2] = 'jquery';
console.log(skills);//[ 'html', 'css', 'javascript' ]
console.log(myskills);//[ 'html', 'css', 'jquery' ]
可以看到此时skills
和myskills
数组相互独立
slice
可以添加参数,拷贝部分数组元素
slice(start, end)
:注意这里拷贝时不包含end,即拷贝从start至(end - 1)的元素
const skills = ['html', 'css', 'javascript'];
const myskills = skills.slice(0, 2);//拷贝索引0-1的元素
console.log(skills);//[ 'html', 'css', 'javascript' ]
console.log(myskills);//[ 'html', 'css']
slice(start)
:拷贝从start开始至数组最后的所有元素
start
和end
都可以为负数,数组最后一个元素索引为-1
const skills = ['html', 'css', 'javascript'];
const myskills = skills.slice(-3);//拷贝-3至-1位的元素,第一位索引就是-3,相当于拷贝了整个数组
console.log(myskills);//[ 'html', 'css', 'javascript' ]
咋一看和slice有点相像,但这个方法用起来比slice要复杂,功能很强大,接收的参数也比较多,这里来看看常用的场景
splice(start)
:删除从start开始至数组末尾的所有元素
const skills = ['html', 'css', 'javascript'];
const removed = skills.splice(1);
console.log(skills); //[ 'html' ]
console.log(removed); //[ 'css', 'javascript' ]
splice(start, count)
:删除数组从start开始count个元素
const skills = ['html', 'css', 'javascript'];
const removed = skills.splice(1, 2); //删除从索引1开始的2个元素
console.log(skills); //[ 'html' ]
console.log(removed); //[ 'css', 'javascript' ]
splice(start, count, elem1, elem2,...)
:删除数组从start开始count个元素,并在start位置插入提供的元素,可以插入多个元素,下例,我们删除了索引从1开始的2个元素,并插入了两个新元素jquery
和lodash
const skills = ['html', 'css', 'javascript'];
const removed = skills.splice(1, 2, 'jquery', 'lodash');
console.log(skills); //[ 'html', 'jquery', 'lodash' ]
console.log(removed); //[ 'css', 'javascript' ]
start也可以为负数,最后一位元素索引为-1,下例,我们删除了最后一个元素,并插入了两个新元素jquery
和lodash
const skills = ['html', 'css', 'javascript'];
const removed = skills.splice(-1, 1, 'jquery', 'lodash');
console.log(skills); //[ 'html', 'css', 'jquery', 'lodash' ]
console.log(removed); //[ 'javascript' ]
indexOf(searchElem, startIndex)
: 从startIndex开始,顺序(从左向右)查找某个元素的索引,如果没有找到返回-1,startIndex可选,如果不提供,相当于是从第一位索引即0开始
lastIndexOf(searchElem, startIndex)
: 和indexOf类似,但是是逆序(从右向左)查找,startIndex可选,如果不提供,相当于是从最后一位索引即-1开始
const skills = ['html', 'css', 'javascript', 'html'];
console.log(skills.indexOf('html')); //0
console.log(skills.indexOf('html', 1)); //3
console.log(skills.indexOf('html5')); //-1
console.log(skills.lastIndexOf('html')); //3
console.log(skills.lastIndexOf('html', -1)); //3
console.log(skills.lastIndexOf('html', -2)); //0
console.log(skills.lastIndexOf('html5')); //-1
这一组方法是查找满足某个特定条件的元素,该条件通过回调函数传递,当数组中的元素是对象时,可以发挥很大作用
find():查找 第一个
满足条件的元素
findIndex():查找 第一个
满足条件的元素的索引
const blogs = [
{ title: 'Array学习', author: '李雷' },
{ title: 'Object学习', author: '韩梅梅' },
{ title: 'Array学习', author: 'Jack' },
];//blogs数组的元素是三个对象
const blog = blogs.find(blog => blog.title === 'Array学习');//查找数组元素中title为“Array学习”的对象
console.log(blog);//{ title: 'Array学习', author: '李雷' }
const blogIndex = blogs.findIndex(blog => blog.title === 'Array学习');
console.log(blogIndex);//0
以上我们用箭头函数,也可以用普通的函数,返回值为bool,如:
function compare(blog){
return blog.title === 'Array学习';
}
blogs.find(compare);
blogs.findIndex(compare);
includes(elem)
:判断某个值是否属于某个数组,返回值为bool类型
const numbers = [1, 2, 3, 4, 5];
console.log(numbers.includes(1)); //true
console.log(numbers.includes(0)); //false
console.log(numbers.includes('1')); //false
includes(elem, startIndex)
:从startIndex开始判断某个值是否属于某个数组,返回值为bool类型,startIndex可选,不提供则默认为0,
如果startIndex>=数组长度,返回false
console.log(numbers.includes(1, 0)); //true
console.log(numbers.includes(1, 1)); //false
console.log(numbers.includes(5, 4)); //true
console.log(numbers.includes(5, 5)); //false
和includes
作用一样,some
用来判断某个值是否属于某个数组,但此方法用的是条件判断函数,如果判断函数返回true则some方法返回true,否则some方法返回false
const numbers = [1, 2, 3, 4, 5];
console.log(numbers.some(number => number > 1)); //true
console.log(numbers.some(number => number > 5)); //false
通过for循环即可遍历数组
for(let index = 0; index < arr.length; index++){...}
这里我们看数组方法forEach
的使用方法
forEach()
接收一个回调函数,遍历数组中的每一个元素,对每个元素调用回调函数,回调函数只用提供一个参数即可正常使用,如:
const numbers = [1, 2, 3, 4, 5];
numbers.forEach(number => console.log(number));//1,2,3,4,5
回调函数多个参数情形:
forEach(callback(currentValue [, index [, array]]))
const numbers = [1, 2, 3, 4, 5];
numbers.forEach((number, index, array) =>
console.log(`当前元素:${number},index:${index},当前数组${array}`)
);
//当前元素:1,index:0,当前数组1,2,3,4,5
//当前元素:2,index:1,当前数组1,2,3,4,5
//当前元素:3,index:2,当前数组1,2,3,4,5
//当前元素:4,index:3,当前数组1,2,3,4,5
//当前元素:5,index:4,当前数组1,2,3,4,5
当你想转换你的数组时,map很有用,map不会修改原数组,会返回一个新数组
例如:
const arrs = ['Array', 'Object', 'Function'];
const finalarrs = arrs.map(arr => `${arr}学习`);
console.log(finalarrs);//[ 'Array学习', 'Object学习', 'Function学习' ]
原素组没有发生变化
console.log(arrs);//['Array', 'Object', 'Function']
当你想筛选数组的元素时,可以用filter,同样,filter也不会修改原数组,会返回一个满足筛选条件的所有元素的新数组
例如:
const numbers = [1, 2, 3, 4, 5];
const filtered = numbers.filter(number => number > 3);
console.log(filtered);//[ 4, 5 ]
console.log(numbers);//[1, 2, 3, 4, 5]
reduce用起来稍显复杂,非常适合用来做计算,统计等
reduce接收一个回调函数,这个函数可以有四个参数
arr.reduce(callback(accumulator, currentValue[, index[, array]] )[, initialValue])
如果没有提供初始值(initialValue)时,accumulator为数组第一个元素的值,index为1,currentValue即为第一个元素的值,例如:
const numbers = [0, 1, 2, 3, 4];
const result = numbers.reduce(function(accumulator, currentValue, currentIndex, array) {
const result = accumulator + currentValue;
console.log(accumulator, currentValue, currentIndex, array, result);
return result;
});
上面我们将每一步的四个参数都输出,即可看的非常清楚这个过程了,结果如下
accumulator | currentValue | currentIndex | array | result |
---|---|---|---|---|
0 | 1 | 1 | [ 0, 1, 2, 3, 4 ] | 1 |
1 | 2 | 2 | [ 0, 1, 2, 3, 4 ] | 3 |
3 | 3 | 3 | [ 0, 1, 2, 3, 4 ] | 6 |
6 | 4 | 4 | [ 0, 1, 2, 3, 4 ] | 10 |
本例没有提供初始值
故,第一次迭代时
accumulator = numbers[0] = 0
currentValue = numbers[1] = 1
currentIndex = 1
最后一次迭代的结果即为最终reduce返回的结果10
console.log(result);//10
如果提供了初始值(initialValue),accumulator为初始值(initialValue),index为0,currentValue即为第一个元素的值,例如:
const result = numbers.reduce(function(accumulator, currentValue, currentIndex, array) {
const result = accumulator + currentValue;
console.log(accumulator, currentValue, currentIndex, array, result);
return result;
}, 10);
accumulator | currentValue | currentIndex | array | result |
---|---|---|---|---|
10 | 0 | 0 | [ 0, 1, 2, 3, 4 ] | 10 |
10 | 1 | 1 | [ 0, 1, 2, 3, 4 ] | 11 |
11 | 2 | 2 | [ 0, 1, 2, 3, 4 ] | 13 |
13 | 3 | 3 | [ 0, 1, 2, 3, 4 ] | 16 |
16 | 4 | 4 | [ 0, 1, 2, 3, 4 ] | 20 |
本例没有提供初始值
故,第一次迭代时
accumulator = 初始值 = 10
currentValue = numbers[0] = 0
currentIndex = 0
这里参数array在每次迭代时,输出都一样,它其实代表的就是当前被reduce的数组,实际使用中可以不要
同样如果不需要知道当前迭代的次数index参数也可以不要
初始值还可以是其它数据类型如对象,例如我们有一组博客数组,我们需要按category字段来分组,这时可以用reduce来实现此需求:
const blogs = [{
title: 'Array学习',
category: 'javascript'
}, {
title: 'Object学习',
category: 'javascript'
}, {
title: 'Animation学习',
category: 'css'
}, {
title: '语义化标签学习',
category: 'html'
}];
const groupByCategory = blogs.reduce((accumulator, currentValue) => {
console.log(accumulator, currentValue);
const key = currentValue["category"];//当前的category值
if (!accumulator[key]) {//如果accumulator对象还没有该key,则初始化为空数组
accumulator[key] = [];
}
accumulator[key].push(currentValue);//将当前对象插入数组中
return accumulator;
}, {});
console.log(groupByCategory);
//最终得到的结果如下:
// {
// javascript: [
// { title: 'Array学习', category: 'javascript' },
// { title: 'Object学习', category: 'javascript' }
// ],
// css: [ { title: 'Animation学习', category: 'css' } ],
// html: [ { title: '语义化标签学习', category: 'html' } ]
// }
sort()方法给数组元素排序,会改变原数组
例如:
const numbers = [1, 2, 12, 3, 4];
numbers.sort();
console.log(numbers);//[ 1, 12, 2, 3, 4 ]
可以看到,排序后数组与原来的数组已不一样,但结果似乎不是我们想要的,因为sort()方法默认将数组元素按string类型
来排序
console.log('12' < '2'); //true
怎样让数字按我们想要的排序方式来呢?
可以给sort()传一个函数,如果这个函数返回正数代表大于
,返回负数代表小于
,返回0代表等于
function compareNumber(a, b){
return a - b;
}
const numbers = [1, 12, 2, 3, 4];
numbers.sort(compareNumber);
console.log(numbers); //[ 1, 2, 3, 4, 12 ]
当然用ES6箭头函数会更简洁
numbers.sort((a, b) => a - b);
将数组逆序返回,会改变原数组
const numbers = [1, 2, 12, 3, 4];
numbers.reverse();
console.log(numbers);//[ 4, 3, 12, 2, 1 ]
join()将数组元素合并成一个字符串,如果不提供参数,则以逗号分隔,否则按提供的参数分隔
const elements = ['Fire', 'Air', 'Water'];
console.log(elements.join());//Fire,Air,Water
console.log(elements.join(''));//FireAirWater
console.log(elements.join('-'));//Fire-Air-Water
join常和split结合使用,split是将字符串拆成数组,
const str = "Fire,Air,Water"
const elements = str.split(',');//['Fire', 'Air', 'Water']
判断某个给定值值是否为数组,返回值为bool类型
Array.isArray([1, 2, 3]); // true
Array.isArray({foo: 123}); // false
Array.isArray('foobar'); // false
Array.isArray(undefined); // false
不用数组方法,也可以判断是否为数组,下面提供两种方法参考:
const numbers = [1,2,3];
numbers.constructor === Array;//true
Object.prototype.toString.call(numbers) === '[object Array]';//true
如果用typeof判断数组,返回的是object,无法知道是否为数组
console.log(typeof []);//object
数组方法真的很多,这里就不一一列举了,有需要的时候可以再去查文档 MDN Array
数组定义:
let arr = []
let arr = new Array()
添加删除元素
数组拷贝
查找数组元素
判断元素是否存在
遍历
数组计算,转换,过滤
string
类型排序,会改变原数组判断是否为数组