Javascript基本概念
JavaScript是一种基于对象(Object)和事件驱动(Event Driven)并具有安全性能的脚本语言,最初由Netscape公司创造出来,起名Live Script,它和Java的关系只有一个:名字比较像。使用它的目的是与HTML超文本标记语言、Java 脚本语言(Java小程序)一起实现在一个Web页面中链接多个对象,与Web客户交互作用。从而可以开发客户端的应用程序等。它是通过嵌入或调入在标准的HTML语言中实现的。它的出现弥补了HTML语言的缺陷,它是Java与HTML折衷的选择。(注意,如同VBScript一样,JavaScript一样有服务器端版本)
一、JavaScript的基本语法
0、引言
Javascript的语法基本上与Java一致,但是由于Javascript是一个弱类型的脚本语言,在程序编写的过程中会有一些不同。同时由于Javascript是基于对象的语言,注意不是面向对象的语言,所以它在对对象的支持上有一定缺陷,大家所熟悉的诸如对象继承,多态等面向对象语言所具有的基本特性在Javascript中只能通过一些变通手段来实现(通常比较复杂)。然而,弱类型语言也有其优点,那就是简单性,Javascript中类型转化是非常方便的(弱类型语言在代码中基本上表现为无类型),一个String通过一个简单的加减操作就可以转化为Int(相当于调用了Integer.ParseInt(String)),而且不会抛异常。Javascript作为一种解释性语言,还能使用在编译性语言C/C++、JAVA难以支持的eval语句。由于运行在沙箱中,Javascript运行时有很多的安全性限制。它不允许访问本地的硬盘,并不能将数据存入到服务器上,不允许对网络文档进行修改和删除,只能通过浏览器实现信息浏览或动态交互,从而有效地防止数据的丢失。 总体上来说,Javascript应该是优缺点兼备(辨证的说=])。
作为学过JAVA的软院本科生来说,学习Javascript并不困难。Javascript不像HTML、CSS这种经验性很强的的领域,一旦大家入门之后,剩余阶段的Javascript相关的学习很可能就是查阅资料而已。在这里我希望我所写的内容能够起到抛砖引玉的作用,为大家打下基础。以下内容主要是Javascript的入门知识,我会从关键字开始描述,强调关键字是为了让大家对Javascript的语法有比较全面的认识,可能大家在今后的开发中可能一直用不到其中的某些关键字,但我认为大家有必要了解一下,同时请留意其中标出的注意事项。随后将是在页面中加入脚本的四种方法。在“Javascript客户端编程”中,我将提及浏览器文档(DOM)模型和事件(EVENT)模型,其中会有如何寻找对象以及安装事件处理器(事件处理映射)的详细解说。最后我将在“Javascript样例”中给出一段核心代码的注解和三个样例。“Javascript学习资料”中有一些有用的书籍名,电子参考资料和相关网址,请留意其中的推荐参考资料和MLParser的使用指南。大家的对Javascript问题我将会在FAQ中给出解答。
第一次写,错误在所难免,恳请大家指正和谅解。
1、VAR
var i = 5;
var j = "hello world";
var k = document;
for( var u = 0; ... ; ... ) { ... }
function fun() { var v = document; ... }
VAR的作用是声明变量,但是不通过VAR声明的变量也可以使用。
在BLOCK块(用 { 和 } 或 (和 )括起来的的代码段)中用VAR声明的变量是局部变量,一旦出了BLOCK的范围(SCOPE),变量就会失效。例如在样例中的 u 和 v 变量,当程序流分别出了 FOR 和 FUNCTION 语句的范围之后 u 和 v 就成了未定义变量。
其他情况下用VAR声明或者通过未声明直接使用的变量(解释器会隐式声明这些变量)都是全局变量。
在同一个范围(SCOPE)中对同一个变量名不能用一次以上的VAR,即不可重复声明变量。
不同范围(SCOPE)中声明的同名变量在Javascript中会相互隐藏,例如,有一个全局变量 variable,同时在程序段中还有一个局部变量 variable,那么你在程序中引用的变量实际上会是局部变量 variable 。
一个变量在赋值之后,其类型就转化为所赋值的类型。
从未声明过(包括赋值操作引发的隐式声明)的变量值为 undefined 类型也为 undefined 。
变量的有效性是它的定义范围与定义语句出现的顺序无关。
function test(){
document.write(great) ; // print "undefined"
document.write(odd) ; // print "javas" , not "undefined"
var odd = "javas" ;
}
样例中虽然 odd 在 document.write 之后,但在程序被解释时 odd 就被初始化了,所以打印结果不是 "undefined" 而是odd被赋予的初始值。
2、IF-ELSE
if( val > 2.3){
rs = 5.56;
}
else if( val + rs > "1.2") {
rs = document;
}
else{
rs = "Hello world";
}
IF-ELSE的用法与JAVA中的完全相同。
注意表达式中的中的“val + rs > '1.2' "这在JAVA中是不允许出现的。
另外虽然Javascript不要求在每句语句之后加分号,但加上分号是良好的编程习惯。
在样例中出现的未声明就使用的变量的情况在Javascript中是允许的,他们将自动转化为全局变量。
Javascript是大小写敏感的,所以请注意关键字的大小写。
3、SWITCH
switch(key - 65){
case 0:
ch = "A" ;
break;
case 1:
ch = "B" ;
break;
default:
ch = "X" ;
break;
case 9:
ch = "Y" ;
break;
}
SWITCH的用法与JAVA中的完全相同。
CASE后跟的变量建议使用常量表达式(整数和字符串),不要用浮点。
每个CASE结束时的BREAK语句通常不可少,除非想利用SWITCH的FALL-THROUGH来实现特定的功能。
DEFAULT语句可以放在SWITCH结构中的任意位置,可以于CASE语句交叉放置。
4、WHILE
while( i < 0 && bool_var){
if( i > -5)
continue;
i += 3 + i;
}
WHILE的用法与JAVA中的完全相同。
如果是BOOL变量可以不写bool_var == true/false,直接使用即可。
CONTINE语句会使程序流跳过循环中剩余的语句,进入循环的下一次迭代。
在Javascript中也有带LABEL的BREAK和CONTINUE,用法与JAVA相同。
在写循环时,注意不要产生“死”循环。样例程序片断中的就是一个“死”循环。
5、DO-WHILE
do{
i -= 8;
} while( i > 0);
DO-WHILE的用法与JAVA中的完全相同。
不要遗漏结尾WHILE(Expression)之后的分号。
6、FOR
for (var i = 0; i < 8; i++){
document.writeln("Hello world !");
}
DO-WHILE的用法与JAVA中的完全相同。
不要在计数变量 i 之前加 int 类型标识符,Javascript是弱类型语言,加了 int 反倒会报语法错,但是可以用 var 使之成为局部变量。
FOR(... ; ... ; ...)中分号之间的内容都可以空缺(for (;;)相当于while(true)),其中也可以使用多句语句用逗号分隔。
7、FOR-IN
for ( var ite in document) {
str_result += document [ ite ];
}
FOR-IN控制语句在JAVA中不存在,它的作用有点类似JAVA中的 Iterator 接口描述的功能。在样例中,ite将遍历 docment 中所有的可遍历元素(不是所有元素),每次迭代时,ite中会包含被遍历数组或对象的索引字符串(可以看作对象名),例如,textfield(如果你在页面中有一个元素的ID为textfield),或者像数字1、2、3(它们用来引用对象中的未命名元素)。
引用对象中元素时使用关联数组的方式:数组名或对象名 [ 索引值 ],例子中用 document [ ite ] 表示 document 中索引为 ite 的元素。
使用FOR-IN的最大好处就是你不需要知道目标对象或者数组究竟有多少元素,以及其内部结构是怎么样的,就可以遍历其所有可遍历元素。
8、CONTINUE-BREAK
again:
while ( test() ){
whie (is_run) {
if(work()) {
break again;
// continue again;
}
reap();
}
i++;
}
CONTINUE-BREAK的用法与JAVA中的完全相同。
使用带Label的break或者continue可以在内外循环中进行跳转。
9、FUNCTION
function fun_1(arg1, arg2) {
rs = arg1 + arg2;
return rs;
}
FUNCTION在Javascript中的写法与JAVA中的有很大的差异。
首先是参数类型的问题,参数前面不需要加任何类型描述,VAR也不能加。Javascript方法参数也有传值和传引用之分,规则与JAVA基本一致,具体请查阅相关资料。
其次是返回值,返回值的类型不需要标明,RETURN会返回相应的对象,若无RETURN的数据,则返回值为undefined。从这个意义上讲,FUNCTION总是有返回值的。
最后是参数个数的问题,参数列表并不限制实际传入函数的参数个数,它只是提供了一个访问参数的快捷方式,也就是说给了特定位置参数一个特定的名字。
sum = fun_1(1) ;
以上函数调用时只传给 fun_1 一个参数( fun_1 定义时需要两个参数)。那么此时 arg2 的值是什么呢?undefined,你猜对了。
我们可以把 fun_1 改成以下形式来应对这种情况。
function fun_2(arg1, arg2) {
if ( !arg1 ) arg1 = 0;
if ( !arg2 ) arg2 = 0;
rs = arg1 + arg2;
return rs;
}
undefined在布尔表达式中相当于 false 。
好了,问题似乎解决了。可是如果我们要处理更多参数怎么办呢?例如以下函数调用所代表的情况。
sum = fun_2(1, 2, 3) ;
在函数内部有一个Arguments对象,它是一个参数数组,通过它可以访问到传入函数的所有参数。
根据这一特性我们把 fun_2 改成 fun_3。
function fun_3 () {
rs = 0;
for (var i = 0 ; i < Arguments.length; i++) {
rs += parseInt( Arguments[i] );
}
return rs;
}
注意:这里使用了parseInt而不是直接加法引起的隐式转化。这是因为隐式转化的要求过高,而且有可能把 rs 转化为其他内部类型。
0 + "23a" = NaN;0 + parseInt ( "23a" )= 23
function Point ( x, y ) {
this.x = x;
this.y = y;
this.func = m_func;
}
function m_func( num ) { ... }
var newPoint = new Point( 1, 3 );
newPoint.func( newPoint.x + new Point.y);
任何一个函数都可以成为构造函数,在函数中的 this 关键字同JAVA中意义类似,但不完全相同。
通过 new 产生的对象最终会通过垃圾回收机制清除。
函数也是Javascript的内部类型之一,所以可以赋给某个变量,注意不要加 () ,()实际上也是一个操作符表示对函数的调用。
this.func = m_func; 表示把m_func函数赋给 this 的 func 成员变量。
this.func = m_func(); 表示把m_func函数调用的返回值赋给 this 的 func 成员变量。
对象成员访问与JAVA类似:对象名.成员名
为一个类添加新成员,只要给特定的成员名赋值即可(不赋值的话读出来都是 undefined),实际上全局变量或函数也就是顶级对象的成员属性和方法,从这个角度上来思考,大家就很容易理解我在VAR一节中描述的变量声明规则了。
在Javascript中函数既然被视作一个类型,那么函数的声明就会有与普通变量相似的方法:
var my_func = new Function ("arg1", "arg2", ... , "argN", " var rs = arg1 + arg2 + ... + argN; return rs; ");
var my_func = function (arg1, arg2, ... , argN)
{
var rs = arg1 + arg2 + ... + argN;
return rs;
};
前者被称之为构造器法,后者被称之为直接量法。
10、PROTOTYPE
function Point ( x, y ) {
this.x = x;
this.y = y;
// this.func = m_func;
}
Point.prototype.func = m_func;
Point.prototype.s_name = "Point";
function m_func( num ) { ... }
new Point () ;
var newPoint = new Point( 1, 3 );
newPoint.func( newPoint.x + new Point.y);
PROTOTYPE是原型的意思,我改变在第九节中 Point 的实现。把 m_func 赋给了Point的原型。
这一改变唯一的好处就是我们不用在每次调用 Point 函数都对 func 属性赋值了,func 属性被保存在 Point 的原型对象中,从而节省了内存空间。 Point 与 Point.prototype 的关系请查阅相关的资料,这里不再详述。
用PROTOTYPE可以实现JAVA中的静态变量和静态方法(由于某些浏览器实现在对象创建之后才创建它的原型对象,所以建议在使用原型对象中静态成员之前先调用一次构造器方法,如同样例中 new Point();语句,调用结束之后,无用对象将被回收,但其原型对象将继续驻留在内存中),在Javascript支持的有限的对象继承也与PROTOTYPE有一定联系。
11、ARRAY
var arr_8 = new Array(8);
var arr = new Array();
var arr_d = [ 1, 2, , 4 , .., "Hi", null, true, document ];
var arr_m = [ [ 1, 2, 3, 4 ], [ 5, 6, 7], [ 8 ] ];
arr_8[ 3 ] = "ABC";
arr[ 100 ] = 8.8888;
arr_d[ 0 ] = "CDE";
arr_m[ 1 ][ 0 ] = "XYZ";
数组的创建可以通过 new Array 的构造器方法(参数是数组初始长度,空参数表示零长度)。
或者是把[ 数据 , 数据 , ... , 数据]的数组直接量赋给变量,数据之间用逗号分隔,arr_d中蓝色的部分有两个连续的逗号表示第三个元素空缺,其值为 undefined。
构造器方法的样例: arr_8和arr ;数组直接量的样例: arr_d和arr_m 。
Javascript中的数组是动态数组,它将随着元素数量自动调节数组长度。
Javascript中的数组元素没有任何类型限制,未初始化的元素值为 undefined。
Javascript中的多维数组的实现与JAVA中的完全相同。arr_m中 [ 1, 2, 3, 4] 表示 arr_m[0]所指向的第二维数组的数据。
Javascript对数组的访问与JAVA中的完全相同。
var textfield = document.all[ "textfield" ];
document.all 是一个数组吗?不完全是。
那为什么我们可以用 “textfield” 来访问我们的对象呢?
这是因为以上我们所看到的是Javascript中非常特殊的用法――关联数组,也可以称之为索引器。
对象名[ "成员名" ] = 对象名.成员名
关联数组的使用,可以使某些操作从硬编码中解脱出来,使之更具有灵活性。请看下面一个例子。
假如我们在执行某个与对象相关的操作时需要靠外界输出才能确定调用的函数。
方案之一:SWITCH,每更改一个分支就需要更新该方法。
方案之二:对象 + . + 函数名();,语法错误。
方案之三:对象 [ 函数名字符串 ]();,好的。
方案之四:eval(对象名字符串 + "." + 函数名字符串 + "();");,也可以的。
关联数组的使用,使我们能够用字符串,数字或者其他的类型的变量作为索引来访问我们所需要访问的属性和方法。
在FOR-EACH中常常会用这种方法来遍历对象或数组属性和方法。
12、UNDEFINDED-NULL
undefined == null ? true
undefined === null ? false
undefined 表示所引用的对象未经定义。
null表示所引用的对象的值是空值。
在布尔表达式中它的作用基本与null一致,都表示 false。
13、TRY-CATCH-FINALLY-THROW
try{
throw new Error( "Test Exception" ) ;
}
catch( e ){
document.writeln( e.name + ":" + e.message);
}
finally{
document.writeln( "Final here");
}
TRY-CATCH-FINALLY-THROW的用法与JAVA中的完全相同。
这是Javascript 1.5才有的新特性,在早期的浏览器中可能不支持。目前常用的浏览器 IE6、NAV7、Opera、FireFox 1.0 都支持。
14、WITH
function Point ( x, y ) {
this.x = x;
this.y = y;
}
var newPoint = new Point( 1, 3 );
with (newPoint) {
var sum = x + y;
}
WITH的用法与DELPH中的完全相同。
由于使用了WITH,在其作用域中newPoint.x 和 newPoint.y 分别被简化为了 x 和 y 。
15、TYPEOF
swich (typeof obj) {
case "String" :
rs = "string" ;
break;
case "Object" :
rs = "object" ;
break;
case "Number" :
rs = "Number" ;
break;
defualt:
rs = "Unknown Type" ;
}
TYPEOF的作用是返回变量中数据类型对应的字符串。
TYPEOF返回的字符串随着浏览器的不同会有一定的差异。
二、在网页中使用JavaScript
1、链接标记的URL中
<a href = "Javascript: alert('Hi !');" >Click Me </a>
Click Me
这种做法通常只在教学演试中使用。
HREF中的"Javascript : // "的协议头一定要加,其中可以写多句脚本,但不能写 RETURN 语句。
2、HTML标签的事件处理属性中
<a href = "#" onclick = "Javascript: alert('Hello !');return false;">Click Me Too</a>
Click Me Too
这种做法比较简单,比较常用。return false 是为了禁止页面跳转。
通常 "Javascript : // "的协议头可以不加,简写为 onclick = "alert('Hello !');return false;"。
3、页面的SCRIPT标签中
<script language="javascript" type="text/javascript">
<!--//--><![CDATA[//><!--
function testJs(){
alert('Hello !');
...
}
//--><!]]>
</script>
...
<a href = "#" onclick = " testJs();return false;">Click Me Again</a>
Click Me Again
这种做法把脚本与HTML做了一定的分离,代码的整体结构比较良好。
在代码的周围加上<!--//--><![CDATA[//><!-- 和 //--><!]]>是为了避免不支持脚本的浏览器把脚本当作普通文本输出。
与之作用类似的还有<noscript>标签,<noscript>标签在浏览器不支持脚本时显示出其中的提示性文字。
<script>标签通常都放在<head>标签内。
4、外部脚本文件中
[ testJs.js ]
<!--//--><![CDATA[//><!--
function testJsExt(){
alert('Ohhh No!');
...
}
//--><!]]>
[ *.htm ]
<script language="javascript" type="text/javascript" src="mat/js/testJs.js"></script>
...
<a href = "#" onclick ="testJsExt();return false;">Click Me Nowww! </a>
Click Me Nowww !
外部脚本就是把脚本保存在一个单独的 *.js 文件中,通过指定<script>标签的 src 属性,把脚本引入。
效果相当于在原先的<script> 标签中间插入外部文件中的脚本文本。
注意某些浏览器将忽略有SRC属性的<script>标签中的脚本代码。
这种方法从本质上来讲与第三种方法没有差别,但是由于把脚本和HTML做了完全的分离,所以是商业领域最常用的方法。
现在我们在标签<a>中仍然有Javascript的痕迹,在Javascript客户端编程中我将会介绍如何将其去除,以及使Javascript脚本在HTML中留下最少痕迹的手段
新闻热点
疑难解答