数组是值的有序集合,每个值叫做一个元素,而每一个元素在数组中有一个位置,以数字表示,称为索引。JavaScript数组是无类型的,数组元素可以是任意类型且同一个数组中不同元素也可能有不同的类型。数组的元素甚至也可以是对象或其它数组,这允许创建复杂的数据结构,像是对象的数组,数组的数组。数组Array类型是js中最常用的类型。且js中的数组与其它多数语言中的数组有比较大的区别。从以下几个方面进行分析:
一、创建数组
1、直接量创建数组
使用数组字面量是创建数组最简单的方法,在方括号中将数组元素用逗号隔开。数组直接量中的值不一定要是常量,它们可以是任意的表达式;它可以包含对象直接量或者是其它数组直接量,如果省略数组直接量中的某个值,省略的元素将被赋予。undefined值。
<script>
let empty = [];//空数组
let obj = [1.1, '1.1', true];//三个不同类型的数组
let a = [[0, { x: 1, y: 2 }], [3, { x: 4, y: 5 }]];
let b = [1, , 3];//数组有三个元素,中间的值为undefined
let c = [,,];//数组有两个元素,同为undefined
</script>
2、使用Array()构造函数
调用构造函数Array()是创建数组的另外一种方法,可以用三种方式调用构造函数。当调用时没有参数,相当于创建了一个空数组;当调用时有一个数值参数,它指定长度;有多个参数时,参数表示为数组的具体元素。使用Array()构造函数时,可以省略new操作符。
<script>
let obj = new Array();
let a = new Array(20);
let b = new Array('20');
let c = new Array(1, 2, 3);
console.log(a);//Array(20)
console.log(a[0], a.length);//undefined 20
console.log(b);//["20"] 若存在一个其它类型的参数,则会创建包含那个值只有一项的数组
console.log(b[0], b.length);//20 1
console.log(c);//(3) [1, 2, 3] 使用Array()构造函数时,可以省略new
</script>
3、Array.of()方法(es6新增)
Array.of()方法创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型。可以这样理解,这个方法用于将一组值转化为数组,创建新数组。
Array.of()和new Array()的区别:Array.of()不论几个参数,总是返回参数值组成的数组,而new Array() 传一个参数时,表示生成多大的数组,传多个参数时,每个参数都是数组的一个元素。
<script>
let arr1 = new Array();
let arr2 = new Array(5);//创建一个长度为5,各项为empty的数组
let arr3 = new Array(1, '2', true, { a: 'a' });
console.log(arr1.length, arr2.length, arr3.length); //0 5 4
let arr4 = Array.of(1, 3, 4);
let arr5 = Array.of(4); //创建一个具有单个元素为4的数组
console.log(arr4.length, arr5.length); //3 1
</script>
二、数组元素的读和写
数组是对象的特殊形式,它是按次序排列的一组值。使用[]操作符来访问数组中的一个元素。数组的引用位于方括号的左边。方括号中的是一个返回非负数整数值的任意表达式。使用这种语法即可写又可以读数组的一个元素。
它的特殊性体现在,它的键名是按次序排列的一组整数,数组成员的键名是固定的,因此数组不用为每个元素指定键名,而对象的每个成员都必须对键名进行指定。由于js语言规定,对象的键名一律为字符串,所以数组的键名其实也是字符串,之所以可以用数值读取,是因为非字符串的键名会被转为字符串,然后将其作为属性名来使用。
需要强调的一点是,要区分数组索引和对象的属性名,所有的索引都是属性名,但是不是所有的整数属性名是索引。只有在0~2的32次方-2之间的整数属性名才是索引。可以使用负数或者是非整数来索引数组,这种情况下,数值转换成为字符串,字符串作为属性名来用。它只能当做是常规的对象属性,由于不在数组索引的范围值之内,所以它不能是数组的索引,不能改变数组的长度。
<script>
obj = {};//创建一个对象
obj[0] = 'obj';//用一个整数来索引它
let arr = ['a', 'b', 'c'];
console.log(arr[0]) === console.log(arr['0']); //a
// arr[-1]=true;
arr['abc'] = 'testing'; //属性名
console.log(arr.length); //3 数组长度不会改变
arr[10] = 4;
console.log(arr.length); //11
</script>
三、稀疏数组
稀疏数组就是包含从0开始的不连续索引的数组,通常,数组的length属性值代表数组中元素的个数,如果数组是稀疏的,length属性值大于元素的个数。足够稀疏的数组通常在实现上比稠密的数组更慢,内存利用率更高,在这样的数组中查找元素的时间与常规对象属性的查找时间一样长。
产生稀疏数组的三种方法: Array(), delete操作符, 逗号。
<script>
obj = new Array(5); //数组没有元素,但是a.length是5
obj = []; //创建一个空数组
obj[1000] = 0; //赋值添加一个元素,但是设置length为1001
let arr = [1, 2, 3, 4, 5];
let arr1 = [1, , 3, 4, 5];
let arr2 = [1, undefined, 3, 4, 5];
delete arr[1];
console.log(arr[1], arr1[1], arr2[1]); //undefined undefined undefined
console.log(1 in arr, 1 in arr1, 1 in arr2); //false false true
//省略元素值和值为undefined的元素是有区别的
</script>
四、数组长度
每个数组有一个length属性,length的最大值是2的32次方减1(4294967295)。就是length这个属性使其区别于常规的js对象,针对稠密数组,length属性值代表数组中元素的个数,其值比数组中最大的索引大1.当数组是稀疏数组时,length属性值大于元素的个数,,同样的其值比数组中最大的索引大1.(如果为一个数组元素赋值,它的索引 i大于等于现有数组的长度时,length的值将设置为i+1)
<script>
console.log([].length); //0 数组中没有元素
console.log(['a', 'b', 'c'].length); //3 最大索引为2,length为3
console.log([, , ,].length);//3
console.log(Array(10).length) //10
</script>
数组的长度是可以动态调整的,可读可写,写的话为增加或者删除数据。
当length设置为大于数据总数的话,会在数末尾添加空位(读取时为undefined),数据总数为length的值;length设置为小于数据总数的话,会在末尾删除数据,数据总数为length;length设置为0的话,表示清空数组;length设置为小于0的非数字,报错。
数组添加的属性并不会影响length的值。
<script>
let color = ['red', 'green', 'blue', 'yellow'];
color.length = 8;
console.log(color); //["red", "green", "blue", "yellow", empty × 4]
color.length = 2;
console.log(color); //["red", "green"]
color.length = 0;
console.log(color); //[]
//color.length=-1; //报错
//color.length='kaivon'; //报错
</script>
五、数组遍历
使用for和for..in..都是可以遍历数组元素的最常见方法。
<script>
let arr=[1,2,3,4,5];
//用for进行遍历
for(var i=0;i<arr.length;i++){
console.log(arr[i]); //1 2 3 4 5
}
//用for当然也可以用while
let i = 0;
while(i < arr.length){
console.log(arr[i]); //1 2 3 4 5
i++
}
let a = arr.length;
while(a--){
console.log(arr[a]); //5 4 3 2 1
}
//用for in 遍历稀疏数组
let b =[1,,3,4];
for(var i in b){
console.log(b[i]); //1 3 4
}
for(var i=0;i<b.length;i++){
if(!(i in b)) continue;
console.log(b[i]); 1 3 4
}
</script>
js规范规范允许用for..in循环以不同的顺序遍历对象的属性,通常数组通常数组元素的遍历实现是升序的,但不能保证一定是这样的。
六、数组乱序
数组乱序也称之为洗牌,一般来说有以下两种方法:
1、给数组原生的sort()方法传入一个函数,此函数随机的返回1或者是-1,达到随机排列数组元素的目的(这个不完全是随机的)。
2、依次遍历数组中的每个元素,遍历到的每个元素与一个随机位置的元素交换值,洗牌数组
<script>
//借助sort()方法
let values = [1, 2, 3, 4, , 5, 6, 7, 8, 9, 0];
values.sort(function () {
return Math.random() - 0.5;//用Math.random()函数生成0-1之间的随机数,然后将其与0.5进行比较 返回-1或者1
});
console.log(values);
//打乱10000个元素的数组需要的时间
let arr = [];
let mun = 10000;
for (var i = 0; i < mun; i++) {
arr.push(i);
}
let startTime = +new Date();
arr.sort(function () {
return Math.random() - 0.5
});
console.log(+new Date() - startTime); //43
//洗牌算法
let values = [1, 2, 3, 4, , 5, 6, 7, 8,];
for (var i = 0; i < values.length; i++) {
let randomIndex = Math.floor(Math.random() * values.length);
[values[i], arr[randomIndex]] = [values[randomIndex], values[i]];
}
console.log(values); //(9) [1, 2, undefined, 8, 6, 8, 2, 8, 8]
//打乱10000个元素的数组需要的时间
let arr = [];
let mun = 10000;
for (var i = 0; i < mun; i++) {
arr.push(i);
}
let startTime = + new Date();
for (var i = 0; i < arr.length; i++) {
let randomIndex = Math.floor(Math.random() * arr.length);
[arr[i], arr[randomIndex]] = [arr[randomIndex], arr[i]];
}
console.log(+new Date() - startTime); //13
</script>
七、类数组(Array-like-object)
数组的键名key为下标,在内部会转化成字符串,取的时候,不能用点操作符(数值不能做为标识符),只能用方括号。
<script>
let color = ['red', 'blue', 'green', 'yellow'];
color['4'] = 'pink';
console.log(
color, //["red", "blue", "green", "yellow", "pink"]
Object.keys(color), //(5) ["0", "1", "2", "3", "4"]
color[2], //"green"
color['2'], //"green"
// color.2, 报错
)
</script>
在js中有一些对象它拥有length属性,且拥有为非负整数的属性,但是它又不能调用数组的方法,这种对象被称为类数组对象。在es6中它还可以用for..of进行遍历(但也不是全部类数组身上都可以用这个方法,要看类数组身上是否部署了Iterator接口,如类数组的对象就不可以)。
包含以下几种:字符串,arguments, 获取的DOM元素,有length属性所对象
类数组调用数组的方法
1、把类数组转成真正的数组
a. Array对象的slice()方法(推荐使用);b.Array对象的splice(),只能转arguments与object(因为这个的length可修改);c. Array对象的concat()方法 ;d. Array对象的from方法(推荐使用);e. es6扩展运算符,转不了对象
2、用call借用数组身上的方法
1、字符串 字符串是不可变值,把它当作数组看待时,它们是只读的。length属性不能修改(能不能用call去借用其它的方法和length是否能被修改有关)
<script>
let str = 'davina';
str.length = 10;
console.log(str.length, str['0']); //6 "d" length不能被修改
console.log(
[].slice.call(str), //(6) ["d", "a", "v", "i", "n", "a"]
Array.from(str),// es6新增 (6) ["d", "a", "v", "i", "n", "a"]
);
console.log([...str]); // 扩展运算符(6) ["d", "a", "v", "i", "n", "a"]
</script>
2、arguments对象,length属性可以被修改(arguments性能有比较大的问题)
<script>
function fn() {
arguments.length = 9 //length可以被修改
console.log(
arguments.length, // 9
arguments['0'], //"a"
);
console.log([].slice.call(arguments)); //(9) ["a", "b", "c", empty × 6]
console.log([].concat.apply([], arguments));//(9) ["a", "b", "c", undefined, undefined, undefined, undefined, undefined, undefined]
console.log(Array.from(arguments));//(9) ["a", "b", "c", undefined, undefined, undefined, undefined, undefined, undefined]
console.log([...arguments]);//(9) ["a", "b", "c", undefined, undefined, undefined, undefined, undefined, undefined]
console.log([].slice.call(arguments, 0)); //(9) ["a", "b", "c", empty × 6]
}
fn('a', 'b', 'c');
</script>
3、获取的DOM元素,length属性不能被修改
<body>
<li>1</li>
<li>2</li>
<li>3</li>
<script>
var lis = document.querySelectorAll('li');
lis.length = 9;
console.log(lis.length); // 3 length不能被修改
console.log(
[].slice.call(lis), //[li, li, li]
[].concat.apply([], lis), //(3) [li, li, li]
Array.from(lis), //(3) [li, li, li]
[...lis], //(3) [li, li, li]
// [].splice.call(lis,0) 报错
)
</script>
</body
4、有length属性的对象
<script>
//类数组-arguments对象
const city = {
0: '北京',
1: '上海',
2: '广州',
length: 3
};
console.log([].slice.call(city)); //(3) ["北京", "上海", "广州"]
console.log([].concat.apply([], city));//(3) ["北京", "上海", "广州"]
console.log(Array.from(city));//(3) ["北京", "上海", "广州"]
console.log([].splice.call(city, 0));//(3) ["北京", "上海", "广州"]
</script>