JavaScript學習(1):基礎

李勝攀發表於2013-07-06

  這篇文章裡,我們來聊一些JavaScript的基礎知識。

  1. 如何執行JavaScript?

  JavaScript是一種解釋型的語言,它不需要提前編譯。通常情況下,JavaScript會放在網頁中,在瀏覽器中執行。我們也可以找到一些線上的可以執行JavaScript的平臺,當然我們也可以在Firefox或者Chrome裡執行。

  我在網上找到了一個可以線上執行的網站:runjs,文章中的程式碼都是在該網站上執行的。

  實際上,我們可以在本地建立一個簡單的網頁,然後通過編輯網頁的方式來測試JavaScript程式碼。

  下面是程式碼的基本執行結構:

 1 $(document).ready(
 2     function(){
 3         //在這裡呼叫外面的函式
 4         print(add(1,1));
 5     });
 6 
 7 //封裝document.write
 8 function print(obj){
 9     document.write(obj);
10     document.write("<br>");
11 }
12 
13 //簡單的示例方法
14 function add(left, right){
15     return left + right;
16 }

  2. JavaScript基本資料型別

  JavaScript的基本資料型別包括6種:number/string/boolean/object/function/undefined。

  2.1 number型別

  number型別用來儲存數值,它描述的是64位的浮點型數值。但Javascript並不能表示0-2e64之間的所有數值,因為它還需要表示非整數,包括複數、分數等。對於64位來說,需要使用11位來儲存數字的小數部分,使用1位來表示正負,所以JavaScript實際上可以表示-2e52到2e52之間的值。

  2.2 string型別

  string型別用來表示文字,可以使用單引號或者雙引號來包括文字,任何放在引號內的符號,都會被認為是string,但對於特殊符號,可能需要轉義處理。

  2.3 boolean型別

  boolean型別只包括兩個值:true和false。我們可以在程式中使用各種boolean表示式來得到true或者false,從而實現不同的業務分支處理。

  我們可以在表示式中包含多個條件,條件之間可以是與或非的關係,在計算時,優先順序如下:||的優先順序最低,其次是&&,然後是比較運算子,最後是其他運算子(例如!)。

  和其他許多語言一樣,對於&&來說,當前面的條件為false時,後面的條件不再計算,對於||來說,當前面的條件為true時,後面的條件不再計算。

  來看下面的例子:

 1 function conditionTest(){
 2     var a = 1;
 3     var b = 1;
 4     var c = {"key":"old"};
 5     print(c["key"]);
 6     if (a==1) print("a = 1");
 7     if (a==1 && b==1) print("a == 1 && b == 1");
 8     if (a==1 || changeValue(c)) print(c["key"]);
 9     if (a==1 && changeValue(c)) print(c["key"]);
10 }
11 
12 function changeValue(obj){
13     obj["key"] = "changed";
14     return true;
15 }

  它的輸出結果如下:

old
a = 1
a == 1 && b == 1
old
changed

  可以看出,在使用||時,沒有呼叫changeValue方法。

  2.4 undefined型別

  當我們宣告瞭一個變數,但是沒有對其賦值時,它就是undefined的,就像下面這樣

1 var b;
2 print(b);

  在Javascript中,還有一個和undefined類似的值:null。undefined表示“變數已宣告但是沒有複製”,null表示“變數已賦值但為空”,需要注意的是undefined==null的值為true。

  2.5 型別轉換

  我們在上面提到了undefined == null的值是true,但我們使用typeof操作時可以發現,null是object型別,這說明在比較的過程中,發生了型別轉換。

  型別轉換是指將一種型別的值轉換成另外一種型別的值。我們使用==進行比較時,會有型別轉換,我們可以使用===來禁止型別轉換。

  來看下面的例子:

 1 function convertTypeTest(){
 2     var a = 1;
 3     var b = "1";
 4     print ("a:" + a);
 5     print ("b:" + b);
 6     print ("type of a:" + typeof a);
 7     print ("type of b:" + typeof b);
 8     print ("a==b:" + (a == b));
 9     print ("a===b:" + (a === b));
10     print ("a===Number(b):" + (a === Number(b)));
11     print ("String(a)===b:" + (String(a) === b));
12     print ("type of undefined:" + typeof undefined);
13     print ("type of null:" + typeof null);
14     print ("undefined==null:" + (undefined == null));
15     print ("undefined===null:" + (undefined === null));
16 }

  輸出結果如下:

a:1
b:1
type of a:number
type of b:string
a==b:true
a===b:false
a===Number(b):true
String(a)===b:true
type of undefined:undefined
type of null:object
undefined==null:true
undefined===null:false

  可以很明顯看到==和===的區別。

  3. JavaScript基本邏輯流程控制

  和大部分程式語言一樣,JavaScript的邏輯控制基本上也是分為順序、迴圈和條件。

  3.1 順序

  上面給出的各個示例程式碼都是順序執行的。

  3.2 迴圈

  JavaScript使用for、while來完成迴圈,下面是一個從1加到100的例子:

1 function loopTest(){
2     var start = 1;
3     var end = 100;
4     var result = 0;
5     for (i = start; i <= end; i++){
6         result = result + i;
7     }
8     print (result);
9 }

  3.3 條件

  JavaScript使用if、switch來完成條件判斷,下面是一個關於分數分類的例子:

