EMCAScript函数初级资料.docx
《EMCAScript函数初级资料.docx》由会员分享,可在线阅读,更多相关《EMCAScript函数初级资料.docx(18页珍藏版)》请在冰豆网上搜索。
EMCAScript函数初级资料
1函数概述
什么是函数?
函数是一组可以随时随地运行的语句。
函数是ECMAScript的核心。
函数是由这样的方式进行声明的:
关键字function、函数名、一组参数,以及置于括号中的待执行代码。
函数的基本语法是这样的:
functionfunctionName(arg0,arg1,...argN){
statements
}
例如:
functionsayHi(sName,sMessage){
alert("Hello"+sName+sMessage);
}
如何调用函数?
函数可以通过其名字加上括号中的参数进行调用,如果有多个参数。
如果您想调用上例中的那个函数,可以使用如下的代码:
sayHi("David","Nicetomeetyou!
")
调用上面的函数sayHi()会生成一个警告窗口。
函数如何返回值?
函数sayHi()未返回值,不过不必专门声明它(像在Java中使用void那样)。
即使函数确实有值,也不必明确地声明它。
该函数只需要使用return运算符后跟要返回的值即可。
functionsum(iNum1,iNum2){
returniNum1+iNum2;
}
下面的代码把sum函数返回的值赋予一个变量:
variResult=sum(1,1);
alert(iResult);//输出"2"
另一个重要概念是,与在Java中一样,函数在执行过return语句后立即停止代码。
因此,return语句后的代码都不会被执行。
例如,在下面的代码中,alert窗口就不会显示出来:
functionsum(iNum1,iNum2){
returniNum1+iNum2;
alert(iNum1+iNum2);
}
一个函数中可以有多个return语句,如下所示:
functiondiff(iNum1,iNum2){
if(iNum1>iNum2){
returniNum1-iNum2;
}else{
returniNum2-iNum1;
}
}
上面的函数用于返回两个数的差。
要实现这一点,必须用较大的数减去较小的数,因此用if语句决定执行哪个return语句。
如果函数无返回值,那么可以调用没有参数的return运算符,随时退出函数。
例如:
functionsayHi(sMessage){
if(sMessage=="bye"){
return;
}
alert(sMessage);
}
这段代码中,如果sMessage等于"bye",就永远不显示警告框。
2arguments对象
在函数代码中,使用特殊对象arguments,开发者无需明确指出参数名,就能访问它们。
例如,在函数sayHi()中,第一个参数是message。
用arguments[0]也可以访问这个值,即第一个参数的值(第一个参数位于位置0,第二个参数位于位置1,依此类推)。
因此,无需明确命名参数,就可以重写函数:
functionsayHi(){
if(arguments[0]=="bye"){
return;
}
alert(arguments[0]);
}
检测参数个数
还可以用arguments对象检测函数的参数个数,引用属性arguments.length即可。
下面的代码将输出每次调用函数使用的参数个数:
functionhowManyArgs(){
alert(arguments.length);
}
howManyArgs("string",45);
howManyArgs();
howManyArgs(12);
上面这段代码将依次显示"2"、"0"和"1"。
注释:
与其他程序设计语言不同,ECMAScript不会验证传递给函数的参数个数是否等于函数定义的参数个数。
开发者定义的函数都可以接受任意个数的参数(根据Netscape的文档,最多可接受25个),而不会引发任何错误。
任何遗漏的参数都会以undefined传递给函数,多余的函数将忽略。
模拟函数重载
用arguments对象判断传递给函数的参数个数,即可模拟函数重载:
functiondoAdd(){
if(arguments.length==1){
alert(arguments[0]+5);
}elseif(arguments.length==2){
alert(arguments[0]+arguments[1]);
}
}
doAdd(10);//输出"15"
doAdd(40,20);//输出"60"
当只有一个参数时,doAdd()函数给参数加5。
如果有两个参数,则会把两个参数相加,返回它们的和。
所以,doAdd(10)输出的是"15",而doAdd(40,20)输出的是"60"。
3继承机制
说明继承机制最简单的方式是,利用一个经典的例子-几何形状。
实际上,几何形状只有两种,即椭圆形(是圆形的)和多边形(具有一定数量的边)。
圆是椭圆的一种,它只有一个焦点。
三角形、矩形和五边形都是多边形的一种,具有不同数量的边。
正方形是矩形的一种,所有的边等长。
这就构成了一种完美的继承关系。
在这个例子中,形状(Shape)是椭圆形(Ellipse)和多边形(Polygon)的基类(baseclass)(所有类都由它继承而来)。
椭圆具有一个属性foci,说明椭圆具有的焦点的个数。
圆形(Circle)继承了椭圆形,因此圆形是椭圆形的子类(subclass),椭圆形是圆形的超类(superclass)。
同样,三角形(Triangle)、矩形(Rectangle)和五边形(Pentagon)都是多边形的子类,多边形是它们的超类。
最后,正方形(Square)继承了矩形。
最好用图来解释这种继承关系,这是UML(统一建模语言)的用武之地。
UML的主要用途之一是,可视化地表示像继承这样的复杂对象关系。
下面的图示是解释Shape和它的子类之间关系
在UML中,每个方框表示一个类,由类名说明。
三角形、矩形和五边形顶部的线段汇集在一起,指向形状,说明这些类都由形状继承而来。
同样,从正方形指向矩形的箭头说明了它们之间的继承关系。
继承机制的实现
要用ECMAScript实现继承机制,您可以从要继承的基类入手。
所有开发者定义的类都可作为基类。
出于安全原因,本地类和宿主类不能作为基类,这样可以防止公用访问编译过的浏览器级的代码,因为这些代码可以被用于恶意攻击。
选定基类后,就可以创建它的子类了。
是否使用基类完全由你决定。
有时,你可能想创建一个不能直接使用的基类,它只是用于给子类提供通用的函数。
在这种情况下,基类被看作抽象类。
尽管ECMAScript并没有像其他语言那样严格地定义抽象类,但有时它的确会创建一些不允许使用的类。
通常,我们称这种类为抽象类。
创建的子类将继承超类的所有属性和方法,包括构造函数及方法的实现。
记住,所有属性和方法都是公用的,因此子类可直接访问这些方法。
子类还可添加超类中没有的新属性和方法,也可以覆盖超类的属性和方法。
继承的方式
和其他功能一样,ECMAScript实现继承的方式不止一种。
这是因为JavaScript中的继承机制并不是明确规定的,而是通过模仿实现的。
这意味着所有的继承细节并非完全由解释程序处理。
作为开发者,你有权决定最适用的继承方式。
下面为您介绍几种具体的继承方式。
对象冒充
构想原始的ECMAScript时,根本没打算设计对象冒充(objectmasquerading)。
它是在开发者开始理解函数的工作方式,尤其是如何在函数环境中使用this关键字后才发展出来。
其原理如下:
构造函数使用this关键字给所有属性和方法赋值(即采用类声明的构造函数方式)。
因为构造函数只是一个函数,所以可使ClassA构造函数成为ClassB的方法,然后调用它。
ClassB就会收到ClassA的构造函数中定义的属性和方法。
例如,用下面的方式定义ClassA和ClassB:
functionClassA(sColor){
this.color=sColor;
this.sayColor=function(){
alert(this.color);
};
}
functionClassB(sColor){
}
还记得吗?
关键字this引用的是构造函数当前创建的对象。
不过在这个方法中,this指向的所属的对象。
这个原理是把ClassA作为常规函数来建立继承机制,而不是作为构造函数。
如下使用构造函数ClassB可以实现继承机制:
functionClassB(sColor){
this.newMethod=ClassA;
this.newMethod(sColor);
deletethis.newMethod;
}
在这段代码中,为ClassA赋予了方法newMethod(请记住,函数名只是指向它的指针)。
然后调用该方法,传递给它的是ClassB构造函数的参数sColor。
最后一行代码删除了对ClassA的引用,这样以后就不能再调用它。
所有新属性和新方法都必须在删除了新方法的代码行后定义。
否则,可能会覆盖超类的相关属性和方法:
functionClassB(sColor,sName){
this.newMethod=ClassA;
this.newMethod(sColor);
deletethis.newMethod;
this.name=sName;
this.sayName=function(){
alert(this.name);
};
}
为证明前面的代码有效,可以运行下面的例子:
varobjA=newClassA("blue");
varobjB=newClassB("red","John");
objA.sayColor();//输出"blue"
objB.sayColor();//输出"red"
objB.sayName();//输出"John"
对象冒充可以实现多重继承
有趣的是,对象冒充可以支持多重继承。
也就是说,一个类可以继承多个超类。
用UML表示的多重继承机制如下图所示:
例如,如果存在两个类ClassX和ClassY,ClassZ想继承这两个类,可以使用下面的代码:
functionClassZ(){
this.newMethod=ClassX;
this.newMethod();
deletethis.newMethod;
this.newMethod=ClassY;
this.newMethod();
deletethis.newMethod;
}
这里存在一个弊端,如果存在两个类ClassX和ClassY具有同名的属性或方法,ClassY具有高优先级。
因为它从后面的类继承。
除这点小问题之外,用对象冒充实现多重继承机制轻而易举。
由于这种继承方法的流行,ECMAScript的第三版为Function对象加入了两个方法,即call()和apply()。
call()方法
call()方法是与经典的对象冒充方法最相似的方法。
它的第一个参数用作this的对象。
其他参数都直接传递给函数自身。
例如:
functionsayColor(sPrefix,sSuffix){
alert(sPrefix+this.color+sSuffix);
};
varobj=newObject();
obj.color="blue";
sayColor.call(obj,"Thecoloris","averynicecolorindeed.");
在这个例子中,函数sayColor()在对象外定义,即使它不属于任何对象,也可以引用关键字this。
对象obj的color属性等于blue。
调用call()方法时,第一个参数是obj,说明应该赋予sayColor()函数中的this关键字值是obj。
第二个和第三个参数是字符串。
它们与sayColor()函数中的参数sPrefix和sSuffix匹配,最后生成的消息"Thecolorisblue,averynicecolorindeed."将被显示出来。
要与继承机制的对象冒充方法一起使用该方法,只需将前三行的赋值、调用和删除代码替换即可:
functionClassB(sColor,sName){
//this.newMethod=ClassA;
//this.newMethod(color);
//deletethis.newMethod;
ClassA.call(this,sColor);
this.name=sName;
this.sayName=function(){
alert(this.name);
};
}
这里,我们需要让ClassA中的关键字this等于新创建的ClassB对象,因此this是第一个参数。
第二个参数sColor对两个类来说都是唯一的参数。
apply()方法
apply()方法有两个参数,用作this的对象和要传递给函数的参数的数组。
例如:
functionsayColor(sPrefix,sSuffix){
alert(sPrefix+this.color+sSuffix);
};
varobj=newObject();
obj.color="blue";
sayColor.call(obj,newArray("Thecoloris","averynicecolorindeed."));
这个例子与前面的例子相同,只是现在调用的是apply()方法。
调用apply()方法时,第一个参数仍是obj,说明应该赋予sayColor()函数中的this关键字值是obj。
第二个参数是由两个字符串构成的数组,与sayColor()函数中的参数sPrefix和sSuffix匹配,最后生成的消息仍是"Thecolorisblue,averynicecolorindeed.",将被显示出来。
该方法也用于替换前三行的赋值、调用和删除新方法的代码:
functionClassB(sColor,sName){
//this.newMethod=ClassA;
//this.newMethod(color);
//deletethis.newMethod;
ClassA.apply(this,newArray(sColor));
this.name=sName;
this.sayName=function(){
alert(this.name);
};
}
同样的,第一个参数仍是this,第二个参数是只有一个值color的数组。
可以把ClassB的整个arguments对象作为第二个参数传递给apply()方法:
functionClassB(sColor,sName){
//this.newMethod=ClassA;
//this.newMethod(color);
//deletethis.newMethod;
ClassA.apply(this,arguments);
this.name=sName;
this.sayName=function(){
alert(this.name);
};
}
4加性运算符
法运算符由加号(+)表示:
variResult=1+2
与乘性运算符一样,在处理特殊值时,ECMAScript中的加法也有一些特殊行为:
∙某个运算数是NaN,那么结果为NaN。
∙-Infinity加-Infinity,结果为-Infinity。
∙Infinity加-Infinity,结果为NaN。
∙+0加+0,结果为+0。
∙-0加+0,结果为+0。
∙-0加-0,结果为-0。
不过,如果某个运算数是字符串,那么采用下列规则:
∙如果两个运算数都是字符串,把第二个字符串连接到第一个上。
∙如果只有一个运算数是字符串,把另一个运算数转换成字符串,结果是两个字符串连接成的字符串。
例如:
varresult=5+5;//两个数字
alert(result);//输出"10"
varresult2=5+"5";//一个数字和一个字符串
alert(result);//输出"55"
这段代码说明了加法运算符的两种模式之间的差别。
正常情况下,5+5等于10(原始数值),如上述代码中前两行所示。
不过,如果把一个运算数改为字符串"5",那么结果将变为"55"(原始的字符串值),因为另一个运算数也会被转换为字符串。
注意:
为了避免JavaScript中的一种常见错误,在使用加法运算符时,一定要仔细检查运算数的数据类型。
减法运算符
减法运算符(-),也是一个常用的运算符:
variResult=2-1;
与加法运算符一样,在处理特殊值时,减法运算符也有一些特殊行为:
∙某个运算数是NaN,那么结果为NaN。
∙-Infinity减Infinity,结果为NaN。
∙-Infinity减-Infinity,结果为NaN。
∙Infinity减-Infinity,结果为Infinity。
∙-Infinity减Infinity,结果为-Infinity。
∙+0减+0,结果为+0。
∙-0减-0,结果为-0。
∙+0减-0,结果为+0。
∙某个运算符不是数字,那么结果为NaN。
注释:
如果运算数都是数字,那么执行常规的减法运算,并返回结果。
5类型转换
ECMAScript的Boolean值、数字和字符串的原始值的有趣之处在于它们是伪对象,这意味着它们实际上具有属性和方法。
例如,要获得字符串的长度,可以采用下面的代码:
varsColor="red";
alert(sColor.length);//输出"3"
尽管"red"是原始类型的字符串,它仍然具有属性length,用于存放字符串的大小。
总而言之,3种主要的原始类型Boolean值、数字和字符串都有toString()方法,可以把它们的值转换成字符串。
提示:
您也许会问,“字符串还有toString()方法吗,这不是多余吗?
”是的,的确如此,不过ECMAScript定义所有对象都有toString()方法,无论它是伪对象,还是真对象。
因为String类型属于伪对象,所以它一定有toString()方法。
Boolean类型的toString()方法只是输出"true"或"false",结果由变量的值决定:
varbFound=false;
alert(bFound.toString());//输出"false"
Number类型的toString()方法比较特殊,它有两种模式,即默认模式和基模式。
采用默认模式,toString()方法只是用相应的字符串输出数字值(无论是整数、浮点数还是科学计数法),如下所示:
variNum1=10;
variNum2=10.0;
alert(iNum1.toString());//输出"10"
alert(iNum2.toString());//输出"10"
注释:
在默认模式中,无论最初采用什么表示法声明数字,Number类型的toString()方法返回的都是数字的十进制表示。
因此,以八进制或十六进制字面量形式声明的数字输出的都是十进制形式的。
采用Number类型的toString()方法的基模式,可以用不同的基输出数字,例如二进制的基是2,八进制的基是8,十六进制的基是16。
基只是要转换成的基数的另一种加法而已,它是toString()方法的参数:
variNum=10;
alert(iNum1.toString
(2));//输出"1010"
alert(iNum1.toString(8));//输出"12"
alert(iNum1.toString(16));//输出"A"
在前面的示例中,以3种不同的形式输出了数字10,即二进制形式、八进制形式和十六进制形式。
HTML采用十六进制表示每种颜色,在HTML中处理数字时这种功能非常有用。
注释:
对数字调用toString(10)与调用toString()相同,它们返回的都是该数字的十进制形式。
转换成数字
ECMAScript提供了两种把非数字的原始值转换成数字的方法,即parseInt()和parseFloat()。
正如您可能想到的,前者把值转换成整数,后者把值转换成浮点数。
只有对String类型调用这些方法,它们才能正确运行;对其他类型返回的都是NaN。
parseInt()
在判断字符串是否是数字值前,parseInt()和parseFloat()都会仔细分析该字符串。
parseInt()方法首先查看位置0处的字符,判断它是否是个有效数字;如果不是,该方法将返回NaN,不再继续执行其他操作。
但如果该字符是有效数字,该方法将查看位置1处的字符,进行同样的测试。
这一过程将持续到发现非有效数字的字符为止,此时parseInt()将把该字符之前的字符串转换成数字。
例如,如果要把字符串"12345red"转换成整数,那么parseInt()将返回12345,因为当它检查到字符r时,就会停止检测过程。
字符串中包含的数字字面量会被正确转换为数字,比如"0xA"会被正确转换为数字10。
不过,字符串"22.5"将被转换成22,因为对于整数来说,小数点是无效字符。
一些示例如下:
variNum1=parseInt("12345red");//返回12345
variNum1=parseInt("0xA");//返回10
variNum1=parseInt("56.9");//返回56
variNum1=parseInt("red");//返回NaN
parseInt()方法还有基模式,可以把二进制、八进制、十六进制或其他任何进制的字符串转换成整数。
基是由parseInt()方法的第二个参数指定的,所以要解析十六进制的