JavaScript中的数组是一种特殊的对象,由于对象的属性必须是字符串的形式,所以数组的数字索引在内部被转为字符串类型。
创建数组
1.0 直接用 []创建
数组中的元素可以是不同的数据类型
var arr1 = [1,2,3,4,"hello","adf",false]; // 数组中的元素可以是不同的数据类型
console.log(arr1) //[ 1, 2, 3, 4, 'hello', 'adf', false ]
复制代码
2.0 用Array对象创建
var arr2 = new Array(1,2,4,5,"abc",true)
console.log(arr2) //[ 1, 2, 4, 5, 'abc', true ]
复制代码
Array
构造函数有一个很大的缺陷,就是不同的参数,会导致它的行为不一致。
// 无参数时,返回一个空数组
new Array() // []
// 单个正整数参数,表示返回的新数组的长度
new Array(1) // [ empty ]
new Array(2) // [ empty x 2 ]
// 非正整数的数值作为参数,会报错
new Array(3.2) // RangeError: Invalid array length
new Array(-3) // RangeError: Invalid array length
// 单个非数值(比如字符串、布尔值、对象等)作为参数,
// 则该参数是返回的新数组的成员
new Array('abc') // ['abc']
new Array([1]) // [Array[1]]
// 多参数时,所有参数都是返回的新数组的成员
new Array(1, 2) // [1, 2]
new Array('a', 'b', 'c') // ['a', 'b', 'c']
复制代码
3.0 通过字符串的split()方法生成数组
var s = "i am a web coder i can say hello world!";
var arr3 = s.split(" "); // split()函数的参数是指定分隔符,然后通过分隔符分割字符串形成数组
console.log(arr3) //[ 'i', 'am', 'a', 'web', 'coder', 'i', 'can', 'say', 'hello', 'world!' ]
复制代码
4.0 用已有的数组创建数组
4.0.1 通过数组拼接
用concat
拼接数组是生成了一个新的数组,原数组不发生变化
var a1 = [1,2,3];
var a2 = [4,5,6];
var a3 = a1.concat(a2); // 将a2中的元素按顺序拼接到a1的元素后面并生成两者拼接的新数组
console.log(a1); //[ 1, 2, 3 ] a1没有发生变化
console.log(a2); //[ 4, 5, 6 ] a2没有发生变化
console.log(a3); //[ 1, 2, 3, 4, 5, 6 ] a1和a2的顺序拼接
复制代码
4.0.2 通过数组截取
用splice
截取数组,原数组会发生变化,可用来删除数组中的元素
var b1 = [1,2,3,4,5];
var b2 = b1.splice(3,3); // 将b1数组中的从索引3开始截取最多3个元素,然后将截取的子数组赋值给b2
console.log(b1); // [ 1, 2, 3 ] b1数组会发生改变,截去splice的部分
console.log(b2); // [ 4, 5 ] b2数组获取splice(3,3)截取的子数组
复制代码
5.0 ES6 创建数组的两个API
5.0.1 Array.of
返回由所有参数值组成的数组,如果没有参数,就返回一个空数组
var a = Array.of(1,2,3,4,5) // [1,2,3,4,5]
var b = Array.of(7) // [7]
复制代码
5.0.2 Array.from
定义:用于将两类对象转为真正的数组(不改变原对象,返回新的数组)。
参数:
第一个参数(必需):要转化为真正数组的对象。
第二个参数(可选): 类似数组的map方法,对每个元素进行处理,将处理后的值放入返回的数组。
第三个参数(可选): 用来绑定this。
// 类数组是索引是 0 1 2 3... 并且有length属性的对象
let obj = {0: 'a', 1: 'b', 2:'c', length: 3};
let arr = Array.from(obj); // ['a','b','c'];
复制代码
增加数组元素
1.0 直接通过下标添加
通过数组索引添加,若添加的索引位置原来有值则更新原来的值,若没有值则新建一个数组元素,若新建的元素与之前的数组元素不连续,则中间的数据位置保留并为undefined
var a = [1,2,3];
a[a.length] = 4; //在末尾增加一个元素
console.log(a); // [ 1, 2, 3, 4 ]
a[10] = 10; //索引超过 a.length 也是可以插入的,数组会保留前面没有数据的位置
console.log(a); //[ 1, 2, 3, 4, <6 empty items>, 10 ]
console.log(a[8]); // undefined 保留的数据位置会设置为 undefined
复制代码
2.0 通过push在尾部添加
push
可以在数组的末尾添加1个或者多个元素,并返回添加完毕之后数组中元素的个数
var b = [1,2,3];
var b_length = b.push(4);
console.log(b_length); // 4 push的返回值是添加完元素之后的数组长度
b.push(5,6); //可以一次插入一个或者多个
console.log(b); // [ 1, 2, 3, 4, 5, 6 ]
复制代码
3.0 通过unshift在数组头部插入
unshift
可以在数组的头部插入一个或者多个元素,并返回添加完毕之后数组中的元素个数
var c = [1,2,3];
var c_length = c.unshift(4);
console.log(c_length); // 4 unshift返回的值是添加玩元素之后的数组长度
c.unshift(5,6);
console.log(c); //[ 5, 6, 4, 1, 2, 3 ]
复制代码
4.0 通过splice自由的插入元素
splice
有3个元素, 第一个是索引,删除或者添加的位置, 第二个是删除的元素个数,第三个是添加的元素
var d = ['a','b','c']
d.splice(3,0,4); // splice有3个元素, 第一个是索引,删除或者添加的位置, 第二个是删除的元素个数,第三个是添加的元素
console.log(d); // [ 'a', 'b', 'c', 4 ]
复制代码
删除数组元素
1.0 通过 pop 移除末尾的元素
pop
移除数组最后一个元素并将其返回
var a = [1,2,3];
var tmp = a.pop(); // 返回值为删除的元素
console.log(tmp); // 3
console.log(a); // [1,2]
复制代码
2.0 通过shift 移除头部的元素
shift
移除数组第一个元素并将其返回
var b = [1,2,3];
var tmp = b.shift(); // 返回值为删除的元素
console.log(tmp); // 1
console.log(b); // [2,3]
复制代码
3.0 通过splice 删除任意位置的元素
splice
有3个元素, 第一个是索引,删除或者添加的位置, 第二个是删除的元素个数,第三个是添加的元素
在删除数组元素的时候一般只用前两个,指定删除的索引起始位置和删除的个数,并将删除的元素按照子数组返回
var c = [1,2,3,4,5,6];
var tmp = c.splice(3,3); // 从索引3开始删除最多3个元素,截取的元素按照数组的形式返回
console.log(tmp); //[4,5,6]
console.log(c); // [1,2,3]
复制代码
查询数组元素
1.0 通过indexOf查找
indexOf
查询是否包含指定的元素,若存在则返回索引,不存在返回-1
var a = ['a','b','c','d'];
var index1 = a.indexOf('a');
var index2 = a.indexOf('e');
console.log(index1); //0
console.log(index2); //-1
复制代码
2.0 通过includes查找
查找数组中是否含有某个元素,存在返回true
,不存在返回false
var index3 = a.includes("a"); // 存在返回true 不存在返回false
console.log(index3);
复制代码
数组排序
使用sort
函数
对于字符类型的数组,可以直接用**sort()
**函数按照Unicode
码升序排序,如果需要反序
使用reverse()
var a = ['abc','hello','qwen','bob']; // 默认按照Unicode码的顺序排序
a.sort();
console.log(a); // [ 'abc', 'bob', 'hello', 'qwen' ]
console.log(a.reverse()); //[ 'qwen', 'hello', 'bob', 'abc' ]
var b = [1,100,12,3,4];
b.sort();
// sort()方法排序的时候默认排序的对象是字符串
// 所以在纯数字排序的时候会将number类型转成字符串类型进行排序
console.log(b); //[ 1, 100, 12, 3, 4 ]
// 数字排序的方法 通过给sort()传入一个比较函数
function compare(num2,num1){
/**
* 升序排序
* 返回值大于0 表示交换位置
* 返回值小于0 表示不用交换
* 等于0 相等
*/
return num2 - num1;
}
b.sort(compare)
console.log(b) // [ 1, 3, 4, 12, 100 ]
复制代码
数组的进阶操作
1.0 join
使用join()
拼接数组生成字符串,并返回这个字符串。其中join可以接受一个符号用来拼接字符串,默认的情况下为直接串联不添加任何拼接字符串。
var arr = [1,'a',true,'hello'];
var string1 = arr.join('-');
var string2 = arr.join();
console.log(string1) // 1-a-true-hello
console.log(string2); // 1atruehello
复制代码
2.0 slice
slice(start,end)
获取原数组的部分作为子数组返回,原数组不发生改变。截取的时候遵循左闭右开原则。
var a = [1,2,3,4,5,6,7];
var b = a.slice(0,5); // 包含start,不包含end 左闭右开
console.log(a); // [ 1, 2, 3, 4, 5, 6, 7 ] 原数组不发生改变
console.log(b); // [ 1, 2, 3, 4, 5 ]
复制代码
3.0 map
map()
创建一个新数组并返回,其中新数组的每个元素由调用原始数组中的每一个元素执行提供的函数得到,原数组不发生改变
var a = [1,2,3,4,5,6];
function twoSize(item){
return item * 2; //放大两倍
}
var b = a.map(twoSize);
console.log(a); // [ 1, 2, 3, 4, 5, 6 ]
console.log(b); // [ 2, 4, 6, 8, 10, 12 ]
// 使用匿名函数
var c = a.map(function(item){
return item * 3; // 放大三倍
});
console.log(c); //[ 3, 6, 9, 12, 15, 18 ]
复制代码
4.0 every
对数组中的每个元素都执行一次指定的回调函数,直到回调函数返回false
,此时every()
返回false并不再继续执行。如果回调函数对每个函数都返回true
,every()
函数也返回true
。
var a = [2,4,6,8,10];
var res1 = a.every(function(item){
return item % 2 == 0;//判断是否偶数
});
console.log(res1); //true
var res2 = a.every(function(item){
return item % 2 != 0; //判断是奇数
});// 在判断第一个元素是否奇数的时候就返回了false,后面的不再执行
console.log(res2); //false
复制代码
可以将every(func)
函数看成判断数组中的元素是否都满足func
指定的规则,若全部满足返回true
,若有一个不满足则不再判断返回false
操作类似于下面的截断运算
var tmp3 = func(a[0])&&func(a[1])&&...&&func(a[n])
复制代码
5.0 some
对数组中的每个元素都执行一次指定的回调函数,直到回调函数返回true
,此时some()
返回true
并不再继续执行。如果回调函数对每个元素都返回false
,那么some()
将返回false
。
var a = [1,2,3,4,5];
var res1 = a.some(function(item){
return item % 2 == 0;//只要有一个为偶数就返回true
});
console.log(res1); //true
复制代码
可以将some(func)
函数看成判断数组中的元素是否存在满足func
指定的规则的元素,若存在满足的元素返回true
,并不再向后判断,若全部不满足则返回false
操作类似于
var tmp3 = func(a[0]) || func(a[1]) ||...|| func(a[n])
复制代码
6.0 filter
创建一个新的数组,包含通过所有提供函数实现的测试的所有元素。过滤数组。原数组不发生改变
var a = [1,2,3,4,5,6]
/*
item:当前的遍历值
index:当前的索引
self:数组对象
*/
var res = a.filter(function(item,index,self){
return item % 2 == 0;//返回所有偶数
});
console.log(res); // [ 2, 4, 6 ]
复制代码
7.0 forEach
针对每一个元素执行提供的函数。会修改原来的数组,不会返回执行结果,返回undefined
。
var a = [1,2,3,4,5];
a.forEach(function(item){
return item * 2; //扩大两倍
});
复制代码
8.0 reduce
Array的reduce()
把一个函数作用在这个Array
的[x1, x2, x3...]
上,这个函数必须接收两个参数,reduce()
把结果继续和序列的下一个元素做累积计算,其效果就是:
[x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4)
复制代码
案例1:求累加和
var a = [1,2,3,4,5];
function add(total,next){
return total + next; // 每次的返回值会作为下次调用add的total
}
var b = a.reduce(add);
console.log(b); // 15
复制代码
案例2:求元素出现的次数
var words = ["apple","hello","orange","apple"];
var wordCount = function(prev,next){
prev[next] = (prev[next] + 1) || 1; // 如果存在就加1,否则设置为1
return prev;
}
var obj = words.reduce(wordCount,{});
console.log(obj); // { apple: 2, hello: 1, orange: 1 }
复制代码
对reduce的思考:reduce(callback,initiaValue)
会传入两个变量,回调函数(callback
)和初始值(initiaValue
)。如果不设置初始值,则第一次执行的时候,callback
的第一个参数是数组的第一个元素,第二个参数是数组的第二个元素。有初始值则prev
为初始值,next为数组第一个元素。
数组遍历
1.0 length + for 循环
for(let i =0;i<arr.length;i++){
console.log(i+":"+arr[i]);
}
复制代码
2.0 使用forEach
forEach中的回调函数的两个参数中值在前,索引在后
arr.forEach(function(val,index){
console.log(index+":"+val);
});
复制代码
3.0 for ... in
for...in
的方式遍历的是数组中的索引,前面也提到过数组是一种特殊的对象,在默认的情况下是以0...n
的数字作为索引,但是也可以用其他字符作为索引,所以当数组中存在其他索引的时候可以用for...in
获取
for(let index in arr){
console.log(index+":"+arr[index]);
}
复制代码
前面三种遍历打印的值
/*
0:a
1:b
2:c
3:d
*/
复制代码
4.0 for ... of
for ... of
直接遍历数组中的值
for(let val of arr){
console.log(val);
}
/*
a
b
c
d
*/
复制代码
5.0 for...in 和 for...of的区别
for … in
循环,它遍历的实际上是对象的属性名称。
一个Array数组实际上也是一个对象,它的每个元素的索引被视为一个属性。
当我们手动给Array对象添加了额外的属性后,for … in
循环将带来意想不到的意外效果:
var arr = ['a','b','c'];
arr.k = "hello";
for(let i in arr){
console.log(i+":"+arr[i]); //遍历的是属性值
}
/*
0:a
1:b
2:c
k:hello
*/
console.log(arr); // [ 'a', 'b', 'c', k: 'hello' ]
复制代码
for ... of
执行的时候,只遍历值但是没有去遍历新加的k
var arr = ['a','b','c'];
arr.k = "hello";
for(let val of arr){
console.log(val);
}
/*
a
b
c
*/
复制代码
详细看:developer.mozilla.org/zh-CN/docs/…
两个升序数组合为一个升序数组
var arr1 = [1,6,7,8,9]
var arr2 = [2,3,4,5]
// 1.0 合并成一个数组然后排序
function compare(num1,num2){
return num1 - num2;
}
var res = arr1.concat(arr2)
res.sort(compare);
console.log(res); //[ 1, 2, 3, 4, 5, 6, 7, 8, 9]
// 2.0 用两个指针 i j 指向两个数组的头部依次比较插入
var i = 0;
var j = 0;
tmp = [];
while(i<arr1.length&&j<arr2.length){
if(arr1[i]<=arr2[j]){
tmp.push(arr1[i]);
i++;
}else{
tmp.push(arr2[j]);
j++;
}
}
var res = tmp.concat(arr1.slice(i)).concat(arr2.slice(j)); //拼接没有遍历完的数组
console.log(res)
复制代码
数组去重
// 1.0 用set去重 然后用 Array.from转为list
var myset = Array.from(new Set(arr));
console.log(myset); // [ 1, 2, 3, 4 ]
// 2.0 用reduce
function compare(num1,num2){
return num1 - num2;
}
arr.sort(compare);//先排序
var res = arr.reduce(function(init,current){
if(init.length===0||init[init.length-1]!=current){
init.push(current);
}
return init;
},[]);
console.log(res); // [ 1, 2, 3, 4 ]
// 3.0 filter
var res = arr.filter(function(item,index,self){
return self.indexOf(item) === index; //从前往后查找对应元素的索引是否与当前索引相等,要是不相等说明之前出现过重复的元素
});
console.log(res);
复制代码
求两个数组的交集
遍历其中一个数组,然后用indexOf来判断是否在另一个数组中出现
var a = [1,2,3,4,4,5];
var b = [3,4,5,6,7];
// 1.0 用 indexOf
var res = []
for(let val of a){
if(b.indexOf(val)!=-1){
res.push(val);
}
}
console.log(res); // [ 3, 4, 4, 5 ]
复制代码
求只出现一次的数字
一个非空数组,其中只有一个元素只出现了一次,其余均出现两次,找出那个只出现一次的数字
// 1.0 用reduce
// 用 reduce统计出现次数
var res = a.reduce(function(init,current){
init[current] = (init[current] + 1) || 1;
return init;
},{});
// 遍历结果选出出现一次的元素
for(let i in res){
if(res[i]==1){
console.log(i);// 3
}
}
复制代码
两数之和
给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。需要考虑的是4-2=2这时候数组中必须有两个2
暴力方法
var arr = [1,2,3,5,9];
var target = 7;
// 暴力遍历 假设只有一组数相加为
for(let i=0;i<arr.length;i++){
for(let j=i+1;j<arr.length;j++){
if(arr[i]+arr[j]==target){
console.log(arr[i],arr[j])
}
}
}
复制代码
使用map
var arr = [1,2,5,2,9];
var target = 4;
// 使用map
var m = new Map();
var res = []; // 存放索引
for(let i=0; i<arr.length ;i++){
if(m.has(target-arr[i])){
// 第一次遇到就往里面存,这里若存在一定是之前存入的,不会与这次起冲突
res.push(i,m.get(target-arr[i]));
break;
}
m.set(arr[i],i);
}
console.log(res);
复制代码
判断数组中的元素是否唯一
使用set ,然后比较转为set之后的大小和原来的大小
var a = [1,2,3,4]
var b = [1,2,1,3];
function judge(arr){
let s = new Set(arr);
if(s.size==arr.length){
return true;
}
return false;
}
var flag1 = judge(a);
var flag2 = judge(b);
console.log(flag1); //true
console.log(flag2); //false
复制代码
移动零
将数组中所有元素值为0的元素移动到数组的末尾
1)必须在原数组上操作,不能拷贝额外的数组
2)尽量减少操作次数
用一个新索引记录不是0的数,并添加到原数组中,之后补全没有添加进去的0
var arr = [1,0,0,2,0,3,4,5];
var index = 0; //数组新的索引
const num = 0;//比较的对象
for(let i=0;i<arr.length;i++){
if(arr[i]!=num){
// 不为零就用新索引添加到原数组中
arr[index] = arr[i];
index += 1;
}
}
for(let i=index;i<arr.length;i++){
arr[i] = num;//补上之前没有添加的0
}
console.log(arr); //[ 1, 2, 3, 4, 5, 0, 0, 0 ]
复制代码
JavaScript中的坑点
这里有一个问题需要特别注意,如果字符串键值能够被强制类型转换为十进制数字的话,它就会被当作数字索引来处理。
var a = [];
a["13"] = 1;
console.log(a.length); //14
console.log(a); //[ <13 empty items>, 1 ]
复制代码