伪数组和数组
在JavaScript中,除了5种原始数据类型之外,其他所有的都是对象,包括函数(Function)。
对象与数组的关系
在说区别之前,需要先提到另外一个知识,就是 JavaScript 的原型继承。
所有 JavaScript 的内置构造函数都是继承自 Object.prototype
。
在这个前提下,可以理解为使用 new Array()
或 []
创建出来的数组对象,都会拥有 Object.prototype
的属性值。
var obj = {};// 拥有 Object.prototype 的属性值
var arr = [];
//使用数组直接量创建的数组,由于 Array.prototype 的属性继承自 Object.prototype,
//那么,它将同时拥有 Array.prototype 和 Object.prototype 的属性值
- 可以得到对象和数组的第一个区别:对象没有数组 Array.prototype 的属性值。
什么是数组 ?
数组具有一个最基本特征:索引,这是对象所没有的,下面来看一段代码:
var obj = {};
var arr = [];
obj[2] = 'a';
arr[2] = 'a';
console.log(obj[2]); // => a
console.log(arr[2]); // => a
console.log(obj.length); // => undefined
console.log(arr.length); // => 3
- obj[2]输出'a',是因为对象就是普通的键值对存取数据
- 而arr[2]输出'a' 则不同,数组是通过索引来存取数据,arr[2]之所以输出'a',是因为数组arr索引2的位置已经存储了数据
- obj.length并不具有数组的特性,并且obj没有保存属性length,那么自然就会输出undefined
- 而对于数组来说,length是数组的一个内置属性,数组会根据索引长度来更改length的值
- 为什么arr.length输出3,而不是1
+ 在给数组添加元素时,并没有按照连续的索引添加,所以导致数组的索引不连续,那么就导致索引长度大于元素个数
什么是伪数组 ?
- 拥有 length 属性,其它属性(索引)为非负整数(对象中的索引会被当做字符串来处理,这里你可以当做是个非负整数串来理解)
- 不具有数组所具有的方法
伪数组,就是像数组一样有 length
属性,也有 0、1、2、3
等属性的对象,看起来就像数组一样,但不是数组,比如:
var fakeArray = {
"0": "first",
"1": "second",
"2": "third",
length: 3
};
for (var i = 0; i < fakeArray.length; i++) {
console.log(fakeArray[i]);
}
Array.prototype.join.call(fakeArray,'+'); // 谁调用join方法,this就指向谁,反过来说this指向谁,就表示谁调用了该方法,现在将this
// 指向fakeArray,就表示fakeArray调用了该方法。 实现了间接调用数组的方法。等会查一下,将伪数组转换为真数组
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/slice
常见的伪数组有:
- 函数内部的 `arguments`
- DOM 对象列表(比如通过 `document.getElementsByTags` 得到的列表)
- jQuery 对象(比如 `$("div")` )
伪数组是一个 Object,而真实的数组是一个 Array。
伪数组存在的意义,是可以让普通的对象也能正常使用数组的很多方法,比如:
var arr = Array.prototype.slice.call(arguments);
Array.prototype.forEach.call(arguments, function(v) {
// 循环arguments对象
});
// push
// some
// every
// filter
// map
// ...
以上在借用数组的原型方法的时候都可以通过数组直接量(字面量)来简化使用:
var obj = {
0: 'a',
1: 'b',
2: 'c',
length: 3
}
;[].push.call(obj, 'd')
console.log([].slice.call(obj))
;[].forEach.call(obj, function (num, index) {
console.log(num)
})
如何将伪数组转换为真数组 ?
- slice 方法可以用来将一个类数组(Array-like)对象/集合转换成一个新数组。你只需将该方法绑定到这个对象上。 一个函数中的 arguments 就是一个类数组对象的例子。
function list() {
return Array.prototype.slice.call(arguments);
}
var list1 = list(1, 2, 3); // [1, 2, 3]
- 除了使用 Array.prototype.slice.call(arguments),你也可以简单的使用
[].slice.call(arguments)
来代替。 - 另外,你可以使用
bind
来简化该过程。
var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.call.bind(unboundSlice);
function list() {
return slice(arguments);
}
var list1 = list(1, 2, 3); // [1, 2, 3]
- Array.from() 方法从一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。
function f() {
return Array.from(arguments);
}
f(1, 2, 3);
// [ 1, 2, 3 ]
- ES6的解构赋值
function f(){
console.log(arguments);
let newArr = [...arguments];
console.log(newArr);
}
args(1, 2, 3, 4, 5, 6);
- 利用普通的 for 循环, 把类数组的每一项都push到真正的数组arr中
var arr = []
function f() {
for (var i = 0; i < arguments.length; i++) {
arr.push(arguments[i])
}
}
小结
- 对象没有数组 Array.prototype 的属性值,类型是 Object ,而数组类型是 Array
- 数组是基于索引的实现, length 会自动更新,而对象是键值对
- 使用对象可以创建伪数组(键为数字,还要有length属性),伪数组可以正常使用数组的大部分方法