Javascript知识点详解:数组、Array 对象

目录

数组

定义

数组的本质

对象有两种读取成员的方法:

length 属性

in 运算符

for...in 循环和数组的遍历

数组的空位

类似数组的对象

Array

构造函数

静态方法

Array.isArray()

实例方法

valueOf(),toString()

push(),pop()

shift(),unshift()

join()

concat()

reverse()

slice()

splice()

sort()

map()

filter()

indexOf(),lastIndexOf()


数组

定义

数组(array)是按次序排列的一组值。每个值的位置都有编号(从0开始),整个数组用方括号表示。

var arr = ['a', 'b', 'c'];

上面代码中的abc就构成一个数组,两端的方括号是数组的标志。a是0号位置,b是1号位置,c是2号位置。

除了在定义时赋值,数组也可以先定义后赋值。

var arr = [];

arr[0] = 'a';
arr[1] = 'b';
arr[2] = 'c';

任何类型的数据,都可以放入数组,即一个数组中可以同时存在多种数据类型的元素。

var arr = [
    { a: 1 }, //对象
    [1, 2], //数组
    function () { return 100; } //函数
]
console.log(arr[0]);
console.log(arr[1]);
console.log(arr[2]);

Javascript知识点详解:数组、Array 对象_第1张图片

注:如果数组的元素还是数组,就形成了多维数组。

var a = [[1, 2], [3, 4]];
console.log(a[0][1]) // 2
console.log(a[1][1]) // 4

数组的本质

本质上,数组属于一种特殊的对象。

typeof运算符会返回数组的类型是object


上面代码表明,typeof运算符认为数组的类型就是对象。

数组的特殊性体现在在它的键名是按次序排列的一组整数(0,1,2...)。

var arr = ['a', 'b', 'c'];
console.log(Object.keys(arr)) //keys是一个方法,作用是获取所有键名/下标
// ["0", "1", "2"]

Javascript知识点详解:数组、Array 对象_第2张图片

上面代码中,Object.keys方法返回数组的所有键名。可以看到数组的键名就是整数0、1、2。

由于数组成员的键名是固定的(默认总是0、1、2...),因此数组不用为每个元素指定键名,而对象的每个成员都必须指定键名。

JavaScript 语言规定,对象的键名一律为字符串,所以,数组的键名其实也是字符串。之所以可以用数值读取,是因为非字符串的键名会被转为字符串。

var arr = ['a', 'b', 'c'];
arr['0'] // 'a'
arr[0] // 'a'

上面代码分别用数值和字符串作为键名,结果都能读取数组。原因是数值键名被自动转为了字符串。

注意:这点在赋值时也成立。一个值总是先转成字符串,再作为键名进行赋值。

var a = [];
​
a[1.00] = 6;
a[1] // 6

上面代码中,由于1.00转成字符串是1,所以通过数字键1可以读取值。

