其实网上关于Array对象的文章有很多了,只是在这里自己整理一下,当作一个知识的梳理吧。
这里有一张关于数组的思维导图,感觉比较详细(来源http://www.cnblogs.com/coco1s/p/3953653.html)。
数组简单来说就是数据的有序列表。《JavaScript语言精粹》这本书上是这么来描述的:
数组是一段线性分配的内存,它通过整数计算偏移并访问其中的元素。数组是一种性能出色的数据结构。不幸的是,JavaScript没有像此类数组一样的数据结构。
作为替代,JavaScript提供了一种拥有一些类数组(array-like)特性的对象。它把数组的下标转变成字符串,用其作为属性。它明显地比一个真正的数组慢,但它使用起来更方便。
一.数组的创建
js创建数组的基本方式有两种。一种是使用Array构造函数,另一种是使用数组字面量表示法。
/*使用Array构造函数*/ var colors = new Array(); //预先知道数组要保存的项目数量,下面代码创建length为20的数组 var colors = new Array(20); //也可以向Array构造函数传递数组中应该包含的项 var colors = new Array("red", "blue", "green"); //在使用Array构造函数时也可以省略new操作符 var colors = Array("red", "blue", "green"); var colors = Array(3); /*使用数组字面量表示法*/ var colors = ["red", "blue", "green"]; var names = [];
关于数组的创建有几个要注意的地方:
1.数组的下标必须是大于等于0并小于2的32次方-1的整数;
2.在使用数组字面量表示法时,是不会调用Array构造函数的(Firefox3及更早版本除外);
3.js允许数组包含任意混合类型的值。
二.数组的属性
constructor | 指定创建对象原型的函数。 |
index | 如果数组是通过正则表达式匹配创建的,匹配的子字符串的第一个字符在原始字符串中的位置(从0开始的索引,只读)。 |
input | 如果数组是通过正则表达式匹配创建的,返回原始的字符串。 |
length | 返回数组中元素个数 |
prototype | 允许为所有对象附加属性 |
光看这个表格可能有点晦涩,下面用几段代码来举例说明下这几个属性。
1.constructor
指定创建对象原型的函数。该属性值是一个指向函数自身的引用,而不是一个包含函数名称的字符串。而constructor是构造函数的prototype上的属性,具体可以看下面的代码
(注意constructor是可以被覆盖的,使用要小心):
var a = new Array("a","b"); var b = ["a","b"]; a.__proto__ === Array.prototype; //true a.constructor === a.__proto__.constructor; //true a.constructor === Array.prototype.constructor; //true a.constructor === Array; //true b.__proto__ === Array.prototype; //true b.constructor === b.__proto__.constructor; //true b.constructor === Array.prototype.constructor; //true b.constructor === Array; //true
2.index
如果数组是通过正则表达式匹配创建的,匹配的子字符串的第一个字符在原始字符串中的位置(从0开始的索引,只读)。不多说。上代码:
myRe = /d(b+)(d)/i; //exec()返回一个数组,其中存放匹配的结果。如果未找到匹配,则返回值为 null。此数组的第 0 个元素是与正则表达式相匹配的文本,第 1 个元素是与 RegExpObject 的第 1 个子表达式相匹配的文本(如果有的话),第 2 个元素是与 RegExpObject 的第 2 个子表达式相匹配的文本(如果有的话),以此类推。 myArray = myRe.exec("cdbBdbsbz"); //["dbBd", "b5", "d"] myArray.index; //1因为dbBd是从字符串索引的1开始的
3.input
如果数组是通过正则表达式匹配创建的,返回原始的字符串。是一个只读的属性。
myRe = /d(b+)(d)/i; myArray = myRe.exec("cdbBdbsbz"); //["dbBd", "b5", "d"] myArray.input; //“cdbBdbsbz”
4.length
返回数组中元素的个数。
var colors = ["red", "blue", "green"]; var names = []; colors.length; //3 names.length;//0
数组的length属性不是只读的,因此,通过设置这个属性,可以从数组的末尾移除项或向数组中添加新项。length设小将导致所有下标大于等于新length的属性被删除,设置更大的length不会给数组分配更多的空间,新增的每一项都会取得undefined值。
var colors = ["red", "blue", "green"]; colors.length = 2; colors[2]; // undefined colors; //["red", "blue"] var colors1 = ["red", "blue", "green"]; colors1.length = 4; colors1[3]; // undefined colors1; // ["red", "blue","green", undefined]
关于length的运用还有一个比较有意思的用法,通过把下标指定为一个数组的当前length,可以附加一个新元素到该数组的尾部:
var numbers = ["zero", "one", "two"]; numbers[numbers.length] = "shi"; numbers; // ["zero", "one", "two", "shi"]
5.prototype
这个属性说的太多了,也不是这次整理的重点,大家可以自行去查看,附上一张chrome下的图,版本40.0.2214.94 m:
三.检测数组
说到类型检测,可能很多人第一反应想到的就是typeof,但是对于一个数组来说,typeof返回的是一个"object",很明显是达不到检测数组类型的目的的。那么怎么看一个值是不是数组呢,下面就来整理下我所知道的几种方法。
1.instanceof与constructor
var is_array = function (value) { return value && typeof value === ‘object‘ && value.constructor === Array; }; var is_array1 = function (value) { return value && typeof value === ‘object‘ && value instanceof Array; }; var a = new Array(1,2,3); var b = [1,2,3]; is_array(a); // true is_array(b); // true is_array1(a); // true is_array1(b); // true
这个方法大部分情况下应该是可以满足需求的,兼容性也不错。但是别忘了,Array是window的属性,如果网页中包含多个框架,那实际上就存在两个以上不同的全局执行环境,从而存在两个以上不同版本的Array构造函数。如果你从一个框架向另一个框架传入一个数组,那么传入的数组与在第二个框架中原生创建的数组分别具有各自不同的构造函数。如下面的情况,可以在控制台运行一下看会输出什么:
var myFrame = document.createElement(‘iframe‘); document.body.appendChild(myFrame); var myArray = window.frames[window.frames.length-1].Array; var arr = new myArray("a","b",10); // [a,b,10] arr instanceof Array; // false
2.ECMAScript5 Array.isArray()方法
这个方法可以确定某个值到底是不是数组,而不管它是在哪个全局执行环境中创建的。支持这个方法的浏览器有IE9+、Firefox 4+ 、Safari 5+、Opera 10.5+和Chrome。
var a = new Array(1,2,3); var b = [1,2,3]; Array.isArray(a); // true Array.isArray(b); // true
这个方法不存在instanceof方法的问题,但是兼容性在目前来看还是不能满足需求的(前端开发人员应该都懂,说多了都是泪)。
3.Object.prototype.toString
千呼万唤始出来,终于到了这个方法。在任何值上调用Object原生的toString()方法,都会返回一个[object NativeConstructorName]格式的字符串。每个类在内部都有一个[[Class]]属性,这个属性中就指定了上述字符串中的构造函数名(当然你也可以用({}).toString)。
var a = new Array(1,2,3), b = [1,2,3], toStr = Object.prototype.toString; toStr.call(a); // "[object Array]" toStr.call(b); // "[object Array]"
鉴于本篇只谈数组,就只举数组的例子了。想详细了解这个方法内部原理的,可以戳这个链接JavaScript:Object.prototype.toString方法的原理。
4.一些类库里的检测方法
这里只贴一些大概的实现,不一定能运行哈,看看大概原理就好。
1).jQuery(1.9.1)
var class2type = {}, core_toString = class2type.toString, jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { class2type[ "[object " + name + "]" ] = name.toLowerCase(); }); isArray: Array.isArray || function( obj ) { return jQuery.type(obj) === "array"; }, type: function( obj ) { if ( obj == null ) { return String( obj ); } return typeof obj === "object" || typeof obj === "function" ? class2type[ core_toString.call(obj) ] || "object" : typeof obj; }
2).underscore(1.7.0)
var nativeIsArray = Array.isArray; _.isArray = nativeIsArray || function(obj) { return toString.call(obj) === ‘[object Array]‘; };
3).Zepto(1.1.6)
isArray = Array.isArray || function(object){ return object instanceof Array };
也就不一一列举了,基本都是根据上述的三种方法来处理的,我个人比较喜欢underscore的方式。不知道还有没有其他的办法,如果有希望大家能告知,不胜感激
四.总结
以上就是Array对象系列第一篇的内容了,主要整理了数组的创建、属性以及检测的问题,本人水平有限,难免会有疏漏,还希望大家能多多指教。下一篇将重点分析下数组的方法。