- jQuery即学即用
- 王志刚编著
- 3204字
- 2020-08-27 04:35:55
第2章 JavaScript语法基础
无论使用何种JavaScript程序库,都必须使用基本的JavaScript语句来调用。因此在学习jQuery前,有必要先了解一下JavaScript的基本知识。对那些已经掌握了JavaScript编程知识的读者,可以跳过本章,直接进入下一章的学习。
2.1 JavaScript的语言基础
2.1.1 在HTML/xHTML文档内插入JavaScript
使用标记<script>…</script>可以在HTML/xHTML文档的任意处,甚至在<HTML>之前插入JavaScript。如果要在声明框架的网页(框架网页)中插入,则一定要在<frameset>之前插入;否则不会运行。
基本格式如下:
<script> <!-- ... (JavaScript代码) ... //--> </script>
第2行和第4行的作用是让不能识别<script>标记的浏览器忽略JavaScript代码,一般可以省略。第4行前的双反斜杠“//”是JavaScript的注释标号。
另外一种插入JavaScript的方法是把JavaScript代码写到另一个文件中(此文件通常用“.js”作为扩展名),然后用格式为“<script src="javascript.js"></script>”的标记将其嵌入到文档中。注意,一定要用“</script& gt;”标记。
<script>标记还有一个属性“language”(缩写为“lang”),说明脚本使用的语言,在JavaScript中为“language="JavaScript"”。
相对于<script>,还有一个<server>标记,其中包含服务器端(Server Side)的脚本。本书只讨论客户端(Client Side)的JavaScript,即使用<script>标记包含的脚本。
如果要在浏览器的“地址”栏中执行JavaScript语句,则使用如下格式:
javascript:<JavaScript语句>
这样的格式也可以用在链接中:
<a href="javascript:<JavaScript语句>">...</a>
2.1.2 JavaScript基本语法
每一句JavaScript都有如下类似格式:
<语句>;
其中分号“;”是JavaScript语言的语句结束标识符。虽然现在很多浏览器允许使用回车符,但是使用分号仍然是很好的习惯。
语句块用花括号“{ }”括起,其中可包含多个语句,在花括号外边的语句块被作为一个语句。语句块可以嵌套,即一个语句块中可以包含一个或多个语句块。
1. 变量
从字面上看,变量是可变的量;从编程角度讲,变量是用于存储某种/某些数值的存储器。所存储的值可以是数字、字符或其他内容。
(1)命名变量
命名变量有以下要求。
● 只包含字母、数字和/或下画线。
● 以字母开头。
● 不能过长。
● 不能与JavaScript保留字(Key Words或Reserved Words,凡是可以用来作为JavaScript命令的字都是保留字)重复。
变量区分大小写,如variable和Variable是两个不同的变量。
提示
命名变量最好避免用单个字母如“a”、“b”和“c”等,而使用能清楚表达该变量在程序中作用的词语。这样不仅他人更容易了解程序,而且在以后要修改程序时也很快会记得该变量的作用。变量名一般用小写,如果由多个单词组成,那么第1个单词用小写,其他单词的第1个字母用大写。例如,myVariable和myAnotherVariable。这样做仅仅是为了美观和易读。因为JavaScript的一些命令都使用这种方法命名,如indexOf和charAt等。
(2)声明变量
没有声明的变量不能使用,否则提示“未定义”。声明变量可以用如下语句:
var <变量> [= <值>];
var是一个关键字(即保留字),用于声明变量。最简单的声明方法是“var <变量>;”,这将为<变量>准备内存,并为其赋初始值“null”。如果加上“= <值>”,则为<变量>赋予自定义初始值。
(3)数据类型
变量的数据类型如下。
● 整型
可以是正整数、0和负整数,可以是十进制、八进制和十六进制。八进制数的表示方法是在数字前加“0”,如“0123”表示八进制数 “123”;十六进制则加“0x”,如“0xEF”表示十六进制数“EF”。
● 浮点型
即“实型”。有资料显示,某些平台不能稳定地支持浮点型变量,因此没有需要就不要用该类型。
● 字符串型
用引号“" "”和“' '”包括的零个或多个字符,可用单引号或双引号,用哪种引号开始就用哪种引号结束。单双引号可嵌套使用,如'这里是"JavaScript教程"'。JavaScript中引号的嵌套只能有一层,如果需要多嵌套多层,则使用转义字符。
由于一些字符在屏幕上不能显示,或者JavaScript语法上已经有了特殊用途,所以在使用这些字符时必须添加转义字符。转义字符用斜杠“\”开头,如\' 单引号、\" 双引号、\n换行符和\r回车。使用转义字符可以实现引号的多重嵌套,如'Micro说:"这里是\"JavaScript教程\"。" '。
● 布尔型
常用于判断,只有两个值可选,即true(真)和false(假)。true和false是JavaScript的保留字,属于常数。
● 对象
见2.2节。
由于JavaScript对数据类型的要求不严格,所以一般声明变量时不需要声明类型。而且即使声明了类型,在代码执行过程中还可以为变量赋予其他类型的值。声明类型可以用赋予初始值的方法实现,如:
var aString = '';
这将把aString定义为具有空值的字符串型变量。
var anInteger = 0;
这将把anInteger定义为值为0 的整型。
(4)赋值变量
声明一个变量后可以在任何时候用如下语法为其赋值:
<变量> = <表达式>;
其中“=”为“赋值符”,作用是把右边的值赋给左边的变量。
2. 常数
JavaScript的常数如下。
(1)null
一个特殊的空值。当变量未定义或者定义后未执行赋值操作,则其值为“null”。试图返回一个不存在的对象时也会出现null值。
(2)NaN “Not a Number”
当运算无法返回正确的数值时,会返回“NaN”值。该值非常特殊,因其不是数字,所以任何数均与其不等,甚至NaN本身也不等于NaN。
(3)true
布尔值真。
(4)false
布尔值假。
3. 表达式与运算符
(1)表达式
与数学中的定义相似,指具有一定值并用运算符连接常数和变量的代数式。一个表达式可以只包含一个常数或一个变量。
(2)运算符
运算符包括四则运算符、关系运算符、位运算符、逻辑运算符和复合运算符等,这些运算符及其从高到低的优先级如表2-1所示。
表2-1 运算符及其从高到低的优先级
提示
(1)所有与四则运算有关的运算符都不能用在字符串型变量中,可以使用 +和+= 连接两个字符串。
(2)如果编程时不记得运算符的优先级,可以使用括号( )。例如,(a == 0)||(b ==0)。
由于一些用来赋值的表达式有返回值,所以可加以利用。例如,语句a = b = c =10可以一次为3个变量赋值。
4. 语句
JavaScript的基本编程命令称为“语句”。
(1)注释
在脚本运行时忽略注释语句,JavaScript注释语句包括单行和多行注释语句,单行注释语句用双反斜杠“//”开始,其后部分将被忽略;而多行注释语句是用“/*”和 “*/”括起的一行或多行文本。程序执行到“/*”处,将忽略其后的所有内容,直到出现“*/”为止。
提示
养成写注释的习惯能节省宝贵的开发和维护程序的时间。在程序调试时,有时需要把一段代码换成另一段或者暂时不需要一段代码。这时最忌删除代码,应用中使用注释语句注释暂时不需要的代码。
(2)if语句
其格式如下:
if ( <条件> ) <语句1> [ else <语句2> ];
本语句类似条件表达式“?:”,当<条件>为真时执行<语句1>;否则执行<语句2>。与“?:”不同,if只是一条语句,不会返回数值。<条件>是布尔值,必须用尖括号括起。<语句1>和<语句2>只能是一个语句,多条语句可使用语句块。
看下例:
if (a == 1) if (b == 0) alert(a+b); else alert(a-b);
本例试图用缩进方法说明else对应if (a == 1),但是实际上else与if (b == 0) 对应。正确的代码如下:
if (a == 1) { if (b == 0) alert(a+b); } else { alert(a-b); }
提示
一行代码过长或者涉及比较复杂的嵌套,可以考虑用多行代码。如上例,if (a == 1)后面没有语句,而是换一行继续写。使用缩进也是很好的习惯,当一些语句与上面的一两个语句有从属关系时使用缩进能使程序更易读。
(3)for语句
其格式如下:
for (<变量>=<初始值>; <循环条件>; <变量累加方法>) <语句>;
本语句的作用是重复执行<语句>,直到<循环条件>为false为止。执行过程是首先为<变量>赋<初始值>,然后判断<循环条件>(应该是一个关于变量的条件表达式)是否成立。如果成立,则执行<语句>,即按<变量累加方法>累加<变量>;否则退出循环,称为“for循环”。
看下例:
for (i = 1; i < 10; i++) document.write(i);
本例首先为i赋初始值1,然后执行document.write(i)语句(在文档中写i值)。重复i++,即i加1。循环直到i>=10结束,结果是在文档中输出“123456789”。
<语句>只能是一行语句,如果需要多条语句,则使用语句块。
for循环没有规定循环变量,每次循环一定要加1或减1,<变量累加方法>可以是任意的赋值表达式,如i+=3、i*=2和i-=j等都成立。
提示
适当使用for循环语句可简化HTML文档中大量有规律的重复部分,即使用for循环重复写一些HTML代码达到提高网页下载速度的目的。不过应在Netscape中重复严格测试,保证通过后上传网页,笔者多次试过因为用for循环向文档重复写HTML代码而导致Netscape“猝死”,IE中没有这种情况发生。
(4)while语句
其格式如下:
while (<循环条件>) <语句>;
while语句的作用是当满足<循环条件>时执行<语句>,一般情况下<语句>使用语句块。因为除了要重复执行某些语句之外,还需要一些改变<循环条件>所涉及变量值的语句;否则就会因为条件总是满足而产生“死循环”。
(5)break和continue语句
有时在循环体内需要立即跳出循环或跳过循环体内其余代码而执行下一次循环,break语句放在循环体内,作用是立即跳出循环;continue语句放在循环体内,作用是中止本次循环并执行下一次循环。如果循环的条件已经不符合,则跳出循环。
看下例:
for (i = 1; i < 10; i++) { if (i == 3 || i == 5 || i == 8) continue; document.write(i); }
输出结果为124679。
(6)switch语句
如果要把某些数据分类,如按优、良、中和差分类学生的成绩,则可以使用if语句:
if (score >= 0 && score < 60) { result = 'fail'; } else if (score < 80) { result = 'pass'; } else if (score < 90) { result = 'good'; } else if (score <= 100) { result = 'excellent'; } else { result = 'error'; }
使用过多的if语句导致程序看起来有些乱,使用switch语句是解决这个问题的最好方法:
switch (e) { case r1: ... [break;] case r2: ... [break;] ... [default: ...] }
上述代码计算e的值(e为表达式),然后与下边“case”后的r1、r2……比较。当找到一个相等于e的值时,则执行该“case”后的语句,直到遇到break或switch语句块结束(“}”);如果没有一个值与e匹配,则执行“default:”后边的语句。如果没有default块,switch语句结束。
上述if代码段用switch改写后为:
switch (parseInt(score / 10)) { case 0: case 1: case 2: case 3: case 4: case 5: result = 'fail'; break; case 6: case 7: result = 'pass'; break; case 8: result = 'good'; break; case 9: result = 'excellent'; break; default: if (score == 100) result = 'excellent'; else result = 'error'; }
其中parseInt()方法的作用是取整,最后default段使用if语句是为了不把100 分作为错误处理(parseInt(100 / 10) == 10)。
2.2 对象化编程
JavaScript使用“对象化编程”,即面向对象编程。对象化编程指将JavaScript涉及的范围划分为对象,对象下面继续划分对象,直至详尽为止。所有编程都以对象为出发点。小到一个变量,大到网页文档、窗口,甚至屏幕等都是对象。
2.2.1 对象的基本知识
对象是可以从JavaScript“势力范围”中划分出来的一块内容,可以是一段文字、一幅图片或一个表单(Form)等。每个对象有自己的属性、方法和事件,对象的属性反映该对象的某些特定性质,如字符串长度、图像长宽及文本框(Textbox)中的文本等;对象的方法为该对象可执行的操作,如表单的“提交”(Submit)和窗口的“滚动”(Scrolling)等;对象的事件为该对象可以响应的操作,如提交表单产生表单的“提交事件”和单击链接产生的“单击事件”。有些对象没有事件,有些只有属性。引用对象的任一“性质”使用“<对象名>.<性质名>”方法。
2.2.2 基本对象
1.Number
即数字对象,这个对象用得很少,大多用于变量。
(1)属性
● MAX_VALUE :Number.MAX_VALUE返回“最大值”。
● MIN_VALUE :Number.MIN_VALUE返回“最小值”。
● NaN :Number.NaN或NaN返回“NaN”。
● NEGATIVE_INFINITY :Number.NEGATIVE_INFINITY返回负无穷大,即比最小值还小的值。
● POSITIVE_INFINITY :Number.POSITIVE_INFINITY返回正无穷大,即比最大值还大的值。
(2)方法
toString() 用法为<数值变量>.toString()返回字符串形式的数值。如a == 123,则a.toString() == '123'。
2.String
即字符串对象,声明一个字符串对象最简单、快捷、有效和常用的方法是直接赋值。
(1)属性
length :<字符串对象>.length,返回该字符串的长度。
(2)方法
● charAt() :<字符串对象>.charAt(<位置>),返回该字符串位于第<位置>位的单个字符。字符串中的第1个字符为第0位,第2个为第1位,最后一个字符为第length -1位。
● charCodeAt() :<字符串对象>.charCodeAt(<位置>),返回该字符串位于第<位置>位的单个字符的ASCII码。
● fromCharCode() :String.fromCharCode(a, b, c...),返回一个字符串,其中每个字符的ASCII码由a, b, c... 等来确定。
● indexOf() :<字符串对象>.indexOf(<另一个字符串对象>[, <起始位置>]),在<字符串对象>中查找<另一个字符串对象>(如果给出<起始位置>,则忽略之前的位置)。如果找到,返回其位置;否则返回-1。所有位置都从0开始。
● lastIndexOf() :<字符串对象>.lastIndexOf(<另一个字符串对象>[, <起始位置>])与indexOf() 相似,不过是从后边开始查找的。
● split() :<字符串对象>.split(<分隔符字符>),返回一个数组,该数组从<字符串对象>中分离而来,<分隔符字符>决定了分离处,它本身不会包含在所返回的数组中。如'1&2&345&678'.split('&')返回数组1,2,345,678。
● substring() :<字符串对象>.substring(<始>[, <终>]),返回原字符串的子字符串,该字符串是原字符串从<始>位置到<终>位置的前一位置的一段,< 终> - <始> =返回字符串的长度(length)。如果没有指定<终>或指定了超过字符串长度,则子字符串从<始>位置一直取到原字符串尾;如果指定的位置不能返回字符串,则返回空字符串。
● substr() :<字符串对象>.substr(<始>[, <长>]),返回原字符串的子字符串,该字符串是原字符串从<始>位置开始,长度为<长>的一段。如果没有指定<长>或指定了超过字符串长度,则子字符串从<始>位置一直取到原字符串尾;如果指定的位置不能返回字符串,则返回空字符串。
● toLowerCase() :<字符串对象>.toLowerCase(),返回把原字符串所有大写字母都变成小写的字符串。
● toUpperCase() :<字符串对象>.toUpperCase(),返回把原字符串所有小写字母都变成大写的字符串。
3.Array
数组对象,是一个对象的集合,其中的对象可以是不同类型的。数组的每一个成员对象都有一个下标,用来表示它在数组中的位置(从0开始)。
数组的定义方法如下:
var <数组名> = new Array();
这样定义了一个空数组,添加数组元素的语句如下:
<数组名>[<下标>] = ...;
注意这里的方括号用于括起数组的下标。
如果要在定义数组时直接初始化数据,则使用如下语句:
var <数组名> = new Array(<元素1>, <元素2>, <元素3>...);
例如,var myArray = new Array(1, 4.5, 'Hi'); 定义了一个数组myArray,其中的元素是myArray[0] == 1、myArray[1] == 4.5和myArray[2] == 'Hi'。
如果元素列表中只有一个元素,而且又是一个正整数,则定义一个包含<正整数>个空元素的数组。
提示
JavaScript只有一维数组,千万不要用“Array(3,4)”这种方法来定义4 × 5 的二维数组,或者用“myArray[2,3]”这种方法来返回“二维数组”中的元素。如果使用“myArray[...,3]”这种形式的调用,则返回“myArray[3]”。要使用多维数组,则使用如下虚拟法:
var myArray = new Array(new Array(), new Array(), new Array(), ...);
其实这是一个一维数组,其中的每一个元素又是一个数组,调用这个“二维数组”的元素使用myArray[2][3] = ...语句。
(1)属性
length :<数组对象>.length,返回数组的长度。即数组中的元素数,等于数组中最后一个元素的下标加1,所以添加一个元素只需使用myArray[myArray.length] = ...语句。
(2)方法
● join() :<数组对象>.join(<分隔符>),返回一个字符串。该字符串把数组中的各个元素串起,用<分隔符>置于元素之间。这个方法不影响数组的原值。
● reverse() :<数组对象>.reverse(),使数组中的元素顺序反过来。如果对数组[1, 2, 3]使用这个方法,则为[3, 2, 1]。
● slice() :<数组对象>.slice(<始>[, <终>]),返回一个数组,该数组是原数组的子集。始于<始>,终于<终>。如果未给出<终>,则子集一直取到原数组的结尾。
● sort() :<数组对象>.sort([<方法函数>]),使数组中的元素按照一定的顺序排列。如果未指定<方法函数>,则按字母顺序排列。在这种情况下,8排在9前;如果指定了<方法函数>,则按指定的方法排序。
如按升序排列数字:
function sortMethod(a, b) { return a - b; } myArray.sort(sortMethod);
按降序排列数字则把上面的“a - b”换为“b - a”。
4. Math
数学对象,提供数据的数学计算,在使用时记住“Math.<名>”这种格式。
(1)属性
● E:返回常数e(2.718281828...)。
● LN2:返回2的自然对数(ln 2)。
● LN10:返回10的自然对数(ln 10)。
● LOG2E:返回以2为底的e的对数(log2e)。
● LOG10E:返回以10为底的e的对数(log10e)。
● PI:返回π(3.1415926535...)。
● SQRT1_2:返回1/2的平方根。
● SQRT2:返回2的平方根。
(2)方法
● abs(x):返回 x 的绝对值。
● acos(x):返回 x 的反余弦值(余弦值等于 x 的角度),用弧度表示。
● asin(x):返回 x 的反正弦值。
● atan(x):返回 x 的反正切值。
● atan2(x, y):返回复平面内点(x, y)对应的复数的幅角,用弧度表示,其值为 -π ~ π。
● ceil(x):返回大于等于 x 的最小整数。
● cos(x):返回 x 的余弦。
● exp(x):返回e的 x 次幂(ex)。
● floor(x):返回小于等于 x 的最大整数。
● log(x):返回 x 的自然对数(ln x)。
● max(a, b):返回a, b中较大的数。
● min(a, b):返回a, b中较小的数。
● pow(n, m):返回 n 的 m 次幂(nm)。
● random():返回大于0小于1的一个随机数。
● round(x):返回x 四舍五入后的值。
● sin(x):返回x 的正弦。
● sqrt(x):返回x 的平方根。
● tan(x):返回x 的正切。
5. Date
日期对象,用于保存任意一个日期,范围为0001年-9999年,并且可以精确到毫秒(1/1000秒)。在内部日期对象是一个整数,从1970年1月1日零时整开始计算到日期对象指日期的毫秒数。如果所指日期比1970年早,则是一个负数;如果所有日期时间未指定时区,则采用UTC(世界时)时区,它与GMT(格林尼治时间)在数值上相同。
定义一个日期对象:
var d = new Date;
这个方法使d成为日期对象,并且已有初始值,即当前时间。如果要自定义初始值,可以使用:
var d = new Date(99, 10, 1); //99 年 10 月 1 日 var d = new Date(‘Oct 1, 1999’); //99 年 10 月 1 日
等方法。
以下有很多“g/set[UTC]XXX”这样的方法,表示既有“getXXX”方法,又有“setxxx”方法。“get”获得某个数值,而“set”设定某个数值。如果带有“UTC”字母,则表示获得/设定的数值基于UTC时间;否则基于本地时间或浏览器的默认时间。
如无说明,方法的使用格式为“<对象>.<方法>”。
日期对象的方法如下。
(1)g/set[UTC]FullYear():返回/设置年份,用4位数表示。如果使用“x.set[UTC] FullYear(99)”,则年份被设定为0099 年。
(2)g/set[UTC]Year():返回/设置年份,用两位数表示。设定时浏览器自动加上“19”开头,故使用 “x.set[UTC]Year(00)”把年份设定为1900 年。
(3)g/set[UTC]Month():返回/设置月份。
(4)g/set[UTC]Date():返回/设置日期。
(5)g/set[UTC]Day():返回/设置星期,0表示星期天。
(6)g/set[UTC]Hours():返回/设置小时数,24小时制。
(7)g/set[UTC]Minutes():返回/设置分钟数。
(8)g/set[UTC]Seconds():返回/设置秒钟数。
(9)g/set[UTC]Milliseconds():返回/设置毫秒数。
(10)g/setTime():返回/设置时间,该时间是日期对象从1970 年1 月1 日零时整开始计算到日期对象所指的日期的毫秒数。如果要使某日期对象所指的时间推迟1 小时,则用 “x.setTime(x.getTime() + 60×60×1000);(1小时为60分,1分钟为60秒,1秒钟为1000毫秒)语句。
(11)getTimezoneOffset():返回日期对象采用的时区与格林尼治时间所差的分钟数,在格林尼治东方的时区,该值为负。例如,中国时区(GMT+0800)返回“-480”。
(12)toString():返回一个字符串,描述日期对象所指的日期,这个字符串的格式类似 “Fri Jul 21 15:43:46 UTC+08002000”。
(13)toLocaleString():返回一个字符串,描述日期对象所指的日期,用本地时间表示格式,如“2000-07-21 15:43:46”。
(14)toGMTString():返回一个字符串,描述日期对象所指的日期,用GMT格式。
(15)toUTCString():返回一个字符串,描述日期对象所指的日期,用UTC格式。
(16)parse() 用法:Date.parse(<日期对象>);返回该日期对象的内部表达方式。
6. 全局对象
全局对象从不现形,可以说是虚拟出来的,目的在于把全局函数“对象化”。在Microsoft JScript语言参考中称为“Global对象”,但是引用其方法和属性应直接使用“xxx”,使用“Global.xxx”格式会出错。
(1)属性
NaN如前所述。
(2)方法
● eval():把括号内的字符串作为标准语句或表达式执行。
● isFinite() :如果括号内的数字是有限的(Number.MIN_VALUE~Number.MAX_VALUE),则返回true;否则返回false。
● isNaN():如果括号内的值是NaN,则返回true;否则返回false。
● parseInt():返回把括号内的内容转换为整数之后的值,如果括号内是字符串,则字符串开头的数字部分被转换为整数;如果以字母开头,则返回NaN。
● parseFloat():返回把括号内的字符串转换为浮点数之后的值,字符串开头的数字部分被转换为浮点数;如果以字母开头,则返回NaN。
● toString():<对象>.toString()把对象转换为字符串,如果在括号中指定一个数值,则转换过程中所有数值转换为特定进制。
● escape():返回括号中的字符串经过编码后的新字符串,该编码应用于URL,把空格写成“%20”这种格式。“+”不被编码,如果要编码,则用escape('...', 1)语句。
● unescape() : escape() 的反过程。解编括号中字符串成为一般字符串。
2.3 函数
函数是有返回值的对象或对象的方法。
函数的内部有一行或多行语句,这些语句在其他程序调用它时才执行。在执行一个函数时遇到return语句,函数停止执行,并返回调用它的程序。如果return后带有<值>,则退出函数的同时返回该值。
常见的函数有构造函数(如Array()构造一个数组)、全局函数(即全局对象中的方法)和自定义函数等。
(1)自定义函数
自定义函数使用以下语句:
function函数名([参数集]) { ... [return[ <值>];] ... }
其中不能省略用在function之后和函数结尾的花括号。
函数名与变量名的命名规则相同,即包含字母、数字和下画线。并且以字母开始,不能与保留字重复等。
参数集可有可无,但括号一定要有。
● 参数
参数是函数外部向函数内部传递信息的桥梁,如需要一个函数返回3 的立方,则要让函数知道3这个数值。为此需要有一个变量来接收数值,即参数。
参数集是一个或多个用逗号分隔开的参数集合,如a, b, c。
在函数的内部参数可以直接作为变量来使用,并可以用var语句来新建一些变量,但是这些变量不能被函数外部的过程调用。要使函数内部的数据能被外部调用,则使用“return”返回值或全局变量。
● 全局变量
在Script的“根部”(非函数内部)的var语句定义的变量即全局变量,它能在整个过程的任意处被调用和更改。
看下例:
function addAll(a, b, c) { return a + b + c; } var total = addAll(3, 4, 5);
这个例子建立一个名“addAll”的函数,它的3个参数是a、b和c。作用是返回3个数的相加结果,在函数外部利用“var total = addAll(3, 4, 5);”接收函数的返回值。
函数一般没有返回值,这种函数在一些比较强调严格的语言中叫做“过程”。例如,Basic类语言的“Sub”和Pascal语言的“procedure”。
● 属性
Arguments是一个数组,反映外部程序调用函数时指定的参数,其用法是直接在函数内部调用“arguments”。
(2)自定义构造函数
自定义构造函数使用function,并使用其中的this来定义属性:
function <构造函数名> [(<参数>)] { ... this.<属性名> = <初始值>; ... }
然后使用new构造函数关键字来构造变量:
var <变量名> = new <构造函数名>[(<参数>)];
构造变量后,<变量名>成为一个对象,其属性使用this设定。
以下是一个从网上找到的搜集浏览器详细资料的自定义构造函数的例子:
function Is() { var agent = navigator.userAgent.toLowerCase(); this.major = parseInt(navigator.appVersion); //主版本号 this.minor = parseFloat(navigator.appVersion); //全版本号 this.ns = ((agent.indexOf('mozilla')!=-1) && ((agent.indexOf(‘spoofer’)==-1) && //是否Netscape (agent.indexOf('compatible') == -1))); this.ns2 = (this.ns && (this.major == 3)); //是否Netscape 2 this.ns3 = (this.ns && (this.major == 3)); //是否Netscape 3 this.ns4b = (this.ns && (this.minor < 4.04)); //是否Netscape 4 低版本 this.ns4 = (this.ns && (this.major >= 4)); //是否Netscape 4 高版本 this.ie = (agent.indexOf(“msie”) != -1); //是否IE this.ie3 = (this.ie && (this.major == 2)); //是否IE 3 this.ie4 = (this.ie && (this.major >= 4)); //是否IE 4 this.op3 = (agent.indexOf(“opera”) != -1); //是否Opera 3 this.win = (agent.indexOf(“win”)!=-1); //是否Windows版本 this.mac = (agent.indexOf(“mac”)!=-1); //是否Macintosh版本 this.unix = (agent.indexOf(“x11”)!=-1); //是否Unix版本 } var is = new Is();
这个构造函数完整地搜集浏览器的信息,它为对象定义了多个属性,如major、minor、ns、ie、win和mac等。把is变量定义为Is() 对象后,使用if (is.ns) 这种格式可以方便地知道浏览器的信息。从这个构造函数中也可以看到它使用的是一般的JavaScript语句(例中为var语句)。
再来看一个使用参数的构造函数:
function myFriend(theName, gender, theAge, birthOn, theJob) { this.name = theName; this.isMale = (gender.toLowerCase == 'male'); this.age = theAge; this.birthday = new Date(birthOn); this.job = theJob } var Stephen = new myFriend('Stephen', 'Male', 18, 'Dec 22, 1982', 'Student');
从这个构造函数可以看到参数的用法及不同属性可用不同的数据型(例中的5个属性分别为字符串、布尔值、数字、日期和字符串)和构造函数来“构造”属性。如果使用足够的“保护措施”来避免无限循环,则可以用构造函数自身来构造自己的属性。
(3)无名函数(function表达式)
在JavaScript中存在一种不用指定函数名定义函数的方法,用这种方法定义的函数称为“无名函数”。
在JavaScript中使用无名函数现在已经渐渐变为主流,在学习编写jQuery的插件前一定要掌握这种编程方法。定义无名函数的语法如下:
function(参数系列){函数的具体内容};
实际上只是没有指定函数名,其他与普通函数的定义没有区别。但是因为没有函数名,所以已经不能使用通常的函数调用方法。调用方法如下:
var变量名= function(参数系列){函数的具体内容}; 变量名(参数值);//函数调用
上面将无名函数赋值给变量,事件、数组和对象也可以这样为变量赋值,为函数参数赋值等与通常的函数调用没有区别。
2.3.1 闭包
闭包(closure)是JavaScript语言的一个难点,也是其特色之一,很多高级应用(如jQurey)都要依靠闭包实现。
要理解闭包,首先必须理解JavaScript特殊的变量作用域,变量的作用域包括全局变量和局部变量。JavaScript语言的特殊之处就在于函数内部可以直接读取全局变量,如下例:
var n='test'; function f1(){ alert(n); } f1(); // test
在函数外部无法读取函数内的局部变量,如:
function f1(){ var n='test'; } alert(n); // error
注意函数内部声明变量时一定要使用var命令;否则实际上声明了一个全局变量,如:
function f1(){ n='test'; } f1(); alert(n); // test
出于种种原因,有时需要得到函数内的局部变量。但是必须通过变通方法实现。即在函数的内部再定义一个函数,如:
function f1(){ var n='test'; function f2(){ alert(n); // test } }
在上面的代码中,函数f2被包括在函数f1内部。这时f1内部的所有局部变量对f2都是可见的,但是f2内部的局部变量对f1不可见。这就是JavaScript语言特有的“链式作用域”结构(chain scope),子对象会一级一级地向上查找所有父对象的变量。所以父对象的所有变量对子对象都是可见的,反之则不成立。
既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,即可在f1外部读取其内部变量,如:
function f1(){ var n='test'; function f2(){ alert(n); } return f2; } var result=f1(); result(); //test
上面的f2函数就是闭包。
各种专业文献中有关闭包的定义非常抽象,比较难理解,其实闭包就是能够读取其他函数内部变量的函数。由于在JavaScript语言中只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解为定义在一个函数内部的函数。所以在本质上,闭包是连接函数内部和外部的一座桥梁。
闭包的最大用处有两个,一是前面提到的读取函数内部的变量;二是让这些变量的值始终保存在内存中,实现数据共享。下面再看几个使用无名函数来定义闭包的例子:
var cnt = (function (){ var i=0; return function(){ alert(i); i++; } })(); cnt(); //显示0 cnt(); //显示1 cnt(); //显示2
第1次调用[cnt()]后开始执行无名函数,执行后变量i的值将保存在内存中;第2次调用[cnt()]时将使用内存中保存的i值,因此结果为1。
也可以向闭包内部传入参数,如下:
var cnt = (function (num){ return function(){ alert(num); num++; } })(5); cnt(); //显示5 cnt(); //显示6 cnt(); //显示7
另外,还可以在调用时指定参数值,如:
var cnt = (function (){ var i = 0; return function(num){ num += i; alert(num); num++; } })(); cnt(5); //显示5 cnt(6); //显示7 cnt(7); //显示9
还可以以哈希表的形式定义多个返回的function函数,如:
var cnt = (function (){ var i = 0; return { counter:function(){ i++; this.getValue(); }, getValue: function(){ return alert(i); } } })(); cnt.counter(); //显示1 cnt.getValue(); //显示1 cnt.counter(); //显示2
2.3.2 基于prototype的对象
Prototype对象在JavaScript中实现面向对象编程的主要功能,通常在生成类(class)和继承已有类时使用。
在生成的function对象中使用Prototype对象在生成的function对象中追加内容,称为“基于prototype的对象”,下面是一个简单的例子:
var cnt = function (){}; cnt.prototype = { i : 0; num:function(){ alert(this.i); this.i++; } }; var c = new cnt(); c.num(); //显示0 c.num(); //显示1 c.num(); //显示2
作为高级的JavaScript编程方法,在编写库或框架时会用到闭包和Prototype对象。
2.3.3 with语句和this对象
1. with语句
该语句为一个或一组语句指定默认对象。
用法:
with (<对象>) <语句>;
with语句通常用来缩短特定情形下必须写的代码量,在下例中注意Math的重复使用:
x = Math.cos(3 * Math.PI) + Math.sin(Math.LN10); y = Math.tan(14 * Math.E);
当使用with语句时,代码变得更短且更易读:
with (Math) { x = cos(3 * PI) + sin(LN10); y = tan(14 * E); }
2. this对象
该对象返回当前对象,在不同处this代表不同的对象。如果在JavaScript的主程序,而不在任何function和事件处理程序中使用this,则代表window对象;如果在with语句块中使用this,则代表with所指定的对象;如果在事件处理程序中使用this,则代表发生事件的对象。
一个常用的this用法如下:
<script> ... function check(formObj) { ... } ... </script> <body ...> ... <form ...> ... <input type="text" ... onchange="check(this.form)"> ... </form> ... </body>
这个用法常用于检测表单输入的有效性。
2.4 事件处理
事件处理是对象化编程的一个很重要的环节,没有事件处理,程序就会缺乏灵活性。事件处理的过程即发生事件,然后启动事件处理程序做出反应,必须首先告诉对象可能发生的事件及其处理程序;否则这个流程就不能进行下去。事件处理程序可以是任意JavaScript语句,但是一般用特定的自定义函数(function)来处理事件。
2.4.1 指定事件处理程序
指定事件处理程序有如下3种方法。
(1)直接在HTML标记中指定,这种方法运用最为普遍,格式如下:
<标记 ... ... 事件="事件处理程序" [事件="事件处理程序" ...]>
看下例:
<body ... onload="alert('网页读取完成,请慢慢欣赏!')" onunload="alert(' 再见!')">
这样定义的<body>标记能使读取文档后弹出一个对话框,提示“网页读取完成,请慢慢欣赏!”,并且在用户退出文档(或者关闭窗口或者到另一个页面)时弹出“再见!”。
(2)编写特定对象特定事件的JavaScript,这种方法用得比较少。但是在某些场合很好用,格式如下:
<script language="JavaScript" for="对象" event="事件"> ... (事件处理程序代码) ... </script>
例如:
<script language="JavaScript" for="window" event="onload"> alert('网页读取完成,请慢慢欣赏!'); </script>
(3)在JavaScript中声明,格式如下:
<事件主角 - 对象>.<事件> = <事件处理程序>;
用这种方法要注意 “事件处理程序”是真正的代码,而不是字符串形式的代码。如果事件处理程序是一个自定义函数,如无使用参数的需要,则不要加圆括号 “()”。例如:
... function ignoreError() { return true; } ... window.onerror = ignoreError; // 没有使用“()”
这个例子将ignoreError() 函数定义为window对象的onerror事件的处理程序,作用是忽略该window对象下任何错误(由引用不允许访问的location对象产生的“没有权限”错误不能被忽略)。
2.4.2 常用事件
常用事件如下。
(1)onblur事件:发生在窗口失去焦点时。
应用于window对象。
(2)onchange事件:发生在文本输入区的内容被更改,然后焦点从文本输入区移走之后。捕捉此事件主要用于实时检测输入的有效性,或者立即改变文档内容。
应用于Password、Select、Text和Textarea对象。
(3)onclick事件:发生在对象被单击时。
一个普通按钮对象(Button)通常会有onclick事件处理程序,因为这种对象不能从用户处得到任何信息。为按钮添加onclick事件处理程序可以模拟另一个“提交”按钮的方法是在事件处理程序中更改表单的action、target、encoding和method等一个或多个属性,然后调用表单的submit()方法。
在Link对象的onclick事件处理程序中返回false值,能阻止浏览器打开此链接。如果有一个链接为<a href="http://www.a.com" onclick="return false">Go!</a>,那么无论用户如何单击,都不会打开www.a.com网站;除非用户禁止浏览器运行JavaScript。
应用于Button、Checkbox、Image、Link、Radio、Reset和Submit对象。
(4)onerror事件:发生在错误发生时,其事件处理程序通常叫做“错误处理程序”(Error Handler)。要忽略一切错误,则使用如下代码:
function ignoreError() { return true; } window.onerror = ignoreError;
应用于window对象。
(5)onfocus事件:发生在窗口得到焦点时。
应用于window对象。
(6)onload事件:发生在文档全部下载完毕时,意味着不但HTML文件,而且包含的图片、插件、控件和小程序等全部内容都下载完毕。本事件是window事件,但是在HTML中指定事件处理程序时将写在<body>标记中。
应用于window对象。
(7)onmousedown事件:发生在用户把鼠标放在对象上并按下鼠标键时,参考onmouseup事件。
应用于Button和Link对象。
(8)onmouseout事件:发生在鼠标离开对象时,参考onmouseover事件。
应用于Link对象。
(9)onmouseover事件:发生在鼠标进入对象范围时,这个事件和onmouseout事件加上图片的预读,即可实现当鼠标移到图像链接上更改图像的效果。有时在指向一个链接时状态栏中未显示地址,而显示其他信息,并且看起来这些信息可以随时更改。这个效果的实现如下:
<a href="..." onmouseover="window.status='Click Me Please!'; return true;" onmouseout="window.status=''; return true;">
应用于Link对象。
(10)onmouseup事件:发生在用户把鼠标放在对象上且按下鼠标键,然后放开鼠标键时。如果按下鼠标键时,鼠标并不在放开鼠标的对象上,则不会发生本事件。
应用于Button和Link对象。
(11)onreset事件:发生在单击表单的“重置”按钮时,通过在事件处理程序中返回false值可以阻止表单重置。
应用于Form对象。
(12)onresize事件:发生在调整窗口大小时。
应用于window对象。
(13)onsubmit事件:发生在单击表单中的“提交”按钮时,可以使用该事件来验证表单的有效性,并且通过在事件处理程序中返回false值可以阻止提交表单。
应用于Form对象。
(14)onunload事件:发生在用户退出文档(或者关闭窗口,或者打开另一个页面)时,与onload一样,要放在HTML中的<body>标记中。
有的Web Masters用这个方法来弹出“调查表单”,以“强迫”来访者填写;有的弹出广告窗口,诱使来访者单击链接。
应用于Window对象。
2.5 DOM
W3C已于2000年11月13日推出了DOM level 2规范,DOM(Document Object Model,文档对象模型)是HTML和XML文档的编程接口规范。由于与平台和语言无关,因而可以用多种语言并在多种平台上实现。
该规范定义了HTML和XML文件在内存中的文档结构,提供了访问和存取HTML和XML文件的方法。利用DOM规范,可以实现DOM和XML文档之间的相互转换,并对相应DOM文档的内容执行遍历或其他操作。要想自由地操作XML文件,则会用到DOM规范。
DOM的原理简单地说就是通过解析XML文档,为其在逻辑上建立一个树模型。树的节点是一个个对象,通过存取这些对象即可操作XML文档中的内容。
在jQuery中使用DOM来处理HTML/xHTML文档,提供取得父节点和子节点的函数。
2.6 Ajax
Ajax(Asynchronous JavaScript and XML)用来描述一组技术,使浏览器可以为用户提供更为自然的浏览体验。在Ajax之前,Web站点强制用户进入提交/等待/重新显示范例,用户的动作总是与服务器的“思考时间”同步。Ajax提供与服务器异步通信的能力,从而使用户从请求/响应的循环中解脱出来。借助于Ajax可以在用户单击按钮时使用JavaScript和DHTML立即更新UI,并向服务器发出异步请求,以执行更新或查询数据库。当请求返回时,就可以使用JavaScript和CSS来相应地更新UI,而不是刷新整个页面。最重要的是,用户甚至不知道浏览器正在与服务器通信,Web站点看起来是即时响应的。
虽然Ajax所需的基础架构已经出现了一段时间,但直到Google公司在Google Maps中应用该技术,这种异步请求技术的真正威力才广为网络技术人员所认知。能够拥有一个响应极其灵敏的Web站点确实激动人心,因为它最终允许开发人员和设计人员使用标准的HTML/CSS/JavaScript堆栈创建“桌面风格”(desktop-like)且可用性强的Web应用程序。
根据Ajax提出者Jesse James Garrett的建议,Ajax技术应该包含如下特征。
(1)使用xHTML+CSS来表示信息。
(2)使用JavaScript操作DOM实现动态显示及交互。
(3)使用XML和XSLT执行数据交换及相关操作。
(4)使用XMLHttpRequest对象与Web服务器执行异步数据交换。
(5)使用JavaScript将所有内容绑定在一起。
因此Ajax技术类似DHTML或LAMP,不是指一种单一的技术,而是有机地利用了一系列相关的技术,并且与服务器端应用程序(PHP、Perl、Java和DB等)组合来构建Web应用程序的解决方案总称。