使用构造函数Array
// 创建一个空数组
var a1 = new Array();
// 创建一个数组长度为size的数组
var a2 = new Array(size);
// 创建一个数组元素为参数列表的数组
var a3 = new Array(ele1,ele2,···,eleN);
如果把构造函数当作函数使用,不加new运算符,创建的数组也是一样的。
直接赋值
var a = [1,2,'3',[4,true]];
创建数组的规则重叠时的取舍
下列代码创建的数组都是怎样的?
var arr1 = new Array(3);
// 答案A [null, null, null]
// 答案B [undefined, undefined, undefined]
// 答案C [3]
当构造函数中的参数是一个数字时,似乎既满足Array(size)也满足Array(eleList),这时js会选择按照Array(size)的规则去创建数组,而在没有指定数组元素值的情况下数组元素应为undefined,所以最终的正确答案是B。
直接比较数组是不靠谱的
下列每行比较数组的代码的返回结果是什么呢?
var arr_newArr = new Array(1,2,3);
var arr_Arr = Array(1,2,3);
var arr_literal = [1,2,3];
console.log(arr_newArr == arr_Arr);
console.log(arr_newArr == arr_literal);
console.log(arr_Arr == arr_literal);
console.log([1,2,3] == [1,2,3]);
答案是全部都为false。所以不要直接拿数组来做比较的结果作为某些判断的依据。
Function和Object的属性和方法
因为Array继承自Function和Object,所以自然也有它们的属性和方法。
Array.prototype
所有的数组实例都继承自Array.prototype,该属性指向的是Array的原型对象,该原型对象是一个数组。
Array.prototype // 控制台输出为[]
Array.length
该属性表示的是数组实例的长度,因为length属性是挂在Array上的,所以length属性是每个实例所独有的,并不会共享。可以通过减小length的属性值来截断一个数组,也可以通过增大length的属性值来拓展数组长度,但是拓展出的多余的数组元素默认为undefined。
var arr = [1, 2, 3];
arr.length = 2;
console.log(arr); // [1, 2]
arr.length = 3;
console.log(arr); // [1, 2, undefined]
值得注意的是,如上面提到的,Array.prototype也是一个数组,所以在Array.prototype上也有一个length属性,Array.prototype.length的值为0。
Array.isArray()
该方法用来判断一个值是否为数组。如果是,返回true,反之返回false。
Array.isArray([]); // true
Array.isArray(); // false
Array.isArray({}); // false
Array.from()
该方法可以把一个类数组对象或者可迭代对象(如Set或Map)转换成真正的数组。它有三个参数。
arrayLike
该参数为想要转换成真正数组的对象
mapFn
该参数为一个处理的函数,是一个可选的参数,如果制定了该参数,则转换的对象在转换成数组后其数组元素会经过该函数处理后再返回。
thisArg
该参数也是一个可选参数,是mapFn函数中的this的指向。
Array.of()
该方法可以根据传入的参数列表来生成数组。
var arr = Array.of(1,2,3,[true]);
console.log(arr); // [1, 2, 3, [true]]
var arr1 = new Array(3); // [undefined, undefined, undefined]
var arr2 = Array.of(3); // [3]
可以看到,Array.of()在一定程度上弥补了Array构造方法的一些缺陷。
Array.observe()
该方法用来异步监视数组的变化,有两个参数,第一个参数为需要监视的数组实例,第二个参数为数组发生变化时执行的回调函数。该方法仍处于ES7的规范提案中。不建议现在使用。
Array.unobserve()
该方法用来解除对数组的监视。也有两个参数,第一个参数是需要解除监视的数组实例,第二个参数为绑定监视器时的回调函数。该方法和Array.unobserve()一样仍处于ES7的规范提案阶段。不建议现在使用。
Array是JavaScript中的一种引用类型对象,所以追溯到根源Array也是继承自Object。
var arr = [1, 2, 3];
var obj = {pro: 1};
instanceof运算符
instanceof 运算符可以用来判断某个构造函数的 prototype 属性是否存在另外一个要检测对象的原型链上。借由这样的特性,我们可以判断一个对象是否是Array类型。
console.log(arr instanceof Array); // true
console.log(obj instanceof Array); // false
constructor属性
利用实例对象的原型的constructor属性来判断该对象是否由Array构造函数构造而来,同样可以达到判断类型的目的。
console.log(arr.constructor == Array); // true
console.log(obj.constructor == Array); // false
虽然上面两种方法在大多数场合用来判断实例是否为数组类型已经够用了,但是它们还是不够靠谱。因为跨页面的环境是不共享原型链的,如果在一个父页面的环境里用instanceof或者constructor去检测一个子页面的实例,得到的结果是不准确的。
var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
xArray = window.frames[window.frames.length-1].Array;
var arr = new xArray(1,2,3); // [1,2,3]
arr instanceof Array; // false
arr instanceof xArray; // true
arr.constructor === Array; // false
arr.constructor === xArray; // true
如上所示,Array是处于父页面的环境中的,xArray才是子页面环境中的Array。
Object.prototype.toString方法
Object.prototype上的toString方法,会去获取实例对象的类型名称,并和"[Object "、"]"拼接成形如"[Object className]"的字符串并返回。我们可以通过获取该字符串中的子串className来获知实例对象的类型。
// 输出为 "[Object Array]"
console.log(Object.prototype.toString.call(arr));
Array.isArray()
正如该文档前面所介绍的,Array.isArray()可以用来判断某值是否为数组。
for 循环
for循环一定的次数,在Array中一般是以数组的长度值作为边界条件来做循环。
for/in 循环
for/in 循环根据对象的属性来进行循环,在Array中把每个元素的索引看做属性,数组元素作为属性值。要注意的是,在Array的for/in循环中,length并不会被循环到,for/in循环只能枚举可枚举的属性(如用户定义的属性、可继承的用户定义的属性等)。
var arr = new Array('a','b','c');
var n = 0;
for(var key in arr) {
console.log(key, arr[key]);
n++;
}
console.log(n);
最终的结果如下。
// console.log(key, arr[key]);
0 a
1 b
2 c
// console.log(n);
3
forEach 方法
forEach是Array的一个方法,它可以传入两个参数,一个是循环中针对每个数组元素项的执行函数,第二个参数是执行函数中this所指的对象。
在执行函数中,会传入三个参数,分别是当前遍历到的元素项的值、当前元素的索引和数组本身。
var arr = ['a', 'b', 'c'];
arr.forEach(function(d,i,arr){
console.log(d, i, arr, this.a);
}, {
a: 1000,
b: 2000
})
最终输出的结果如下。
a 0 ["a", "b", "c"] 1000
b 1 ["a", "b", "c"] 1000
c 2 ["a", "b", "c"] 1000
但是在forEach中不能用break语句跳出循环。
for/of 循环
该循环方式是ES6新提供的,它适用于数组、类数组对象、字符串等。for/of循环读取键值,如果想获取键值对应的索引,必须间接的使用其他方法来获取。
var arr = ['a', 'b', 'c'];
for(var value of arr) {
console.log(value); // 输出的是键值
}
for循环与for/in循环的区别
for/in循环中把数组元素的下标看成数组的属性,此时它是string类型的,而for循环中的索引是number类型的。如果此时对下标进行相加,在for/in循环中会被看成字符串的拼接,而在for循环中是数值的相加。
当我们对array进行了属性上的扩展的时候(比如给它添加了属性或者方法,虽然这样很奇葩),for/in循环就会有一定的问题。
var arr = new Array('a', 'b', 'c');
arr.otherProperty = 'pppp';
for(var key in arr) {
console.log(key, arr[key]);
}
上述代码的输出会是这样的。
0 a
1 b
2 c
otherProperty pppp
看,for/in循环遍历到了otherProperty,这跟我们对Array中的循环的预期认知是不一样的。所以需要慎用for/in循环。
顾名思义,类数组对象就是类似数组的对象。首先,它是个对象;其次,它只是类似数组,所以并没有所有数组的特性。
类数组对象需要满足两个条件:
它不具有数组的方法。并且添加元素并不会自动修改length属性的值。
var arr = ['a', 'b', 'c'];
var obj = {
0: 'a',
1: 'b',
2: 'c',
length: 3
};
for(var i=0;i<arr.length;i++) {
console.log(i, arr[i]);
}
for(var j=0;j<obj.length;j++) {
console.log(j, obj[j]);
}
最终的输出结果是一样的,这就是类数组对象跟数组类似的地方。
但是,如果此时给obj添加元素,length属性并不会自动的增加,并且使用数组的方法会报错。
arr[3] = 'd';
console.log(arr.length); // 4
obj[3] = 'd';
console.log(obj.length); // 3
obj.push('e'); // 报错
当然,如果要让类数组对象使用数组的方法,也并不是没有办法,可以通过call或者apply方法间接达到这样的目的。
Array.prototype.slice.call(obj, eleList);
但是要记住类数组对象它始终不是正宗的数组,所以有一些数组方法对它并不适用,如push等。
数组的空位是指,在数组中存在这样一个位置,但是该位置上并没有任何值(这里的任何值包括undefined,位置上的值为undefined说明该位置上依然是有值的,并不能算空位)。
在ES5中,对空位的处理并不是非常一致,所以应该避免使用空位。而在ES6中,统一将空位处理成undefined。
需要注意的是,有一种情况并不会产生空位。如下所示,最后的那个逗号会被忽略,并不会产生多余的空位。
var a = [1, 2,];
console.log(a); // [1, 2]
数组推导提供一种简洁的写法,使能够通过现有的数组直接生成新的数组。目前被推迟到ES7中。
var arr = [1, 2, 3];
var newArr = [for (i of arr) i * 2]; // [2, 4, 6]