JavaScript常见引用类型笔记(一):Array类型(上)
这是我的第一篇博客。在今后将会不断更新自己在学习JavaScript的过程中整理的知识点,算是在博客上做笔记吧。
由于对这些知识还没有深入的理解,文中可能会大量引用《JavaScript高级程序设计》里描述性的语言,其他部分若是有错误,希望各位小伙伴们指出来并给与建议和意见(指点我怎么写博客也行(*^▽^*))。
今天整理的是有关Array类型的一部分知识。
Array类型的特点:
一、存储的数据的任意性
ECMAscrip数组的每一项可以保存任意类型的数据:
C语言或者很多其他类C语言中,数组里保存的所有数据必须保证数据类型一致:例如C语言中的数组:
int a[4] = [1,2,3,4,5]; char str[] = "program";
上述分别是一个整形数组和一个字符串类型的数组,数组里的各项数据都是同一种数据类型,数组a中保存的全是int类型,str中保存的都是char类型。而JavaScript数组中的每一项都可以保存任意的数据类型,下面是一个长度为3的数组,数组第一项保存一个Number类型、第二项保存一个String类型,第三项保存一个Function类型:
var a = [ 1 , "Jams" , function(sum){console.log(sum);} ]; console.log(a); a[2](a[1]); //打印“Jams”
数组a的输出结果(chrome)为:
二、数组长度的灵活性
在此观察上述C语言代码中的数组,发现在声明的时候数组a数组后会跟一个数字,这个数字就是该数组的长度,也就是说,在后续对数组的操作过程中,数组长度是固定的。而str数组由于在声明的过程中初始化了,所以其长度也是固定的,长度为“program”的字符个数7。
JavaScript中的数组大小可以动态调整,可以直接按索引添加,也可以使用pop()等方法移除。
创建数组的方式:
一、使用Array构造函数
基本语法:
var 数组名 = [new] Array(); //这时length属性默认为0,[]括起来的内容表示可以省略,即可以省略new
构建指定初始大小的数组:
var 数组名 = new Array(长度); //这个长度会变成length属性的值
使用构造函数创建并初始化:
var 数组名 =new Array(数据1,数据2,...,数据n); //注意,不能仅有一个为Number类型的数据,这样会变成指定初始大小的数组
二、使用数组字面量
使用字面量表示法时,不会调用Array构造函数。
基本语法:
var 数组名 = []; //创建一个length为0的空数组
包含初始数据项:
var 数组名 = [数据1,数据2,...,数据n] //创建一个包含n个数据的数组
注意,不要在最后一项数据末尾添加逗号“,”,类似于这样:var = [1,2,3,4,],也不要不写数据项,仅仅写逗号,类似于这样:var 数组名 = [,,,,,,]。因为这两种写法在不同浏览器上会有不同的显示结果,具体请看《JavaScript高级程序设计》Array类型讲解。
length属性:
length属性表示了数组当前的长度。它不是只读的,故而可以使用它改变数组的长度,直接赋值即可。也可以很方便的使用length在数组末尾添加一个数据。方法:
数组名[数组名.length] = 数据; //添加完之后,length的值会加一
这里整理一下,读写数组值和length值的变化:
//读取:索引小于length,读取存储在数组中原本的数据,索引大于等于length,读取到的值为undefined var a = [1,2,3,4,5]; console.log(a[4],a[7]); //5 undefined //写入:索引小于length值,改变数组中原本的数据,大于等于length值,修改这个位置的数组值,同时数组的length将更新为(索引+1),中间空出来的数组值都设置为undefined var b = [1,2,3,4,5]; b[3] = 7; console.log(b); //[1,2,3,7,5] var c = [1,2,3,4,5] c[8] = 9; console.log(c); //[1,2,3,4,5,undefined,undefined,undefined,9]; console.log(c.length); //9
检测数组的方法:
一、使用instanceof
由于数组是对象,故可以使用instanceof来判断某个数组是否是Array引用类型。方法:
数组名 instanceof Array //该表达式返回true或者false
优点:在单一全局环境中使用方便
缺点:多个全局环境下,由于Array构造函数在不同环境下可能不相同,那么就不能使用instanceof来进行判断。
个人猜测(如有理解不当,请不吝赐教,这也算是遗留的问题,在将来还需要深入学习):instanceof仅仅是顺着对象的原型链去查找各个原型对象的constructor属性,各个constructor属性指向的构造函数,就是对象所属的类别。由于不同全局环境下,Array构造函数都不一样,故有很多种数组引用类型。比如在某个环境下,有Array1类型的数组,这时从其他环境中传入了一个Array2类型的数组,他们的构造函数名字在各自的环境中可能都叫Array,这时,使用 数组名 instanceof Array; 怎么判断到底是Array1还是Array2。
二、使用isArray()方法
ES5新增的方法,使用Array调用,参数传入待检测的数组。
Array.isArray(数组名); //结果返回true或者false
优点:最终确定某个值到底是不是数组,而不管它在哪个全局执行环境中创建的
缺点:仅有IE9+、FireFox 4+、Safari 5+、Opera 10.5+和Chrome支持这个方法。
三、其他方法
后续再另开一个随笔总结,同时希望真正理解instanceof检测数组的限制。
转换方法:
一、使用继承自Object的toString()、toLocalString()、valueOf():
JS所有对象都有继承自Object的一些默认方法,可以直接拿来使用, 但若是自定义toString()、toLocalString()、valueOf()实例方法来覆盖继承自原型的Object方法之后,这些方法的行为将发生改变(下面用方法重写来表示)。
下面分几种情况来讨论(是否重写上述三种方法,数组里的数据类型)
1.数组里存储的全部为基本数据类型(这时也不存在重写了)
var a1 = [1,true,false,null,undefined,"KOBE"]; console.log(a1.toString()); console.log(a1.valueOf()); console.log(a1.toLocaleString()); console.log(a1); alert(a1.toString()); alert(a1.valueOf()); alert(a1.toLocaleString()); alert(a1);
使用console.log输出结果为:
使用alert输出结果为(四个alert均是这样的结果):
总结:基本数据类型数组
toString()方法:调用各数据项的toString()方法,将各数据项变成字符串的形式,然后使用逗号连接作为结果返回。
toLacolaeString()方法:调用各数据项的toLocaleString()方法,将各数据项变成字符串的形式,然后使用逗号连接作为结果返回。
valueOf()方法:返回该数组。
注意:alert()打印结果都一样,是因为alert()函数会将结果以字符串的形式打印出来。
2.数组里全为对象,且都不重写toString()、toLocaleString()和valueOf()方法。
var obj1 = {}; var obj2 = {}; var fun = function(){}; var a1 = [obj1,obj2,fun]; console.log(a1.toString()); console.log(a1.valueOf()); console.log(a1.toLocaleString()); console.log(a1); alert(a1.toString()); alert(a1.valueOf()); alert(a1.toLocaleString()); alert(a1);
使用console.log的输出结果:
使用alert的输出结果(四个alert均是这样的结果):
总结:对象数组,不重写
toString()方法:调用各数据项的toString()方法,将各数据项变成字符串的形式,然后使用逗号连接作为结果返回。
toLacolaeString()方法:调用各数据项的toLocaleString()方法,将各数据项变成字符串的形式,然后使用逗号连接作为结果返回。
valueOf()方法:返回该数组。
注意:alert()打印结果都一样,是因为alert()函数会将结果以字符串的形式打印出来。
3.对象数组,重写
var obj1 = {
id: 1, toString: function(){ return "toString"; }, toLocaleString: function(){ return "toLocaleString"; }, valueOf: function(){ return "valueOf"; } }; var obj2 = { id: 2, toString: function(){ return "toString"; }, toLocaleString: function(){ return "toLocaleString"; }, valueOf: function(){ return "valueOf"; } }; var fun = function(){}; fun.id = 3; fun.toString = function(){ return "toString"; }; fun.toLocaleString = function(){ return "toLocaleString"; }; fun.valueOf = function(){ return "valueOf"; }; var a1 = [obj1,obj2,fun]; console.log(a1.toString()); console.log(a1.valueOf()); console.log(a1.toLocaleString()); console.log(a1);
alert(a1.toString());
alert(a1.valueOf());
alert(a1.toLocaleString());
alert(a1);
使用console.log输出结果:
使用alert输出结果:
String,String,String,String
String,String,String,String
toLocaleString,toLocaleString,toLocaleString
String,String,String,String
总结:对象数组,重写
toString()方法:调用各数据项重写后的toString()方法,将各数据项变成字符串的形式,然后使用逗号连接作为结果返回。
toLacolaeString()方法:调用各数据项重写后的toLocaleString()方法,将各数据项变成字符串的形式,然后使用逗号连接作为结果返回。
valueOf()方法:返回该数组。
注意:使用alert打印数组会先调用各数据项的toString方法(在这里是重写后的方法),然后再连接起来打印输出。
二、join()方法
join方法和toString()方法类似,只不过该方法可以传入一个字符,该字符将取代toString方法中的 逗号分隔符。不传入参数和传入“,”时,输出结果和toString()方法一样。
//创建一个Date类型的对象 var a1 = new Date(); //创建一个Function类型的对象 var a2 = function(){}; //创建一个Obeject类型的对象 var a3 = {}; //创建一个数组 var b = [1,"li",null,undefined,true,a1,a2,a3]; console.log(b.join()); //不传入参数 console.log(b.join(",")); //传入一个逗号 console.log(b.join("||")); //传入一个其他分隔符
输出结果:
注意:无论是join()方法还是toString()方法,数据项如果是null或者undefined,则会返回一个空串""。
栈方法和队列方法:
1.栈方法。
可以将数组想象成一个数据栈,栈遵循LIFO(Last-In-First-Out,后进先出)的规则。
push()方法:
//数组名.push(数据1,数据2,数据3,...,数据n); 该方法返回值为插入数据后的length值 var a = ["li","ma","zhang"]; var b = a.pop("liu","zhao"); console.log(a); //["li","ma","zhang","liu","zhao"]
console.log(a.length); //5
console.log(b); //5
pop()方法:
//数组名.pop(); 该方法返回被弹出的数据 var a = ["li","ma","zhang"]; var str = a.pop(); console.log(a); //["li","ma"] console.log(a.length); //2 console.log(str); //"zhang"
2.队列方法
也可以将数组想象成一个队列,队列遵循FIFO(First-In-First-Out,先进先出)的规则。
shift()方法:
//数组名.shift(); 用法和pop()类似,只不过它是在数组最前方剔除数据 var a = ["li","ma","zhang"]; var str = a.shift(); console.log(a); //["ma","zhang"] console.log(a.length); //2 console.log(str); //"li"
unshift()方法:
//数组名.unshift(数据1,数据2,数据3,...,数据n); 用法与push类似,只不过它是在数组最前方添加数据 var a = ["li","ma","zhang"]; var b = a.unshift("liu","zhao"); console.log(a); //["liu","zhao","li",ma","zhang"] console.log(a.length); //5 console.log(b); //5
重排序方法:
reverse()方法:
reverse()方法会反转数组项。调用该方法后,会返回反转之后的数组的引用。
var a = [1,2,3]; var b = a.reverse(); //数组a调用reverse()方法,并将反转后的数组引用传给变量b,直接使用a.reverse()也可以,这样a指向的数组内部数据将会被反转
b.pop(); console.log(a); //[3,2] console.log(b); //[3,2] //剔除b的最后一个数据,发现a也改变了,可见a和b都是指向反转之后的数组
sort()方法:
1.不传入参数
按升序排列数组项,即最小的值位于最前面,最大的值位于最后面。但须要注意:sort()方法会先调用每个数据项的toString方法,然后再对每个转换之后的字符串进行比较。即使数据项是数字,也是对字符串进行比较。
var a = [1,31,21,18,94,7,4]; a.sort(); console.log(a); //1,18,21,31,4,7,94
2.给sort()方法传入一个比较函数做为参数
可以对数值型数组进行排序:
var a = [1,31,21,18,94,7,4]; a.sort(function(num1,num2){ //升序排列 if(num1<num2){ return -1; }else if(num1>num2){ return 1; }else{ return 0; } }); console.log(a); // 1,4,7,18,21,31,94
比较函数的原理是:将数据项按照冒泡原理的顺序传入比较函数之后,若比较函数返回值为负数,那么就将sum1对应的数据项将放在前面,sum2对应的数据项放在后面,如果返回值为正数,则sum1对应的数据项在后面,sum2对应的数据项在前面,如果返回值为0,则顺序保持不变。
也可以指定某个属性,对对象数组进行排序:
//创建三个对象 var obj1 = { id:1, name:"孙悟空" }; var obj2 = { id:2, name:"猪八戒" }; var obj3 = { id:3, name:"沙和尚" }; //放进数组中 var a = [obj2,obj1,obj3]; //创建一个返回比较函数的函数,其参数将被比较函数使用,比较函数也称为闭包 var compare = function(property){ return function(obj1,obj2){ var idValue1 = obj1[property]; var idVulue2 = obj2[property]; return idValue1 - idVulue2; } }; //注意下面compare要加()并传入参数,不然不会返回比较函数 a.sort(compare("id")); console.log(a);
输出结果(按id值升序排列):
下篇将总结数组的操作方法、迭代方法等等。
写在最后:诚心希望各位大佬能指点一二,这篇总结写的我怀疑人生了。花了将近四个小时,你们都是这样吗,还是说我总结的方法不对。另外,有错误请一定要帮我指出来,谢谢你