数组在JS里是一种神一样的存在,就像你所知道的一样,typeof([1,2])
的结果是"object"
。
说到这里就不得不提到JS里的五种基本数据类型(不考虑function):undefined、null、boolean、number、string。和第六种复杂的数据类型object。object本质是一组由键值构成的对象,我们所熟知的dom节点、数组、正则都隶属于object。
尝试做件事情
在富javascript应用里,js管理数据是相当常见的,一般存储数据时会使用这种方式(以用户列表为例)
var users = [
{'id' : 1001, 'name' : 'Jack', 'age' : 23},
{'id' : 1002, 'name' : 'Joys', 'age' : 18},
{'id' : 1003, 'name' : 'Gate', 'age' : 34}
];
为了是创建的用户信息更符合业务需求,于是乎我们很容易写出下面这个类
//用户类
function USERS(){
this.users = [];
}
users.prototype['add'] = function(item){
//对item执行校验,过滤等操作
//……
this.users.push(item)
};
这种数据模型已经可以很高效的工作了,但是站在使用者的视角来看(见下一个code),操作用户信息时的深度是不是还能再降低一点呢?能不能让实例化后的对象,就是数据本身,而非将数据挂载到users属性下呢?
//实例化一个对象
var myUserList = new USERS();
myUserList.add({'id' : 1001, 'name' : 'Jack', 'age' : 23});
//读取第一条用户信息
console.log(myUserList.users[0]);
伪数组开始啦
终于扯到伪数组啦。要做到实例化后的对象,最直观地以数组的形式呈现,这就涉及到如何把对象伪装成数组。
这里提到的伪数组是指。对象具有length属性,且值为数字(包含字符类型的数字),以及从零开始的有序下标。如下面这个变量arr
,一个赤裸裸的对象,却是一个最标准的伪数组。
var arr = {
'0' : '000',
'1' : '111',
'2' : '222',
'length' : 3,
'name' : 'an object looks like array',
'type' : 'object'
}
$.each(arr,function(key,value){
console.log(key,value)
});
/**
* 打印结果,并不包括length、name、type属性
* 0,000
* 1,111
* 2,222
**/
看到这里应该就豁然开朗了,只要严格遵循length与下标在数组中的逻辑,就可以轻松地构建出一个伪数组,那如何在类的定义时就构建为伪数组,小剧来改写一下USERS
类。
//用户类
function USERS(){
//初始化长度
length = 0;
}
//修改原型对象为数组
users.prototype = [];
users.prototype['add'] = function(item){
//对item执行校验,过滤等操作
//……
//借用数组原型链上的push方法,完成对增加操作
Array.prototype.push.apply(this, item);
};
此时类的使用者看到的就是下面这样纸,区别仅仅在于读取用户信息时不需要再借助子属性。回想下通过选择器实例化的jquery对象,再看下这样一个对象,是不是有种豁然开朗的感觉。
//实例化一个对象
var myUserList = new USERS();
myUserList.add({'id' : 1001, 'name' : 'Jack', 'age' : 23});
//读取第一条用户信息
console.log(myUserList[0]);
可能到了这儿,你感觉利用伪数组降低对象结构深度的意义不大,但对于类的使用者来说,可以用尽可能少的学习成本,来使用尽可能多的经过深度定制的数据类型。
PS:最近又在造轮子,模仿jquery的API在写一个类库lorry.js,其中最核心的构造函数,就涉及到了对伪数组的实现,深深感到了JS的精妙与猥琐,与君共品。