对象有两种读取成员的方法

  • 点结构(object.key

  • 方括号结构(object[key])。

    但是注意对于数值的键名,不能使用点结构。

var arr = [1, 2, 3];
console.log(arr[1])
console.log(arr.1); // SyntaxError

上面代码中,arr.0的写法不合法,因为单独的数值不能作为标识符(identifier)。

所以,数组成员只能用方括号arr[0]表示(方括号是运算符,可以接受数值)。

length 属性

数组的length属性,返回数组的成员数量。

arr = ['a', 'b', 'c']
console.log(arr.length);

只要是数组,就一定有length属性。

该属性是一个动态的值,等于键名中的最大整数加上1

var arr = ['a', 'b'];
arr.length // 2

arr[2] = 'c';
arr.length // 3

arr[9] = 'd';
arr.length // 10

arr[1000] = 'e';
arr.length // 1001

注意:稀疏数组的长度是最后一个元素的下标 + 1

上面代码表示,数组的数字键不需要连续,length属性的值总是比最大的那个整数键大1。另外,这也表明数组是一种动态的数据结构,可以随时增减数组的成员。

如果人为设置一个小于当前成员个数的值,该数组的成员数量会自动减少到length设置的值。

var arr = ['a', 'b', 'c'];
console.log(arr);
console.log(arr.length);

arr.length = 2;
console.log(arr);

Javascript知识点详解:数组、Array 对象_第3张图片

上面代码表示,当数组的length属性设为2(即最大的整数键只能是1)那么整数键2(值为c)就已经不在数组中了,被自动删除了。

清空数组的一个有效方法,就是将length属性设为0。

var arr = [ 'a', 'b', 'c' ];
arr.length = 0;
arr // []

如果人为设置length大于当前元素个数,则数组的成员数量会增加到这个值,新增的位置都是空位。

var a = ['a'];
a.length = 3;
a[1] // undefined

上面代码表示,当length属性设为大于数组个数时,读取新增的位置都会返回undefined

值得注意的是,由于数组本质上是一种对象,所以可以为数组添加属性,但是这不影响length属性的值。

var a = [];
a['p'] = 'abc';
console.log(a.length) // 0

a[2.1] = 'abc';
console.log(a.length) // 0
a[3] = '3'
console.log(a.length);// 4

Javascript知识点详解:数组、Array 对象_第4张图片

注意:上面代码将数组的键分别设为字符串和小数,结果都不影响length属性。

in 运算符

检查某个键名是否存在的运算符in,适用于对象,也适用于数组。

var arr = ['a', 'b', 'c'];
console.log(2 in arr);
console.log('2' in arr);
console.log(4 in arr);

Javascript知识点详解:数组、Array 对象_第5张图片

上面代码表明,数组存在键名为2的键。由于键名都是字符串,所以数值2会自动转成字符串。

注意,如果数组的某个位置是空位,in运算符返回false

for...in 循环和数组的遍历

for...in循环不仅可以遍历对象,也可以遍历数组,毕竟数组只是一种特殊对象。

var a = [1, 2, 3];
​
for (var i in a) {
  console.log(a[i]);
}
// 1
// 2
// 3

但是,for...in不仅会遍历数组所有的数字键,还会遍历非数字键。

var a = [1, 2, 3];
a.foo = true;
for (var key in a) {
    console.log(key);
}

上面代码在遍历数组时,也遍历到了非整数键foo。所以,不推荐使用for...in遍历数组。

数组的遍历可以考虑使用for循环或while循环。

var a = [1, 2, 3];
​
// for循环
for(var i = 0; i < a.length; i++) {
  console.log(a[i]);
}
​
// while循环
var i = 0;
while (i < a.length) {
  console.log(a[i]);
  i++;
}
​
var l = a.length;
while (l--) {
  console.log(a[l]);
}

数组的forEach方法,也可以用来遍历数组。

var colors = ['red', 'green', 'blue'];
colors.forEach(function (color) {
    //forEach(element,index,array)
    console.log(color);
});

数组的空位

当数组的某个位置是空元素,即两个逗号之间没有任何值,我们称该数组存在空位(hole)。

var a = [1, , 1];
a.length // 3

上面代码表明,数组的空位不影响length属性。虽然这个位置没有值,引擎依然认为这个位置是有效的。

需要注意的是,如果最后一个元素后面有逗号,并不会产生空位。也就是说,有没有这个逗号,结果都是一样的。

数组的空位是可以读取的,返回undefined

使用delete命令删除一个数组成员,会形成空位,并且不会影响length属性。

数组的某个位置是空位,与某个位置是undefined,是不一样的。

如果是空位,使用数组的forEach方法、for...in结构、以及Object.keys方法进行遍历,空位都会被跳过。

var a = [, , ,];

a.forEach(function (x, i) {
    console.log(i + '. ' + x);
})
// 不产生任何输出
for (var i in a) {
    console.log(i);
}
// 不产生任何输出
console.log(Object.keys(a))
// []

如果某个位置是undefined,遍历的时候就不会被跳过。

var a = [undefined, undefined, undefined];
​
a.forEach(function (x, i) {
  console.log(i + '. ' + x);
});

for (var i in a) {
  console.log(i);
}
​
console.log(Object.keys(a))

Javascript知识点详解:数组、Array 对象_第6张图片

这就是说,空位就是数组没有这个元素,所以不会被遍历到,而undefined则表示数组有这个元素,值是undefined,所以遍历不会跳过。

类似数组的对象

如果一个对象的所有键名都是正整数或零,并且有length属性,那么这个对象就很像数组,语法上称为“类似数组的对象”(array-like object)。

var obj = {
    0: 'a',
    1: 'b',
    2: 'c',
    length: 3
};
console.log(obj[0]);
console.log(obj[1]);
console.log(obj.length);
console.log(obj.push('d'));

上面代码中,对象obj就是一个类似数组的对象。但是,“类似数组的对象”并不是数组,因为它们不具备数组特有的方法。对象obj没有数组的push方法,使用该方法就会报错。

“类似数组的对象”的根本特征,就是具有length属性。只要有length属性,就可以认为这个对象类似于数组。

但是有一个问题,这种length属性不是动态值,不会随着成员的变化而变化。

var obj = {
    length: 0
};
obj[3] = '100'
console.log(obj[3]);
console.log(obj.length);

典型的“类似数组的对象”是函数的arguments对象,以及大多数 DOM 元素集,还有字符串。

// arguments对象
function args() { return arguments }
var arrayLike = args('a', 'b');
​
arrayLike[0] // 'a'
arrayLike.length // 2
arrayLike instanceof Array // false
​
// DOM元素集
var elts = document.getElementsByTagName('h3');
elts.length // 3
elts instanceof Array // false
​
// 字符串
'abc'[1] // 'b'
'abc'.length // 3
'abc' instanceof Array // false

上面代码包含三个例子,它们都不是数组(instanceof运算符返回false),但是看上去都非常像数组。

数组的slice方法可以将“类似数组的对象”变成真正的数组。

var arr = Array.prototype.slice.call(arrayLike);

除了转为真正的数组,“类似数组的对象”还有一个办法可以使用数组的方法,就是通过call()把数组的方法放到对象上面。

function print(value, index) {
  console.log(index + ' : ' + value);
}
​
Array.prototype.forEach.call(arrayLike, print);

上面代码中,arrayLike代表一个类似数组的对象,本来是不可以使用数组的forEach()方法的,但是通过call(),可以把forEach()嫁接到arrayLike上面调用。

Array

构造函数

Array是 JavaScript 的原生对象,同时也是一个构造函数,可以用它生成新的数组。

var arr = new Array(2);
//使用new关键字生成的都是构造函数
console.log(arr.length);// 2
console.log(arr); // [ empty x 2 ]

上面代码中,Array()构造函数的参数2,表示生成一个两个成员的数组,每个位置都是空值。

注:如果没有使用new关键字,运行结果也是一样的。

var arr = Array(2);
// 等同于
var arr = new Array(2);

考虑到语义性,以及与其他构造函数用法保持一致,建议总是加上new

Array()构造函数有一个很大的缺陷,不同的参数个数会导致不一致的行为。

var a = new Array() // []
console.log(a);
// 单个正整数参数,表示返回的新数组的长度
var b = new Array(1) // [ empty ]
var bb = new Array(2) // [ empty x 2 ]

console.log(b);

// 单个非数值(比如字符串、布尔值、对象等)作为参数,
// 则该参数是返回的新数组的成员
var d = new Array('abc') // ['abc']
var dd = new Array([1]) // [Array[1]]

console.log(d);
console.log(dd);
// 多参数时,所有参数都是返回的新数组的成员
var e = new Array(1, 2) // [1, 2]
var ee = new Array('a', 'b', 'c') // ['a', 'b', 'c']
console.log(e);
console.log(ee);

// 非正整数的数值作为参数,会报错
var c = new Array(3.2) // RangeError: Invalid array length
var cc = new Array(-3) // RangeError: Invalid array length
console.log(c);
console.log(cc);

Javascript知识点详解:数组、Array 对象_第7张图片

可以看到,Array()作为构造函数,行为很不一致。因此,不建议使用它生成新数组,直接使用数组字面量是更好的做法。

// bad
var arr = new Array(1, 2);
​
// good
var arr = [1, 2];

注意,如果参数是一个正整数,返回数组的成员都是空位。虽然读取的时候返回undefined,但实际上该位置没有任何值。虽然这时可以读取到length属性,但是取不到键名。

var a = new Array(3);
var b = [undefined, undefined, undefined];
​
a.length // 3
b.length // 3
​
a[0] // undefined
b[0] // undefined
​
0 in a // false
0 in b // true

上面代码中,aArray()生成的一个长度为3的空数组,b是一个三个成员都是undefined的数组,这两个数组是不一样的。

读取键值的时候,ab都返回undefined,但是a的键名(成员的序号)都是空的,b的键名是有值的。

静态方法

Array.isArray()

Array.isArray方法返回一个布尔值,表示参数是否为数组。它可以弥补typeof运算符的不足。

var arr = [1, 2, 3];
console.log(typeof arr) // "object"
console.log(Array.isArray(arr)) // true

 

上面代码中,typeof运算符只能显示数组的类型是Object,而Array.isArray方法可以识别数组。

实例方法

最基本的原型继承关系:

null->Object->valueOf()->...

null->Object->toString()->...

valueOf(),toString()

valueOf方法是一个所有对象都拥有的方法,表示对该对象求值。不同对象的valueOf方法不尽一致,数组的valueOf方法返回数组本身。

var arr = [1, 2, 3];
arr.valueOf() // [1, 2, 3]

toString方法也是对象的通用方法,数组的toString方法返回数组的字符串形式。

var arr = [1, 2, 3];
arr.toString() // "1,2,3"
​
var arr = [1, 2, 3, [4, 5, 6]];
arr.toString() // "1,2,3,4,5,6"
push(),pop()

push方法用于在数组的末端添加一个或多个元素,并返回添加新元素后的数组长度。

注意:该方法会改变原数组。

var arr = [];
console.log(arr);
arr.push(1) // 1
console.log(arr);
arr.push('a') // 2
console.log(arr);
arr.push(true, {}) // 4
console.log(arr); // [1, 'a', true, {}]

上面代码使用push方法,往数组中添加了四个成员。

pop方法用于删除数组的最后一个元素,并返回该元素

注意:该方法会改变原数组。

var arr = ['a', 'b', 'c'];

arr.pop() // 'c'
console.log(arr); // ['a', 'b']

Javascript知识点详解:数组、Array 对象_第8张图片 

对空数组使用pop方法,不会报错,而是返回undefined

[].pop() // undefined

pushpop结合使用,就构成了“后进先出”的栈结构(stack)。

shift(),unshift()

shift()方法用于删除数组的第一个元素,并返回该元素

注意:该方法会改变原数组。

var a = ['a', 'b', 'c'];
​
a.shift() // 'a'
a // ['b', 'c']

上面代码中,使用shift()方法以后,原数组就变了。

push()shift()结合使用,就构成了“先进先出”的队列结构(queue)。

unshift()方法用于在数组的第一个位置添加元素,并返回添加新元素后的数组长度。

注意:该方法会改变原数组。

var a = ['a', 'b', 'c'];
​
a.unshift('x'); // 4
a // ['x', 'a', 'b', 'c']

unshift()方法可以接受多个参数,这些参数都会添加到目标数组头部。

var arr = [ 'c', 'd' ];
arr.unshift('a', 'b') // 4
arr // [ 'a', 'b', 'c', 'd' ]
join()

join()方法以指定参数作为分隔符,将所有数组成员连接为一个字符串返回。如果不提供参数,默认用逗号分隔。

var a = [1, 2, 3, 4];

// '1 2 3 4'
console.log(a.join(' '));
// "1 | 2 | 3 | 4"
console.log(a.join(' | '));
// "1,2,3,4"
console.log(a.join());

Javascript知识点详解:数组、Array 对象_第9张图片 

如果数组成员是undefinednull或空位,会被转成空字符串。

[undefined, null].join('#')
// '#'
​
['a',, 'b'].join('-')
// 'a--b'

通过call方法,这个方法也可以用于字符串或类似数组的对象。

console.log(Array.prototype.join.call('hello', '-'))
// "h-e-l-l-o"

var obj = { 0: 'a', 1: 'b', length: 2 };//这就是前面讲到的类数组的对象
console.log(Array.prototype.join.call(obj, '-')) //所以没有数组的方法,只能使用call绑定
// 'a-b'

concat()

concat方法用于多个数组的合并。

它将新数组的成员,添加到原数组成员的后部,然后返回一个新数组,原数组不变。

['hello'].concat(['world'])
// ["hello", "world"]
​
['hello'].concat(['world'], ['!'])
// ["hello", "world", "!"]
​
[].concat({a: 1}, {b: 2})
// [{ a: 1 }, { b: 2 }]
​
[2].concat({a: 1})
// [2, {a: 1}]

除了数组作为参数,concat也接受其他类型的值作为参数,添加到目标数组尾部。

[1, 2, 3].concat(4, 5, 6)
// [1, 2, 3, 4, 5, 6]
reverse()

reverse方法用于颠倒排列数组元素,返回改变后的数组。注意,该方法将改变原数组。

var a = ['a', 'b', 'c', 'd', 'e', 'f'];
console.log(a);
console.log(a.reverse()) 

 

slice()

slice()方法用于提取目标数组的一部分,返回一个新数组,原数组不变。

格式为:arr.slice(start, end);

它的第一个参数为起始位置(从0开始,会包括在返回的新数组之中),第二个参数为终止位置(但该位置的元素本身不包括在内)。

如果省略第二个参数,则一直返回到原数组的最后一个成员。

var a = ['a', 'b', 'c'];
​
a.slice(0) // ["a", "b", "c"]
a.slice(1) // ["b", "c"]
a.slice(1, 2) // ["b"]
a.slice(2, 6) // ["c"]
a.slice() // ["a", "b", "c"]

上面代码中,最后一个例子slice()没有参数,实际上等于返回一个原数组的拷贝。

如果第一个参数大于等于数组长度,或者第二个参数小于第一个参数,则返回空数组。

var a = ['a', 'b', 'c'];
a.slice(4) // []
a.slice(2, 1) // []

slice()方法的一个重要应用,是将类似数组的对象转为真正的数组。

Array.prototype.slice.call({ 0: 'a', 1: 'b', length: 2 })
// ['a', 'b']
​
Array.prototype.slice.call(document.querySelectorAll("div"));
Array.prototype.slice.call(arguments);

上面代码的参数都不是数组,但是通过call方法,在它们上面调用slice()方法,就可以把它们转为真正的数组。

splice()

splice()方法用于删除原数组的一部分成员,并可以在删除的位置添加新的数组成员,返回值是被删除的元素

注意,该方法会改变原数组。

格式为:arr.splice(start, count, addElement1, addElement2, ...);

splice的第一个参数是删除的起始位置(从0开始),第二个参数是被删除的元素个数。如果后面还有更多的参数,则表示这些就是要被插入数组的新元素。

var a = ['a', 'b', 'c', 'd', 'e', 'f'];
a.splice(4, 2) // ["e", "f"]
a // ["a", "b", "c", "d"]

上面代码从原数组4号位置,删除了两个数组成员。

var a = ['a', 'b', 'c', 'd', 'e', 'f'];
a.splice(4, 2, 1, 2) // ["e", "f"]
a // ["a", "b", "c", "d", 1, 2]

上面代码除了删除成员,还插入了两个新成员。

起始位置如果是负数,就表示从倒数位置开始删除。

var a = ['a', 'b', 'c', 'd', 'e', 'f'];
a.splice(-4, 2) // ["c", "d"]

上面代码表示,从倒数第四个位置c开始删除两个成员。

如果只是单纯地插入元素,splice方法的第二个参数可以设为0

var a = [1, 1, 1];
​
a.splice(1, 0, 2) // []
a // [1, 2, 1, 1]

如果只提供第一个参数,等同于将原数组在指定位置拆分成两个数组。

var a = [1, 2, 3, 4];
a.splice(2) // [3, 4]
a // [1, 2]
sort()

sort方法对数组成员进行排序,默认是按照字典顺序排序。排序后,原数组将被改变。

console.log(['d', 'c', 'b', 'a'].sort());
console.log([4, 3, 2, 1].sort());
console.log([11, 101].sort());
console.log([10111, 1101, 111].sort());

Javascript知识点详解:数组、Array 对象_第10张图片 

上面代码的最后两个例子,需要特殊注意。sort()方法不是按照大小排序,而是按照字典顺序。也就是说,数值会被先转成字符串,再按照字典顺序进行比较,所以101排在11的前面。

如果想让sort方法按照自定义方式排序,可以传入一个函数作为参数。

[10111, 1101, 111].sort(function (a, b) {
  return a - b; //这里传入了一个函数,表示使用函数内规定的方案来进行排序
})
// [111, 1101, 10111]

上面代码中,sort的参数函数本身接受两个参数,表示进行比较的两个数组成员。如果该函数的返回值大于0,表示第一个成员排在第二个成员后面;其他情况下,都是第一个元素排在第二个元素前面。

map()

map()方法将数组的所有成员依次传入参数函数,然后把每一次的执行结果组成一个新数组返回。

var numbers = [1, 2, 3];
aa = numbers.map(function (n) {
    return n + 1;
});
console.log(aa);
// [2, 3, 4]

console.log(numbers);
// [1, 2, 3]

Javascript知识点详解:数组、Array 对象_第11张图片 

上面代码中,numbers数组的所有成员依次执行参数函数,运行结果组成一个新数组返回,原数组没有变化。

map()方法接受一个函数作为参数。

该函数调用时,map()方法向它传入三个参数:当前成员、当前位置和数组本身。

[1, 2, 3].map(function(elem, index, arr) {
  return elem * index;
    0 x 0
    2 x 1
    3 X 2
});
// [0, 2, 6]

上面代码中,map()方法的回调函数有三个参数,elem为当前成员的值,index为当前成员的位置,arr为原数组([1, 2, 3])。

如果数组有空位,map()方法的回调函数在这个位置不会执行,会跳过数组的空位。

var f = function (n) { return 'a' };

[1, undefined, 2].map(f) // ["a", "a", "a"]
[1, null, 2].map(f) // ["a", "a", "a"]
[1, , 2].map(f) // ["a", , "a"]

上面代码中,map()方法不会跳过undefinednull,但是会跳过空位。

filter()

filter()方法用于过滤数组成员,满足条件的成员组成一个新数组返回。

它的参数是一个函数,所有数组成员依次执行该函数,返回结果为true的成员组成一个新数组返回。该方法不会改变原数组。

[1, 2, 3, 4, 5].filter(function (elem) {
  return (elem > 3);
})
// [4, 5]

上面代码将大于3的数组成员,作为一个新数组返回。

filter()方法的参数函数可以接受三个参数:当前成员,当前位置和整个数组。

indexOf(),lastIndexOf()

indexOf方法返回给定元素在数组中第一次出现的位置,如果没有出现则返回-1

var a = ['a', 'b', 'c'];
​
a.indexOf('b') // 1
a.indexOf('y') // -1

indexOf方法还可以接受第二个参数,表示搜索的开始位置。

['a', 'b', 'c'].indexOf('a', 1) // -1

上面代码从1号位置开始搜索字符a,结果为-1,表示没有搜索到。

lastIndexOf方法返回给定元素在数组中最后一次出现的位置,如果没有出现则返回-1

var a = [2, 5, 9, 2];
a.lastIndexOf(2) // 3
a.lastIndexOf(7) // -1

注意,这两个方法不能用来搜索NaN的位置,即它们无法确定数组成员是否包含NaN

[NaN].indexOf(NaN) // -1
[NaN].lastIndexOf(NaN) // -1

这是因为这两个方法内部,使用严格相等运算符(===)进行比较,而NaN是唯一一个不等于自身的值。

你可能感兴趣的:(安全,前端,JavaScript,学习,javascript,开发语言,前端,ecmascript,web安全)