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中留下最少痕跡的手段