创建声明数组
使用对象方式创建数组
//基本类型创建时(回顾)
let str = new String('abc');//创建字符串对象
let bool = new Boolean(true);//创建Boolean类型对象
let num = new Number(124);//创建数字对象
//同前面讲的基本类型的创建一样,我们可以通多new创建对象的方式创建数组
let arr = new Array('a', 'b', 'c', 'd');
console.table(arr);
注意:使用对象方式创建数组时,如果元素时数字需要特别注意,new Array(3);所代表的意义:
let arr = new Array(1, 2, 3);
console.log(arr);//[1,2,3]
let ar = new Array(3);
console.log(ar);//[undefined,undefined,undefined]
//一个参数时,表示要创建的数组长度,而不是作为子元素。
//当然我们也可以通过Array.of(3);来创建包含一个元素的数组
let arr = Array.of(3);
console.log(arr);//[3]
//但但但我们更推荐下面字面量的方式创建数组
但我们更推荐使用字面量创建
const array = ["a", "b"];
length
属性可以获取数组元素数量
let arr= ["a", "b"];
console.log(arr.length); //2
//后端返回的数据不一定是数组可能为undefined或null,那么使用length属性就会报错,
//这是实际开发中经常遇到的问题,那么解决这个问题就要判断,其实我们应该养成习惯,
//对于接受的数据应加上判断处理,避免使用报错
arr && arr.length;//使用&&进行判断
Array方法
Array.isArray(arr);判断某个变量是否是一个数组对象
let arr = [1,2,3];
console.log(Array.isArray(arr));//true
Array.from(str);将类数组对象或者可迭代对象转换成数组
说明:1.类数组对象:有.length属性,例如DOM集合NodeList,arguments等,
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
let lis = document.querySelectorAll('ul li');
console.log(lis.length);//3
//使用Array.from(arr);转成数组,就可以使用数组的遍历方法了
Array.from(lis).map(item=>{
console.log(item);//<li>1</li><li>2</li><li>3</li>
});
//使用展开语法(点语法)转成数组
[...lis].map(item=>{
console.log(item);//<li>1</li><li>2</li><li>3</li>
})
</script>
2.可迭代对象:实现了[Symbol.iterator]方法的对象,例如字符串String
let str = '123';
console.log(Array.from(str));//["1", "2", "3"]
console.log(typeof str[Symbol.iterator] === 'function');//true
数组操作
push
从末尾压入一个或多个元素,直接改变原数组,返回值为数组元素数量
let arr = ['a','b'];
let len = arr.push('c');
len = arr.push('d','e');
console.log(len);//5(返回值为数组长度)
console.log(arr);//['a','b','c','d','e']
pop
从末尾弹出第一个元素,直接改变原数组,返回值为弹出的元素
let arr = ['a','b'];
let item = arr.pop();
console.log(item);//'b'(返回值为弹出的元素)
console.log(arr);//['a']
//我们继续弹出元素,当数组没有元素是,pop的返回值就是undefined(可用于清空数组,下面会做讲解)
console.log(arr.pop());//'a'
console.log(arr.pop());//undefined
shift
方法从数组开始位置删除第一个元素,并返回该元素的值
const array = [1, 2, 3];
const firstElement = array.shift();
console.log(array);// [2, 3]
console.log(firstElement);// 1
unshift
将一个或多个元素添加到数组的开头,并返回该数组的新长度
let arr = ['a','b'];
let len = arr.unshift('c');
len = arr.unshift('d','e');
console.log(len);//5(返回值为数组长度)
console.log(arr);//['d','e','c','a','b']
fill
用一个固定值填充一个数组中从起始索引到终止索引内的全部元素,不包括终止索引
arr.fill(value[, start[, end]]);
value:用来填充数组元素的值。
start :可选,起始索引,默认值为0。
end :可选,终止索引,默认值为 this.length
const array = [1, 2, 3, 4];
console.log(array.fill(0, 2, 4));//[1, 2, 0, 0] 把0填充到索引2和4之间,包括2,不包括4
console.log(array.fill(5, 1));//[1, 5, 5, 5]
console.log(array1.fill(6));//[6, 6, 6, 6]
说明:实际在开发中好像很少用到fill,但是可以用它模拟数据,比如大量重复数据;
let obj = {title: '测试',num: 20};
console.log(new Array(100).fill(obj));//[{title: '测试',num: 20},...]
slice
使用 slice
方法从数组中截取部分元素组合成新数组(并不会改变原数组),不传第二个参数时截取到数组的最后元素。
let arr = [0, 1, 2, 3, 4, 5, 6];
console.log(arr.slice(1, 3)); // [1,2]
//参数为负值
let arr = [0, 1, 2, 3, 4, 5, 6];
console.log(arr.slice(-1)); // [6]等价于arr.slice(arr.length - 1);
console.log(arr.slice(-2,-1)); // [5]等价于arr.slice(arr.length - 2,arr.length - 1);
console.log(arr.slice(-1,3)); // []如果第一个参数为负数,则第二个参数一定要为负数且大于第一个参数,否则截取不到元素
不设置参数是为获取所有元素
let arr = [0, 1, 2, 3, 4, 5, 6];
console.log(arr.slice()); //[0, 1, 2, 3, 4, 5, 6]
注意:slice截取是浅拷贝,即当数组元素是引用类型时,只是复制指针,如果改变截取的元素,则同时会改变原数组。
let arr = [{title: '测试',num: 20},{title: 'test',num: 30}];
let sArr = arr.slice(0,1);
sArr[0].num = 100;
console.log(sArr);//[{title: '测试',num: 100}]
console.log(arr);//[{title: '测试',num: 100},{title: 'test',num: 30}];
splice
删除、替换、添加数组中的元素,会对原数组进行改变,返回值为被删除的元素组成的数组
删除:第一个参数为从哪开始删除,第二个参数为删除的数量
let arr = [0, 1, 2, 3, 4, 5, 6];
console.log(arr.splice(1, 3)); //返回删除的元素 [1, 2, 3]
console.log(arr); //删除数据后的原数组 [0, 4, 5, 6]
替换:第一个参数为从哪开始,第二个参数为删除的数量,后面的所有参数为要放到该位置的元素
let arr = [0, 1, 2, 3, 4, 5, 6];
console.log(arr.splice(1, 1, 7, 8)); //返回被替换(删除)的元素 [1]
console.log(arr); //替换后的原数组 [0, 7, 8, 2, 3, 4, 5, 6]
添加:第一个参数为从哪开始,第二个参数设置为0,后面的所有参数为要添加该位置的元素
let arr = [0, 1, 2, 3, 4, 5, 6];
console.log(arr.splice(1, 0, 7, 8)); //返回被替换(删除)的元素 []
console.log(arr); //替换后的原数组 [0, 7, 8, 2, 3, 4, 5, 6]
负值:第一个参数为负值,则表示从数组末位开始的第几位(等价于arr.length - n),第二个参数为负值表示不删除同0.
let arr = [0, 1, 2, 3, 4, 5, 6];
console.log(arr.splice(-1, -10, 7, 8)); //等价于arr.splice(arr.length-1, 0, 7, 8)
console.log(arr); // [0, 7, 8, 2, 3, 4, 5, 6]
清空数组
经过上面知识的学习,清空数组的方式大概有4种:
第一种改变变量的指向,指向空数组,即 arr = [];(当存在多个引用的时候,需要注意一些问题)
let arr = [1, 2, 3];
let array = arr;
arr = [];
console.log(arr);//[]
console.log(array);//[1, 2, 3]
注意:经过上面的代码,我们会发现,将arr =[];后,array并没有改变,说明我们只是改变了arr的指向,将它指向[]空数组,但并没有改变真实数组,我们有图说明下:
第二种将length置为0;(真正把数组清空)
let arr = [1, 2, 3];
let array = arr;
arr.length = [];
console.log(arr);//[]
console.log(array);//[]
第三种使用splice
let arr = [1, 2, 3];
let array = arr;
arr = arr.splice(0);
console.log(arr);//[1, 2, 3]
console.log(array);//[]
第四种使用pop/shift
let arr = [1, 2, 3];
let array = arr;
while(arr.pop()){}//使用shift同理
console.log(arr);//[]
console.log(array);//[]
总结:只有第一种arr = []是只改变arr指向,其他三种方法都会改变内存中的真实数组。
join
将数组连接成字符串,可设置连接符
let arr = [1, 2, 3];
let str = arr.join();
console.log(str);//'1, 2, 3'
let string = arr.join('-');
console.log(string);//'1-2-3'
split
将字符串分割成数组
let str = '1-2-3';
let arr = str.split('-');
console.log(arr);//['1','2','3']注意这里元素是字符串
concat
连接合并两个或多个数组,元素是值类型的是复制操作,如果是引用类型还是指向同一对象
let arr1 = [1, 2];
let arr2 = [3, 4];
let arr3 = [5, 6];
let arr = arr1.concat(arr2, arr3);
console.log(arr);//[1, 2, 3, 4, 5, 6]
展开语法(...)es6
上面说到用concat合并数组,我们有更清晰的方法,就是使用es6的展开语法
let arr1 = [1, 2];
let arr2 = [3, 4];
let arr3 = [5, 6];
let arr = [...arr1, ...arr2, ...arr3];
console.log(arr);//[1, 2, 3, 4, 5, 6]
查找元素
数组包含多种查找的函数,包括查找元素,查找元素索引,判断元素是否存在。
indexOf
使用 indexOf
从前向后查找元素出现的位置,返回值是首个被找到的元素在数组中的索引位置; 若没有找到则返回 -1
let arr = [2, 9, 4, 5, 2, 1];
console.log(arr.indexOf(2));//0 从前面查找2出现的位置
console.log(arr.indexOf(8));//-1
indexOf是严格相等,即值和值的类型必须都相同。
let arr = [2, 9, 4, 5, '2', 1];
console.log(arr.indexOf('2'));//4
第二个参数指定查找起始位置,若起始索引大于数组长度则查找不到返回-1,若起始索引为负数,例如为-2,则起始索引为arr.length-2
let arr = [2, 9, 4, 5, 2, 1];
console.log(arr.indexOf(2,1));//4 从前面第二个位置开始查找2出现的位置
console.log(arr.indexOf(2,10));//-1
console.log(arr.indexOf(2,-2));//4
//等价于
console.log(arr.indexOf(2,arr.length - 2));//4
lastIndexOf
与indexOf不同的是 lastIndexOf
从后向前查找元素出现的位置,返回值也是首个被找到的元素在数组中的索引位置;如果找不到返回 -1
。
let arr = [2, 9, 4, 5, 2, 1];
console.log(arr.lastIndexOf(2));//4 从后面开始查找2出现的位置
第二个参数指定查找开始位置,与indexOf不同的是,起始索引大于数组长度或为负数都是从最后开始查找,
let arr = [2, 9, 4, 5, 2, 1];
console.log(arr.lastIndexOf(2));//4 从前面第二个位置开始查找2出现的位置
console.log(arr.lastIndexOf(2,10));//4
console.log(arr.lastIndexOf(2,-2));//4
//负数等价于
console.log(arr.lastIndexOf(2,arr.length + 2));//4
includes (es6)
如果判断元素存不存在,使用includes就再合适不过了,就不用像indexOf还要判断是否等于-1,因为includes返回值就是Boolean,但includes只能用于基本类型的查找,下面我们介绍查找引用类型时使用find和findIndex
let arr = [2, 9, 4, 5, 2, 1];
console.log(arr.includes(4));//true
console.log(arr.includes(6));//false
finde (es6)
查找元素并将查找到的第一个该元素返回
let shop = [
{id:1,name:'iMac',price:8000},
{id:2,name:'iPad',price:3000},
{id:3,name:'iPhon',price:7000}
];
//回调函数三个参数分别为item当前元素,index当前元素索引,arr原数组
let iPhon = shop.find((item,index,arr)=>{
return item.name == 'iPhon';
},{});//还可传递第二个参数,为执行回调时用作this 的对象。
console.log(iPhon);//{id:3,name:'iPhon',price:7000}
//使用includes是无法查找引用类型
console.log(shop.includes({id:3,name:'iPhon',price:7000}));//false
findIndex
与find使用基本相同,不过返回值是数组中找到的元素的索引
let shop = [
{id:1,name:'iMac',price:8000},
{id:2,name:'iPad',price:3000},
{id:3,name:'iPhon',price:7000}
];
let iPhonIndex = shop.findIndex((item,index,arr)=>{
return item.name == 'iPhon';
},{});
console.log(iPhonIndex);//2
数组排序
reverse
将数组中元素的位置颠倒,并返回该数组,该方法会改变原数组。
let arr = [1,2,4,5,6];
let array = arr.reverse();
console.log(array);//[6, 5, 4, 2, 1]
console.log(arr);//原数组也被改变[6, 5, 4, 2, 1]
let user = [{name:'张三'},{name:'李四'},{name:'王五'}]
let reverseUser = user.reverse();
console.log(reverseUser);//[{"name":"王五"},{"name":"李四"},{"name":"张三"}]
sort
默认排序顺序是在将元素转换为字符串,然后按照各个字符的Unicode位点进行排序
let sArr = ['c','d','a','bb'];
console.log(sArr.sort());//["a", "bb", "c", "d"]
let arr = [2,10,4,33];
console.log(arr.sort());//[10, 2, 33, 4]
指定排序函数:
let shop = [
{id:1,name:'iMac',price:8000},
{id:2,name:'iPad',price:3000},
{id:3,name:'iPhon',price:7000}
];
/*排序遵循:
返回负数 a 排在 b前面,从小到大
返回正数 b 排在a 前面
返回 0 时不动*/
let sortShop = shop.sort((a,b)=>{
return a.price - b.price;
});
console.log(sortShop);
循环遍历
for
let arr = [1,2,4,5,6];
for (let index = 0; index < arr.length; index++) {
arr[index] = arr[index]+10;
}
console.log(arr);//[11, 12, 14, 15, 16]
forEach
除了抛出异常以外,没有办法中止或跳出 forEach()
循环,也就是使用break终止遍历会报语法错误
let arr = [1,2,4,5,6];
arr.forEach((item,index,array)=>{
console.log(item);
break;//Uncaught SyntaxError: Illegal break statement
});
for ... in
for...in
是为遍历对象属性而构建的,不建议数组使用(确切的说应该是数组禁止使用for/in遍历,因为遍历顺序有可能不是按照实际数组的内部顺序)
for ... of
遍历可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等)
这里有两个需要注意的问题,我们先来看代码:
//问题一:可以循环中使用let和const定义变量,为什么可以使用const呢?
//其实不管是用let还是const,每次循环都会创建了一个单独的变量
//但要注意使用const定义变量时不可修改的
let arr = [1, 2, 4, 5, 6];
for (let item of arr) {
console.log(item);
}
for (const item of arr) {
// item = item + 1;//不可修改会报错
console.log(item);
}
//问题二:如果元素是基本类型,修改遍历的值是不会修改原数组的
//但如果是引用类型是可以修改原数组的
let arr = [1, 2, 4, 5, 6];
for (let item of arr) {
item = item + 1;
}
console.log(arr);// [1, 2, 4, 5, 6]
let shop = [
{ id: 1, name: 'iMac', price: 8000 },
{ id: 2, name: 'iPad', price: 3000 },
{ id: 3, name: 'iPhon', price: 7000 }
];
for (let item of shop) {
item.price = item.price + 1;
}
console.log(shop);// [{{ id: 1, name: 'iMac', price: 8001 },...}]
keys()
返回一个包含数组中每个索引键的Array Iterator
对象。
const arr = ['a', 'b', 'c'];
const iterator = arr.keys();
console.log(iterator);//Array Iterator {}
那么我们如何取出数组迭代对象中的值呢,我们除了使用for/of,还可以使用迭代对象next()方法
const arr = ['a', 'b', 'c'];
const iterator = arr.keys();
// console.log(iterator);//Array Iterator {}
// console.log(iterator.next());//{value: 0, done: false}
// console.log(iterator.next());//{value: 0, done: false}
// console.log(iterator.next());//{value: 0, done: false}
// console.log(iterator.next());//{value: undefined, done: true}
//我们发现next()返回值为对象,其中第一个值为key值,第二个值为是否迭代完成,可以使用while
//这里使用到结构赋值
while(({value, done} = iterator.next()) && done === false){
console.log(value);
}
values()
和keys()的唯一区别就是返回值为value组成的 Array Iterator
对象,使用和keys()完全相同。
entries()
返回一个新的Array Iterator对象,该对象包含数组中每个索引的键/值对
const arr = ['a', 'b', 'c'];
for (const item of arr.entries()) {
console.log(item);//[0, "a"] [1, "b"] [2, "c"] [2, "c"]
}
// 结构赋值
for (const [key, value] of arr.entries()) {
console.log(key, value);//0 "a" 1 "b" 2 "c"
}
es6新增方法
every
数组内的所有元素是否都能通过某个指定函数的测试,它返回一个布尔值。
let arr = [1,2,4,5,6];
//数组中的元素是否全部大于0
let isMin = arr.every((item,index,array)=>{
return item > 0;
});
console.log(isMin);//true
some
数组中是不是至少有1个元素通过了被提供的函数测试,它返回的是一个Boolean类型的值。
let arr = [1,2,4,5,6];
//数组中的元素是否存在大于4的项
let isMin = arr.some((item,index,array)=>{
return item > 4;
});
console.log(isMin);//true
filter
过滤数组,返回值为满足条件的元素组成的新数组。
let arr = [1,2,4,5,6];
//过滤出于4的元素,组成新数组
let newArr = arr.filter((item,index,array)=>{
return item > 4;
});
console.log(newArr);// [5, 6]
map
由返回值组成的新数组
let arr = [1,2,4,5,6];
// 原数组每个元素乘2,并返回组成新数组
let newArr = arr.map((item,index,array)=>{
return item * 2;
});
console.log(newArr);// [2, 4, 8, 10, 12]
console.log(arr);// [1, 2, 4, 5, 6]
reduce
参数:
callback
执行数组中每个值 (如果没有提供 initialValue则第一个值除外
)的函数,包含四个参数:
accumulator
累计器累计回调的返回值; 它是上一次调用回调时返回的累积值,或initialValue
(见于下方)。
currentValue
数组中正在处理的元素。
index
可选
数组中正在处理的当前元素的索引。 如果提供了initialValue
,则起始索引号为0,否则从索引1起始。
array
可选
调用reduce()
的数组
initialValue
可选
作为第一次调用 callback
函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错。
例子
数组里所有值的和
var sum = [0, 1, 2, 3].reduce(function (accumulator, currentValue) {
return accumulator + currentValue;
}, 0);
// 和为 6
将二维数组转化为一维
var flattened = [[0, 1], [2, 3], [4, 5]].reduce(
function(a, b) {
return a.concat(b);
},
[]
);
// flattened is [0, 1, 2, 3, 4, 5]
计算数组中每个元素出现的次数
var names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
var countedNames = names.reduce(function (allNames, name) {
if (name in allNames) {
allNames[name]++;
}
else {
allNames[name] = 1;
}
return allNames;
}, {});
// countedNames is:
// { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }