一、数组、对象、类数组对象数据结构的异同
1、数组
一组有序的数据集合,其索引为从0开始且自然增长的整数,其元素值可以是任何js数据!并且包含一个名为length的属性,该属性表示数组元素的个数!
var array = [1,2,3];
console.log(array[0])//1
console.log(array[1])//2
console.log(array[2])//3
console.log(array['length'])//3
ps:数组元素的索引和length属性是在数组定义时根据数组元素语言自动就帮你定义好了!
2、对象
一组无序的由键->值组成的数据集合,其元素的键名和值都可以自定义!
var object = {'a': 1, 'b': 2}
console.log(object['a'])//1
console.log(object['b'])//2
console.log(object['length'])//undefined
ps:对象自身所拥有的(不包括继承来的)任何属性都是有用户自定义的!
完全可以使用对象来模拟数组,只要我们定义的对象的每个元素的键名都使用数字并且让其保持递增,且动态的定义一个length属性来表示元素个数。
3、类数组对象
(1)什么是类数组对象
- 拥有length属性,其它属性(索引)为非负整数(对象中的索引会被当做字符串来处理,这里你可以当做是个非负整数串来理解)
- 不具有数组所具有的方法
//类数组示例
var a = {'1':'gg','2':'love','4':'meimei',length:5};
Array.prototype.join.call(a,'+');//'+gg+love++meimei'
//非类数组示例
var c = {'1':2}; //没有length属性就不是类数组
ps:
- 在浏览器环境中,document.getElementsByTagName()语句返回的就是一个类数组对象。
- 在function调用中,function代码内的arguments变量(保存传入的参数)也是一个类数组对象。
- 在ECMAScript 5标准中,字符串string就是一个只读的类数组对象。
(2)判断一个对象是不是类数组
function isArrayLike(o) {
if (o && // o is not null, undefined, etc.
typeof o === 'object' && // o is an object
isFinite(o.length) && // o.length is a finite number
o.length >= 0 && // o.length is non-negative
o.length===Math.floor(o.length) && // o.length is an integer
o.length < 4294967296) // o.length < 2^32
return true; // Then o is array-like
else
return false; // Otherwise it is not
}
(3)类数组转换成数组之后进行操作有什么优势
由于类数组不具有数组所具有的操作数组的方法,讲类数组转换为数组之后就能调用如shift,unshift,splice,slice,concat,reverse,sort等这些强大的方法,方便快捷。
(4)类数组转换成数组的方法
var arrayLike = {0: 'name', 1: 'age', 2: 'sex', length: 3 }
// 1. slice
Array.prototype.slice.call(arrayLike); // ["name", "age", "sex"]
// 2. splice
Array.prototype.splice.call(arrayLike, 0); // ["name", "age", "sex"]
// 3. ES6 Array.from
Array.from(arrayLike); // ["name", "age", "sex"]
// 4. apply
Array.prototype.concat.apply([], arrayLike)
ps:
Array.prototype.slice.call(arguments,1)
这句话的意思就是说把调用方法的参数截取出来。
function test(a,b,c,d)
{
var arg = Array.prototype.slice.call(arguments,1);
alert(arg);
}
test("a","b","c","d"); //b,c,d
所以其后面可以直接调用数组具有的方法,例如
Array.prototype.slice.call(arrayLike).forEach(function(element,index){ //可以随意操作每一个element了 })
- Array.prototype.slice表示数组的原型中的slice方法。注意这个slice方法返回的是一个Array类型的对象。
//slice的内部实现
Array.prototype.slice = function(start,end){
var result = new Array();
start = start || 0;
end = end || this.length; //this指向调用的对象,当用了call后,能够改变this的指向,也就是指向传进来的对象,这是关键
for(var i = start; i < end; i++){
result.push(this[i]);
}
return result;
}
-
能调用call的只有方法,所以不能用[].call这种形式,得用[].slice。而call的第一个参数表示真正调用slice的环境变为了arrayLike对象。所以就好像arrayLike也具有了数组的方法。
补充:将函数的实际参数转换成数组的方法:
(1)var args = Array.prototype.slice.call(arguments);
(2)var args = [].slice.call(arguments, 0);
(3)var args = [];
for (var i = 1; i < arguments.length; i++) {
args.push(arguments[i]);
}
转换成数组的通用函数:
var toArray = function(s){
try{
return Array.prototype.slice.call(s);
} catch(e){
var arr = [];
for(var i = 0,len = s.length; i < len; i++){
//arr.push(s[i]);
arr[i] = s[i]; //据说这样比push快
}
return arr;
}
(5)调用数组方法
如果类数组就是任性的想用数组的方法怎么办呢?
既然无法直接调用,我们可以用 Function.call 间接调用:
var arrayLike = {0: 'name', 1: 'age', 2: 'sex', length: 3 }
Array.prototype.join.call(arrayLike, '&'); // name&age&sex
Array.prototype.slice.call(arrayLike, 0); // ["name", "age", "sex"]
// slice可以做到类数组转数组
Array.prototype.map.call(arrayLike, function(item){
return item.toUpperCase();
});
// ["NAME", "AGE", "SEX"]
(6) 将数组转换为参数列表(类数组)
调用apply方法的时候,第一个参数是对象(this), 第二个参数是一个数组集合, 这里就说明apply的一个巧妙用法,可以将一个数组默认的转换为一个参数列表([param1,param2,param3] 转换为 (param1,param2,param3), 这个如果让我们用程序来实现将数组的每一个项,来转换为参数的列表,可 能都得费一会功夫,借助apply的这点特性,所以就有了以下高效率的方法。