1 function gradeLevel(grade){
2     var level;
3     if (grade <= 100 && grade >= 90) level="優秀";
4     else if (grade >= 80) level = "良好";
5     else if (grade >= 60) level = "及格";
6     else level="不及格";
7     print(level);
8 }

  4. 函式

  函式是JavaScript很重要的一部分,它是模組化的基礎。我們在上面的示例中已經定義了很多函式,接下來我們看一些更有意思的東西。

  4.1 變數的作用域

  在JavaScript中,變數的作用域可以分為全域性作用域和區域性作用域。在函式體內部宣告的變數屬於區域性變數。

  來看下面的例子:

 1 var a = 1;
 2 
 3 function change1(){
 4     a=2;
 5 }
 6 
 7 function change2(){
 8     var a =3;
 9 }
10 
11 function print(obj){
12     document.write(obj);
13     document.write("<br>");
14 }
15 
16 
17 //呼叫順序
18     print(a);
19     change1();
20     print(a);
21     change2();
22     print(a);

  從執行結果可以看出,change1修改了全域性變數a的值,但change2沒有修改,這是因為在change2中定義了一個同名的區域性變數,所以在該方法體內發生的操作只作用在區域性變數上。

  4.2 巢狀函式

  JavaScript允許在函式體內巢狀函式,這樣從某種程度上,可以控制一些變數在某些函式內共享。

  下面是一個實現四則運算的例子:

 1 function operation(left,right,op){
 2     function add(){
 3         return left + right;
 4     }
 5     function minus(){
 6         return left - right;
 7     }
 8     function multiple(){
 9         return left*right;
10     }
11     function divide(){
12         return left/right;
13     }
14     if (op=="+") print (add());
15     else if (op=="-") print (minus());
16     else if (op=="*") print (multiple());
17     else if (op=="/") print (divide());
18     else print ("Invalid operation.");
19 }

  對於示例程式碼,我們可以發現,內嵌函式可以訪問外部函式的引數、全域性變數等。

  在呼叫函式時,通常是沒有辦法直接訪問內嵌函式的。

  4.3 函式呼叫的機制

  JavaScript使用棧和上下文的機制來呼叫函式,我們可以將JavaScript執行時的每一行程式碼的順序看做是上下文,並且使用棧來儲存上下文。

  當我們呼叫一個方法時,在呼叫前,棧會儲存當前上下文,然後呼叫函式,在呼叫結束後,棧會從之前儲存過的上下文開始繼續執行。

  如果上下文增長過快,會導致堆疊溢位,例如當我們在A方法中呼叫B方法,然後在B方法中呼叫A方法,這種迴圈呼叫的方式很容易就將堆疊搞壞了。

  4.4 函式作為值的處理

  在JavaScript中,函式也是一種值,這是和其他語言不太一樣的。

  來看下面的程式碼:

1         var a=false;
2         function test(){
3             print("test");
4         }
5         
6         (a||test)();
7         
8         var b=test;
9         b();

  如果要理解“函式都是值”的意思,我們可以從某種程度上認為b是一個函式指標,它指向了test函式。

  4.5 閉包

  JavaScript中的閉包是指函式的返回值是一個函式,並且返回值函式使用了外部函式的變數(引數或者區域性變數)。

  對於巢狀函式來講,它可以訪問外部函式的引數或者區域性變數,示例如下:

 1         function foo(x){
 2             var y=1;
 3             function bar(z){
 4                 y=y+1;
 5                 print(x+y+z);
 6             }
 7             bar(1);
 8         }
 9 
10         foo(1);

  在這裡,巢狀函式bar訪問了外部函式foo的引數x及區域性變數y,並且對y有加1的操作,但這不叫閉包,我們多次呼叫foo(1),並沒有使得y遞增。

  下面才是閉包:

 1         function foo(x){
 2             var y =1;
 3             return function(z){
 4                 y = y+1;
 5                 print(x+y+z);
 6             }
 7         }
 8         
 9         var bar=foo(1);
10         bar(1);

  我們可以看到上面程式碼中foo的返回值本身就是一個匿名函式,如果我們多次呼叫bar(1),可以發現foo函式的區域性變數y的值是一直在遞增。

  閉包在實際的應用中,一般是用來封裝對複雜資料結構的各種操作,從而對外提供簡單易用的介面,使用者在使用時不用太關心閉包內部的實現細節。

  4.6 函式的可變引數

  JavaScript在呼叫函式時,並不會限制傳入引數的個數,當傳入引數的個數多於函式定義中引數的個數時,多餘的引數會被忽略;當傳入引數的個數小於函式定義中引數的個數時,缺失的引數預設是undefined。

  我們來看下面的例子:

 1 function argumentTest(v1,v2,v3,v4){
 2     print(v1+v2+v3+v4);
 3 }
 4 
 5 //呼叫方法
 6 argumentTest(1,2,3);
 7 argumentTest(1,2,3,4,5);
 8 
 9 
10 //輸出結果
11 NaN
12 10

  可以看到,當我們給出的引數有缺失時,預設的undefined和其他引數做運算時,返回了NaN。當我們給出的引數有富餘時,被自動忽略了。

  在JavaScript中,函式中有預設的一個變數,名為arguments在這裡,arguments並不是一個陣列,typeof arguments返回的是object),它用來儲存所有傳入的引數,我們來看下面的例子:

 1 function argumentTest(v1,v2,v3,v4){
 2     var result=0;
 3     for(var i = 0; i < arguments.length;i++){
 4         result=result+arguments[i];
 5     }
 6     print(result);
 7 }
 8 
 9 //呼叫方法
10 argumentTest(1,2,3);
11 argumentTest(1,2,3,4,5);
12 
13 //執行結果
14 6
15 15

  這裡我們可以看到,無論我們傳入了多少引數,都可以被適當的處理了。

相關文章