JavaScript精粹读书笔记(6)
第6章 数组
数组是一段线性分配的内存,它通过整数去计算偏移并访问其中的元素。数组可以是很快的数据结构。不幸的是,JavaScript的数组不是这样的。它提供了一种拥有类似数组特性的对象。它把数组的下标转变成字符串,用其作为属性。它明显地比一个真正的数组慢,但它可以更方便地使用。属性的检索和更新的方式与对象一模一样,除了有一个可以用整数作为属性名的特性外。数组有它们自己的定义格式。数组也有一套非常有用的内置方法,我将在第8章描述它们。
6.1 数组定义
数组定义提供了一种非常方便地创建新数组的表示法。一个数组定义是在一对方括号中包围零个或多个用逗号分隔的值的表达式。数组定义可以出现在任何表达式可以出现的地方。数组的第一个值将获得属性名’0’,第二个值将获得属性名’1’,依次类推:
而以下的对象定义将产生一个相似的结果:
numbers和numbers_object都是包含有10个属性的对象,并且那些属性刚好有相同的名字和值。但是它们也有一些显著的不同。numbers继承自Array.prototype,而numbers_object继承自Object.prototype,所以numbers继承了大量有用的方法。同时,numbers也有一个诡异的length属性,而numbers_object则没有。
在大多数语言中,一个数组的所有元素都要求是相同的类型。JavaScript允许数组包含任意混合类型的值:
6.2 长度
每个数组都有一个length属性。和大多数其他语言不同,JavaScript数组的length是没有上界的。如果你用大于或等于当前length的数字作为下标来保存一个元素,那么length将增大,而不会发生数组边界越界错误。
length属性的值是这个数组的最大整数属性名加上1.它不一定等于数组里的属性的个数:(默然说话:我的天!那在一些特殊情况下,想要通过一个i的自增来遍历一个数组将是不可靠的了!)
[]后缀下标运算符将它的表达式转换成一个字符串,如果该表达式有toString方法,就使用该方法的值。这个字符串将被用作属性名。如果这个字符串看起来像一个大于等于这个数组当前的length且小于4 294 967 295的正整数,那么这个数组的length就会被重新设置为新的下标加1。
你可以直接设置length的值。设置更大的length对数组无影响。而把length设小将导致所有下标大于等于新length的属性被删除(默然说话:经验证,对length的设置只影响整数下标,对于字符串下标的元素无影响):
通过把下标指定为一个数组的当前length,可以附加一个新元素到该数组的尾部。
有时用push方法可以更方便地完成同样的事情:
6.3 删除
由于JavaScript的数组其实就是对象,所以delete运算符可以用来从数组中移除元素:
不幸的是,那样会在数组中遗留一个空洞。这是因为排在被删除元素之后的元素保留了它们最初的名字(下标)。而你通常想要的是递减后面每个元素的名字(下标)。
幸运的是,JavaScript数组有一个splice方法。它可以对数组做个手术,删除一些元素并将它们替换为其他的元素。第一个参数是数组中的一个序号。第二个参数是要删除的元素个数。任何额外的参数会在序号那个点的位置被插入到数组中:
6.4 枚举
因为JavaScript的数组其实就是对象,所以for in语句可以用来遍历一个数组的所有属性。但for in无法保证属性的顺序,而大多数的数组应用都期望按照阿拉伯数字顺序来产生元素。此外,可能从原型链中得到意外属性的问题依旧存在。常规for语句可以避免这些问题。(默然说话:但如果数组的情况比较特殊,那在遍历时就需要小心了)
6.5 混淆的地方
在JavaScript编程中,一个常见的错误是在需要使用数组时使用对象,而在需要使用对象时使用了数组。其实规则很简单:当属性名是小而连续的整数时,你应该使用数组。否则,使用对象。
JavaScript使用typeof运算符报告数组的类型是’object’,这没有什么意义,也说明JavaScript本身对于数组和对象的区别是混乱的。
JavaScript在区别数组和对象上没有一个好的机制。可以通过定义我们自己的is_array函数来避开这个缺陷:
不幸的是,它在识别从不同的窗口或帧里构造的数组时会失败。如果想要准确地检测那些外部的数组,我们不得不做更多的工作:
因为有了这样的一个测试,就有可能写一个函数,当传递的是单一值时只做一件事,而在传递一组值时要做很多的事情。
6.6 方法
JavaScript提供了一套作用于数组的方法。这些方法是被储存在Array.prototype中的函数。在第3章中,我们看到Object.rototype是可以被扩充的。同样,Array.prototype也可以被扩充。
举例来说,假设我们想要给array增加一个方法,它将允许我们对数组进行计算:
通过给Array.prototype扩充一个函数,每个数组都继承了这人方法。在这个例子中,我们定义了一个reduce方法,它接受一个函数和一个初始值作为参数。它遍历这个数组,以当有元素和该初始传来参数调用这人函数,并且计算出一个新值。当完成时,它返回这个值。如果我们传入一个将两个数字要加的函数,它会计算相加的和。如果我们传入将两个数字相乘的函数,它会计算其乘积:
因为数组其实就是对象,所以我们可以直接给一个单独的数组添加方法:
因为字符串’total’不是整数,所以给数组增加一个total不会改变它的长度。
6.7 维度
JavaScript的数组通常不会初始化。如果你用[]得到一个新数组,它将是空的。如果你访问一个不存在的元素,则将得到undefined。如果你了解这人问题,或者你在尝试获取每个元素之前都很有预见性地设置了它,那么万事大吉。但如果你实现的算法是假设每个元素都从一个已知的值开始(例如0),那么你必须自己准备好这人数组。JavaScript应该提供一个类似Array.dim这样的方法来做这个事情,但我们可以很容易纠正这个疏忽:
JavaScipt没有多维数组,但就像大多数类C语言一样,它支持元素为数组的数组。
为了创建一个二维数组或一个元素为数组的数组,你必须自己去创建那个第二维的数组:
一个空矩阵的每个单元将拥有一个初始值undefined。如果你希望它们有不同的初始值,你必须明确地设置它们。同样,JavaScript应该对矩阵提供更好的支持。好在我们也可以修正它:
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/mouyong/archive/2010/01/25/5252558.aspx
数组是一段线性分配的内存,它通过整数去计算偏移并访问其中的元素。数组可以是很快的数据结构。不幸的是,JavaScript的数组不是这样的。它提供了一种拥有类似数组特性的对象。它把数组的下标转变成字符串,用其作为属性。它明显地比一个真正的数组慢,但它可以更方便地使用。属性的检索和更新的方式与对象一模一样,除了有一个可以用整数作为属性名的特性外。数组有它们自己的定义格式。数组也有一套非常有用的内置方法,我将在第8章描述它们。
6.1 数组定义
数组定义提供了一种非常方便地创建新数组的表示法。一个数组定义是在一对方括号中包围零个或多个用逗号分隔的值的表达式。数组定义可以出现在任何表达式可以出现的地方。数组的第一个值将获得属性名’0’,第二个值将获得属性名’1’,依次类推:
+展开
-JavaScript
var empty=[];
var numbers=[‘zero’,’one’,’two’,’three’,’four’,’five’,’six’,’seven’,’eight’,’nine’];
empty[1] //undefined
numbers[1] //’one’
empty.length //0
numbers.length //10
var numbers=[‘zero’,’one’,’two’,’three’,’four’,’five’,’six’,’seven’,’eight’,’nine’];
empty[1] //undefined
numbers[1] //’one’
empty.length //0
numbers.length //10
而以下的对象定义将产生一个相似的结果:
+展开
-JavaScript
var numbers_object={
‘0’:‘zero’,’1’:’one’,’2’:’two’,’3’:’three’,
’4’:’four’,’5’:’five’,’6’:’six’,’7’:’seven’,
’8’:’eight’,’9’:’nine’
};
‘0’:‘zero’,’1’:’one’,’2’:’two’,’3’:’three’,
’4’:’four’,’5’:’five’,’6’:’six’,’7’:’seven’,
’8’:’eight’,’9’:’nine’
};
numbers和numbers_object都是包含有10个属性的对象,并且那些属性刚好有相同的名字和值。但是它们也有一些显著的不同。numbers继承自Array.prototype,而numbers_object继承自Object.prototype,所以numbers继承了大量有用的方法。同时,numbers也有一个诡异的length属性,而numbers_object则没有。
在大多数语言中,一个数组的所有元素都要求是相同的类型。JavaScript允许数组包含任意混合类型的值:
+展开
-JavaScript
var misc=[‘string’,98.6,true,false,null,undefined,[‘nested’,’array’],{object:true},NaN,Infinity];
misc.length //10
misc.length //10
6.2 长度
每个数组都有一个length属性。和大多数其他语言不同,JavaScript数组的length是没有上界的。如果你用大于或等于当前length的数字作为下标来保存一个元素,那么length将增大,而不会发生数组边界越界错误。
length属性的值是这个数组的最大整数属性名加上1.它不一定等于数组里的属性的个数:(默然说话:我的天!那在一些特殊情况下,想要通过一个i的自增来遍历一个数组将是不可靠的了!)
+展开
-JavaScript
var myArray=[];
myArray.length; //0
myArray[10000]=true;
alert(myArray.length); //10001
//myArray只包含一个属性
myArray.length; //0
myArray[10000]=true;
alert(myArray.length); //10001
//myArray只包含一个属性
[]后缀下标运算符将它的表达式转换成一个字符串,如果该表达式有toString方法,就使用该方法的值。这个字符串将被用作属性名。如果这个字符串看起来像一个大于等于这个数组当前的length且小于4 294 967 295的正整数,那么这个数组的length就会被重新设置为新的下标加1。
你可以直接设置length的值。设置更大的length对数组无影响。而把length设小将导致所有下标大于等于新length的属性被删除(默然说话:经验证,对length的设置只影响整数下标,对于字符串下标的元素无影响):
+展开
-JavaScript
numbers.length=3; //[‘zero’,’one’,’two’]
通过把下标指定为一个数组的当前length,可以附加一个新元素到该数组的尾部。
+展开
-JavaScript
numbers[numbers.length]=’shi’; //[‘zero’,’one’,’two’,’shi’]
有时用push方法可以更方便地完成同样的事情:
+展开
-JavaScript
numbers.push(‘go’); //[‘zero’,’one’,’two’,’shi’,’go’]
6.3 删除
由于JavaScript的数组其实就是对象,所以delete运算符可以用来从数组中移除元素:
+展开
-JavaScript
delete numbers[2]; //[‘zero’,’one’,undefined,’shi’,’go’]
不幸的是,那样会在数组中遗留一个空洞。这是因为排在被删除元素之后的元素保留了它们最初的名字(下标)。而你通常想要的是递减后面每个元素的名字(下标)。
幸运的是,JavaScript数组有一个splice方法。它可以对数组做个手术,删除一些元素并将它们替换为其他的元素。第一个参数是数组中的一个序号。第二个参数是要删除的元素个数。任何额外的参数会在序号那个点的位置被插入到数组中:
+展开
-JavaScript
numbers.splice(2,1); //[‘zero’,’one’,’shi’,’go’]
6.4 枚举
因为JavaScript的数组其实就是对象,所以for in语句可以用来遍历一个数组的所有属性。但for in无法保证属性的顺序,而大多数的数组应用都期望按照阿拉伯数字顺序来产生元素。此外,可能从原型链中得到意外属性的问题依旧存在。常规for语句可以避免这些问题。(默然说话:但如果数组的情况比较特殊,那在遍历时就需要小心了)
6.5 混淆的地方
在JavaScript编程中,一个常见的错误是在需要使用数组时使用对象,而在需要使用对象时使用了数组。其实规则很简单:当属性名是小而连续的整数时,你应该使用数组。否则,使用对象。
JavaScript使用typeof运算符报告数组的类型是’object’,这没有什么意义,也说明JavaScript本身对于数组和对象的区别是混乱的。
JavaScript在区别数组和对象上没有一个好的机制。可以通过定义我们自己的is_array函数来避开这个缺陷:
+展开
-JavaScript
var is_array=function(value){
return value && typeof value==='object' && value.constructor===Array;
}
return value && typeof value==='object' && value.constructor===Array;
}
不幸的是,它在识别从不同的窗口或帧里构造的数组时会失败。如果想要准确地检测那些外部的数组,我们不得不做更多的工作:
+展开
-JavaScript
var is_array=function(value){
return value && typeof value==='object' &&
typeof value.length===’number’ &&
tyeof value.splice===’function’ &&
!(value.propertyIsEnumerable(‘length’));
}
return value && typeof value==='object' &&
typeof value.length===’number’ &&
tyeof value.splice===’function’ &&
!(value.propertyIsEnumerable(‘length’));
}
因为有了这样的一个测试,就有可能写一个函数,当传递的是单一值时只做一件事,而在传递一组值时要做很多的事情。
6.6 方法
JavaScript提供了一套作用于数组的方法。这些方法是被储存在Array.prototype中的函数。在第3章中,我们看到Object.rototype是可以被扩充的。同样,Array.prototype也可以被扩充。
举例来说,假设我们想要给array增加一个方法,它将允许我们对数组进行计算:
+展开
-JavaScript
Array.method('reduce',function(f,value){
var i;
for(i=0;i<tis.length;i+=1){
value=f(this[i],value);
}
return value;
});
var i;
for(i=0;i<tis.length;i+=1){
value=f(this[i],value);
}
return value;
});
通过给Array.prototype扩充一个函数,每个数组都继承了这人方法。在这个例子中,我们定义了一个reduce方法,它接受一个函数和一个初始值作为参数。它遍历这个数组,以当有元素和该初始传来参数调用这人函数,并且计算出一个新值。当完成时,它返回这个值。如果我们传入一个将两个数字要加的函数,它会计算相加的和。如果我们传入将两个数字相乘的函数,它会计算其乘积:
+展开
-JavaScript
Array.method('reduce',function(f,value){
var i;
for(i=0;i<this.length;i+=1){
value=f(this[i],value);
}
return value;
});
//创建一人数字数组
var data=[4,8,15,16,23,42];
//定义两个简单的函数。一个是将两个数字相加,另一个是将两个数字相乘
var add=function(a,b){
return a+b;
}
var mult=function(a,b){
return a*b;
}
//调用data的reduce方法,传入add函数
var sum=data.reduce(add,0)//和是108
//再次调用reduce方法,这次传入mult函数
var product=data.reduce(mult,1);//乘积是7418880
var i;
for(i=0;i<this.length;i+=1){
value=f(this[i],value);
}
return value;
});
//创建一人数字数组
var data=[4,8,15,16,23,42];
//定义两个简单的函数。一个是将两个数字相加,另一个是将两个数字相乘
var add=function(a,b){
return a+b;
}
var mult=function(a,b){
return a*b;
}
//调用data的reduce方法,传入add函数
var sum=data.reduce(add,0)//和是108
//再次调用reduce方法,这次传入mult函数
var product=data.reduce(mult,1);//乘积是7418880
因为数组其实就是对象,所以我们可以直接给一个单独的数组添加方法:
+展开
-JavaScript
//给data数组添加一个total方法
data.total=function(){
return this.reduce(add,0);
}
data.total=function(){
return this.reduce(add,0);
}
因为字符串’total’不是整数,所以给数组增加一个total不会改变它的长度。
6.7 维度
JavaScript的数组通常不会初始化。如果你用[]得到一个新数组,它将是空的。如果你访问一个不存在的元素,则将得到undefined。如果你了解这人问题,或者你在尝试获取每个元素之前都很有预见性地设置了它,那么万事大吉。但如果你实现的算法是假设每个元素都从一个已知的值开始(例如0),那么你必须自己准备好这人数组。JavaScript应该提供一个类似Array.dim这样的方法来做这个事情,但我们可以很容易纠正这个疏忽:
+展开
-JavaScript
Array.dim=function(dimension,initial){
var a=[],i;
for(i=0;i<dimension;i+=1){
a[i]=initial;
}
return a;
}
//创建一个包含10个0的数组
var myArray=Array.dim(10,0);
var a=[],i;
for(i=0;i<dimension;i+=1){
a[i]=initial;
}
return a;
}
//创建一个包含10个0的数组
var myArray=Array.dim(10,0);
JavaScipt没有多维数组,但就像大多数类C语言一样,它支持元素为数组的数组。
+展开
-JavaScript
var matrix={
[0,1,2],
[3,4,5],
[6,7,8]
};
matrix[2][1]//7
[0,1,2],
[3,4,5],
[6,7,8]
};
matrix[2][1]//7
为了创建一个二维数组或一个元素为数组的数组,你必须自己去创建那个第二维的数组:
+展开
-JavaScript
for(i=0;i<n;i+=1){
my_array[i]=[];
}
//注意:Array.dim(n,[])在这里不能工作。
//如果使用它,每个元素都指向同一个数组的引用,那是非常糟糕的。
my_array[i]=[];
}
//注意:Array.dim(n,[])在这里不能工作。
//如果使用它,每个元素都指向同一个数组的引用,那是非常糟糕的。
一个空矩阵的每个单元将拥有一个初始值undefined。如果你希望它们有不同的初始值,你必须明确地设置它们。同样,JavaScript应该对矩阵提供更好的支持。好在我们也可以修正它:
+展开
-JavaScript
Array.matrix=function(m,n,initial){
var a,i,j,mat=[];
for(i=0;i<m;i+=1){
a=[];
for(j=0;j<n;j+=1){
a[j]=0;
}
mat[i]=a;
}
return mat;
}
//构造一个用0填充的4*4矩阵
var myMatrix=Array.matrix(4,4,0);
document.writerln(myMatrix[3][3]);//0
//用来构造一个同值矩阵的方法
Array.identity=function(n){
var i, mat=Array.matrix(n,n,0);
for(i=0;i<n;i+=1){
mat[i][i]=1;
}
return mat;
}
myMatrix=Array.identity(4);
document.writeln(myMatrix[3][3]); //1
var a,i,j,mat=[];
for(i=0;i<m;i+=1){
a=[];
for(j=0;j<n;j+=1){
a[j]=0;
}
mat[i]=a;
}
return mat;
}
//构造一个用0填充的4*4矩阵
var myMatrix=Array.matrix(4,4,0);
document.writerln(myMatrix[3][3]);//0
//用来构造一个同值矩阵的方法
Array.identity=function(n){
var i, mat=Array.matrix(n,n,0);
for(i=0;i<n;i+=1){
mat[i][i]=1;
}
return mat;
}
myMatrix=Array.identity(4);
document.writeln(myMatrix[3][3]); //1
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/mouyong/archive/2010/01/25/5252558.aspx
加支付宝好友偷能量挖...