1. 程式人生 > >介紹JavaScript面向物件

介紹JavaScript面向物件

JavaScript中的引用型別

引用型別通常叫做類(class),也就是遇到引用值或者處理的物件。但是從嚴格意義上講JavaScript並不是面向物件的,而是基於物件(object)的。之所以說是基於面向物件的原因是JavaScript中並沒有類似Java中的多型,抽象,過載等面嚮物件語言中的概念。但是JavaScript中定義了"物件定義",在邏輯上等價於其他面嚮物件語言中的"類"的概念。 例如我們可以使用下面的程式碼建立Object物件的一個例項:

var obj = new Object() ;

JavaScript中的物件

在JavaScript中的Object物件類似於Java中的java.lang.Object物件,所有的物件都是由這個物件繼承而來,也即是說Object中的所有屬性和方法都會出現在其他物件中。

1 Object 物件的屬性和方法

  • constructor : 對建立物件的函式的引用
  • Prototype : 對該物件的物件原型的引用。對於所有的物件,它預設返回 Object 物件的一個例項。
  • ToString() : 返回物件的原始字串表示
  • ValueOf() : 返回最適合該物件的原始值。對於許多物件,該方法返回的值都與 {{{ToString?() 的返回值相同
  • javascript中定義屬性和方法 在javascript中定義屬性和其他語言類似:如下面的例子:

 

   function person(name){
     this.setName= function(newName){name = newName};
     this.getName= function(){
        return name;
     }
   }
   var p = new person("Lily");
   p.setName("Lucy");
   p.getName();

注: 除了上面的定義方式之外,在js中有一種比較重要的定義物件屬性方法的常見的寫法就是使用JSON物件。定義一個JSON物件,同時定義它的一些屬性和方法。

2 JavaScript中一切都是物件

當我們瞭解了Object類的時候就可以理解為什麼JavaScript中一切都是物件了。我們來看下面的程式碼

<script type="text/javascript">
    function car(){}
    var c = new car() ;
    /*證明c就是Object*/
    alert(c instanceof car) ;
    alert(car instanceof Function) ;
    alert(Function instanceof Object) ;
    /*證明一切都是Object*/
    alert(c instanceof Object) ;
    alert(car instanceof Object) ;
    alert(Function instanceof Object) ;
</script>

上面alert中的內容都是true,那麼也就是我們宣告出來的c就是一個Object,並且所有的一切都是Object。但是這裡就又存在了一個疑問:JavaScript不是基於物件的麼?從上面的例子可以發現類似java中的繼承的概念:c是Object的子類。那這又是怎麼回事呢?

3 JavaScript中內建物件(瞭解即可,這裡就不舉例說明了)

  • Global物件:也即window物件,它沒有construct屬性,所以不能用new進行構造,沒有call屬性,所以也無法像函式一樣進行呼叫
  • Object物件:它是所有物件的基礎,任何其它物件都是從object物件原型擴充套件而來。如果Object物件使用原型擴充套件了其它屬性,那麼所有物件將都具有此擴充套件屬性
  • Function物件:使用function可以定義一個函式,在系統內部進行呼叫
  • Error物件:可以在發生錯誤的時候作為引數傳遞給catch子句,也可以使用new關鍵字構造自定義的error物件

繼承機制的實現

現在我們來說明上面提出的疑問:JavaScript是基於面向物件,但是怎麼存在繼承呢?首先我們知道子類將繼承所有父類的公共的屬性和方法,包括構造和方法的實現。同時子類還可以新增父類中不存在的新的屬性和方法,也可以覆蓋父類中的屬性和方法。

在Java中我們可以使用extends輕鬆的實現繼承,JavaScript並沒有提供。但是我們可以模擬繼承,而且在JavaScript中的繼承並非只有一種形式。

1 call()方法

基本用法

 <script type="text/javascript">
      function sayColor(sPrefix,sSuffix) {
          alert(sPrefix + this.color + sSuffix);  // 輸出The color is blue,a very nice color.
      };

      var obj = new Object();
      obj.color = "blue";

      sayColor.call(obj, "The color is ", ",a very nice color.");
  </script>

說明:

  • 1. call()方法中的第一個引數用作this物件,其他的引數都是直接傳遞給函式自身。(this並不一定是執行該函式時的真正的this值,如果this的值是null或者是undefined,那麼this就會自動指向全域性變數,如果是瀏覽器,那麼當然是window了。此時this會指向原始值的自動包裝物件)
  • 2. sayColor在物件之外定義,顯然它不屬於任何物件,當時也可以使用關鍵字this。因為一個function也是一個物件。(javascript中函式即為物件)
  • 3. call()方法中的第一個引數是obj,也就是在sayColor中的this是obj。所以sayColor中的this.color就是blue。
  • 4. 另外的兩個引數就是sayColor中的sPrefix和sSuffix。

使用繼承

0.  <script type="text/javascript">
1.      function ClassA(sColor) {
2.          this.color = sColor;
3.          console.log(this) ;     
4.          this.sayColor = function () {
5.              alert(this.color);
6.          };
7.      }

8.      function ClassB(sColor, sName) {
9.           ClassA.call(this, sColor);

10.          this.name = sName;
11.          this.sayName = function () {
              alert(this.name);
12.          };
13.     }
      
14.      var objA = new ClassA("blue");
15.      var objB = new ClassB("red", "John");
16.      objA.sayColor();	//輸出 "blue"
17.      objB.sayColor();	//輸出 "red"
18.      objB.sayName();		//輸出 "John"
19.  </script>

說明:

  • 1. objB本身並沒有sayColor函式,這是因為通過call方式繼承而來。在其中第9行的this是ClassB,第3行的this也是ClassB,所以第2行的this.colo就是ClassB中的 color,即red。這一特點和繼承相符合:子類繼承父類的方法,並擁有父類的所有屬性。
  • 2.ClassB同時可以擁有新的屬性和方法。在第10,11行。
  • 3.call相當於連線另一個物件的構造方法,類似於java中的super關鍵字的含義,在這裡,相當於是在ClassB作用域下呼叫ClassA函式,很好的體現了繼承。(call還能實現多繼承)

 

2 prototype

在JavaScript中的繼承是基於原型鏈的。看下面的例子

   <script type="text/javascript">
   1   function ClassA() {
   2   }
   3
   4   ClassA.prototype.color = "blue";
   5   ClassA.prototype.sayColor = function () {
   6       alert(this.color);
   7   };
   8
   9   function ClassB() {
   10  }
   11
   12   ClassB.prototype = new ClassA();
   13   ClassB.prototype.name = "";
   14   ClassB.prototype.sayName = function () {
   15       alert(this.name);
   16   };
   17   var objA = new ClassA();
   18   var objB = new ClassB();
   19   objA.color = "blue";
   20   objB.color = "red";
   21   objB.name = "John";
   22   objA.sayColor();
   23   objB.sayColor();
   24   objB.sayName();
   </script>

說明:

  • 1.和上面的例子不同的是,第一行ClassA的建構函式中沒有引數,這個是使用原型鏈的標準。此外在函式體中沒有定義屬性和方法,都是在CalssA的建構函式之外通過ClassA.prototye.Xxx的方式定義的。
  • 2. 第12行就是繼承的關鍵,我們需要使用ClassA中的全部屬性和方法。當然我們可以通過ClassB.prototye.Xxx的方式逐個定義,使其相等。但是我們可以使用ClassB.prototype = new Xxx()的方式,將ClassA的例項賦予給ClassB,這樣就更好了。第20和23也證明了ClassB繼承了ClassA的屬性和方法。
  • 3.我們從第一行中可以發現ClassA是一個Function,但是同時Function也是一個Object,而之前講過,Object物件的屬性:Prototype預設返回Object物件的一個例項。也就是說prototype實際上是Object的屬性。
  • 4.從4,5行可以看出我們既可以使用prototype定義屬性也可以定義方法,而且完全取決於你自己。因為prototype返回一個Object例項,自然就可以新增任意你喜歡的屬性和方法了。
  • 5.類的例項可以通過"."的方式訪問prototype定義過的屬性方法,第19 - 24行

javaScript物件體系結構

javascript物件體系結構圖

JavaScript中的this

  • this是javascript中理解類和繼承的重要基礎。this屬性代表當前物件。如果用在全域性變數中,那麼它代表當前頁面物件window,我們可以通過使用call和apply方法來改變this指標,(注意:this指標是在函式呼叫時改變的,並不是函式定義時)如下面的例子:
    function hello(){
        alert(this.say);
    }
    var say = "你好";
    hello();
    var test = {
        say:"歡迎訪問",
        hello:hello
    }
    test.hello();
  • 1.開始時執行hello方法是全域性的,相當於window.hello(),輸出的是"你好",在下面呼叫的時候this指向了當前物件test,所以輸出的是"歡迎訪問"。
  • 2.在函式定義時,this指向的是window物件,在第一次執行的時候this指標不變,但是在第二次執行的時候this指標變成了test物件。this指標發生了變化。
  • this指標可以是全域性物件、當前物件或者任意物件,這個要取決於函式的呼叫方式:在javascript中,函式有幾種不同的呼叫方式:作為物件方法呼叫、作為函式呼叫、作為建構函式呼叫、使用apply和call進行呼叫。以下分別是這幾種呼叫方式下的this指標:
    • 1.作為函式物件呼叫

由於函式也是物件,因此函式也可以作為物件的一個屬性,此時函式就是該物件的方法,當使用該物件呼叫這個方法時,this當然指的是該物件 例子:

       var hello = {
        say:"welcome",
        test:function(name){
           this.say = name + " say:" + this.say;
        }
       };
     hello.test("jack");

此時的this就被繫結到hello物件中

  • 2.作為函式呼叫

在函式體內也可以直接呼叫,此時的this指的是全域性物件window。例子:

     var say = "welcome";
     function hello(say){
       this.say = say;
     }

此時的this指的是全域性物件。

  • 3.作為建構函式呼叫
         Hello.js
         function Hello(say){
           this.say = say
         }
    

this被繫結到新建立的物件上面。

  • 4.使用call或者apply呼叫(同上面的call方法中的舉例)

JavaScript匿名函式

1 定義一個函式方式

首先需要說明的是在JavaScript中定義一個函式一般存在如下三種方式:

  • 1. 使用關鍵字function
       funtion sayHello(){alert("hello"); }
    

這種方式定義的函式,函式宣告在整個程式執行之前的預處理就完成了。只要同處於一個作用域就可以訪問到。

  • 2. 使用Function類
       var myFunction = new Function("x","alert(x)") ;
    

Function類中前面的引數就是在最後一個引數中需要傳遞的引數,也就是最後一個引數是函式的主體。使用Function類定義的函式中的引數必須都是字串。

  • 3. 使用匿名物件
     var myFunction = function(x){alert(x) ;}
    

這種方式定義的函式,只能按照程式流程執行到定義的那一行才可以被宣告,所以只能在定義之後才可以呼叫。

2 變數的作用域

變數的作用域無非就是兩種:全域性變數和區域性變數。 在JavaScript中支援在函式的內部直接訪問全域性變數,同時在函式的外部自然就無法訪問到函式內的區域性變量了。 另外需要說明的是在函式的內部宣告變數時候,一定要使用var關鍵字,否則的話,實際上你聲明瞭一個全域性的變數!!

3 示例

<html>
    <head>
        <title>匿名物件</title>
    </head>
    <body>
        <script type="text/javascript">
            // 測試函式作用域
            var NUM = 999 ;       // 全域性變數
            function testNum(){
                alert(NUM) ;        
                var count = 100     //  區域性變數
                TEST_NUM = 1 ;      //  全域性變數
            }
            testNum() ;         //  999
            alert(count) ;      // error
            alert(TEST_NUM) ;   // 1  
            
            // 測試匿名函式
            var testFunction;
            testFunction();          //error
            testFunction=function(){     
                sayHello();          //sayHello works
                alert("testFunction works");
                
                return false;       // 雖然已經return,說明程式流程並沒有執行,但是sayHello已經在預處理中處理了
                
                function sayHello(){
                    alert("hello");
                }
            }
            testFunction();        // 先輸出hello , 再輸出testFunction works , 這是因為sayHello程式的執行流程之前被呼叫。
        </script>
    </body>
</html>

4 匿名函式和this結合

<html>
    <head>
        <title>匿名物件</title>
    </head>
    <body>
        <script type="text/javascript">
            // 不使用this關鍵字
            objA={
                Calculate : function(num){
                    return num > 2 ? objA.Calculate(num-1) : num ;   // 將objA替換成this即可
                }
            }
            objB={
                Calculate : objA.Calculate 
            }
            alert(objB.Calculate(5)) ;  // 2
            objA = {} ;                 // 將objA清空
            alert(objB.Calculate(5)) ;  // error
        </script>
    </body>
</html>

說明:在上面的程式碼中objA中定義了一個遞迴,直到num的值為2。然後將objA = {}進行了清空,那麼下面的輸出就會出現錯誤了。

這個時候我們的this就可以派上用場了。將遞迴函式中的objA替換成this,這樣的話將objA = {} 清空,但是並不會將objB進行清空。

JavaScript閉包

閉包在js中是一個難點,本人也只是懂得其中的一點基礎,希望以下的介紹能夠對初學者起到入門的作用

1變數的作用域

要理解閉包的概念,首先應該理解一下變數的作用域,它分為兩種:全域性變數和區域性變數,在js中,函式內部可以直接訪問函式外部的變數,例如:

     var say = "hello";
     function test(){
        alert(say);
     }
     test();

當然外部不能訪問內部的變數,如下的訪問時錯誤的:

     
     function test(){
        var say = "hello";
       // 
     }
     alert(say);

注意:在函式內部定義變數的時候需要加var,否則預設的宣告的是全域性變數

2.從外部獲取區域性變數

上面說了,在一般情況下是不能訪問的,但是如果改變一下思路,這個問題就會迎刃而解了,就是在函式內部定義函式:

    function test1(){
       var say = "hello";
       function test2(){
         alert(say);
       }
       return test2;
    }
     var test = test1();
     test();

以上就是在函式test1的內部定義了函式test2,在test2中訪問區域性變數。這樣就在函式外部訪問到了區域性變數。

3.閉包的概念

閉包,通俗的講,其實就是能夠訪問其他函式內部變數的函式

4.閉包的用途

閉包主要有兩大用途:一個是前面提到的可以訪問函式內部的變數,二是讓這些變數的值始終儲存在記憶體中。

    function test1(){
    var say = 1;
    add = function(){
          say += 1;
        }
    function test2(){
      alert(say);
    }
    return test2;
  }
  var result=test1();
  result(); 
  add();
  result(); 

以上函式中,result實際上就是一個閉包函式,它一共執行了兩次,這說明test1的變數say一直在記憶體中。並沒有在呼叫test1以後自動清除。這裡面有一個add方法,因為在定義它的時候前面沒有var關鍵字,所以它是一個全域性變數,並且它又是一個匿名函式,它實際上也是一個閉包,所以能夠改變區域性變數的值。

5.閉包的執行機制,你理解了嗎?看下面兩個例子:

      var name = "Lily";
      var test = {
          name = "Lucy";
          getName:function(){
               return function(){
                         return this.name;
                      }
          }
      };
      console.log(test.getName()());

輸出 "Lily";

      var name = "Lily";
      var test = {
          name = "Lucy";
          getName:function(){
               var Dthis = this;
               return function(){
                         return Dthis.name;
                      }
          }
      };
      console.log(test.getName()());

輸出 "Lucy". 
還有幾個閉包的例子,看這裡​http://www.cnblogs.com/zhangle/archive/2010/07/02/1770206.html

JavaScript指令碼化Java

1什麼是指令碼化

指令碼化可以使宿主程式具有指令碼所描述的能力,比如JavaScript技術,JavaScript可以讓原本是靜態的HTML程式碼的頁面變活、具有區域性重新整理等更高階的功能。應用程式一般是以二進位制的形式釋出的,使用者很難根據自己的需求對其進行定製。指令碼化是通過使用者自己設計指令碼,然後將其注入到應用中,使得應用的行為得到改變。

2看這裡簡單的例子

​http://han2000lei.iteye.com/blog/353253