JavaScript面向對象編程

 

許多Web開發人員對JavaScript的瞭解僅僅停留在簡單的表單數據操作,以及瀏覽器DOM對象的簡單操作上,以達到一些數據驗證和動態頁面的效果。所以當要實現的功能比較複雜時,寫出的代碼就顯得凌亂並且難以維護,更不用說實現一個基於JavaScript的UI框架了。事實上,JavaScript 提供了完善的機制來實現面向對象的開發思想。

本章假設讀者已經瞭解面向對象思想的基本概念,熟悉對象、類、繼承等基本術語。以此爲基礎,將重點介紹如何在 JavaScript 中使用面向對象的思想,包括實現的原理、機制和技巧。我們將使用JavaScript來實現以下面向對象特性:

¾ 對象、類;

¾ 封裝;

¾ 多態;

¾ 繼承。

1.對象

在JavaScript中創建一個對象非常簡單,我們可以使用內建的Object對象來創建一個對象:

var myObj=new Object;    //創建一個名爲myObj的對象

myObj.name='sam';         //給myObj對象添加一個name屬性,其值爲sam

myObj.age=28;             //給myObj對象添加一個age屬性,其值爲28

我們也可以使用JSON(JavaScript Object Notation)[1] 來創建一個對象:

var myObj={name:’sam’,age:28}

//創建一個包含name屬性值爲sam,age屬性值爲28的對象

2.類

JavaScript不同於Java、C++、C#等面嚮對象語言,它通過構造函數和原型對象(prototype)來實現類的創建。爲了創建一個類,還需要創建一個構造函數:

function Person(name,age){

this.name=name;

this.age=age;

}

這樣我們就創建了一個構造函數(類),它包含兩個屬性:name和age。

var sam=new Person('sam',28);   //創建一個Person對象,name爲sam,age爲28

我們可以通過“.”運算符來訪問它的屬性:

alert(sam.name);   //輸出結果爲sam

var bob=new Person('bob',30);   //創建一個Person對象,name爲bob,age爲30

alert(bob.age);   //輸出結果爲30

細心的讀者可能會發現,到目前爲止,我們通過函數創建的對象只是封裝了數據成員,並沒有封裝相應的方法。下面我們將在Person類中添加一個方法:

function Person(name,age){

this.name=name;

this.age=age;

this.introduceSelf=function(){

alert('I am '+name+' , I am '+age +' years old.');

}

}

var sam=new Person('sam',28);

sam.introduceSelf() //輸出結果爲:I am sam,I am 28 years old

var bob=new Person('bob',30);

bob.introduceSelf() //輸出結果爲:I am bob,I am 30 years old

但是上面這種添加方法的方式不是很好。因爲introduceSelf函數對於所有Person的實例來說,都是一樣的,我們不希望在實例級別加上一個對於所有實例來說都一樣的方法。這裏我們要引入原型對象(prototype)的介紹。

每個JavaScript構造函數都有一個內部引用指向另一個稱爲原型對象(prototype)的對象。對於這個構造函數所產生的對象實例,該構造函數的原型對象所有屬性對於它們來說都是可見的,並且是共享的。所以有時候也稱該原型對象是這些實例的原型對象。對於這些實例,對原型對象的屬性的訪問方式和對自身的屬性的訪問方式是一致的。下面我們舉例說明。

首先定義一個構造函數(類):

function Person(name,age){

this.name=name;

this.age=age;

}

然後,在函數的原型對象中添加一個屬性kind:

Person.prototype.kind='animal';

接着創建兩個Person的實例:

var p1=new Person('sam',28);

var p2=new Person('bob',30);

訪問這兩個實例的kind屬性:

alert(p1.kind); //輸出animal

alert(p2.kind); //輸出animal

通過上面的例子,我們可以看到函數的原型對象對於所有實例來說是共享的,並且屬性的訪問方式和實例本身的屬性的訪問方式完全一致。

如果修改這個屬性,會怎麼樣呢?讓我們接着往下看:

p1.kind='male';

alert(p1.kind); //輸出male

alert(p2.kind); //輸出animal

這是怎麼回事呢?原來對於原型對象的操作,讀寫是不對稱的。通過實例對屬性名的引用並不能修改原型對象屬性的值,它只是在實例本身添加了一個和原型對象屬性名一樣的屬性,並將該值賦給自身的那個屬性。而對屬性的訪問,JavaScript首先會從對象本身開始查找,如果找到則返回該屬性的值;如果沒有找到,纔會在其原型對象中查找。所以對於p1,當執行了p1.kind='male'之後,p1本身就有了一個kind屬性,所以當再次訪問p1的kind屬性時,就會直接返回p1本身kind屬性值“male”,而不是其原型對象裏的值“animal”。

在瞭解了原型對象之後,我們再回到原來的例子。我們需要在構造函數Person的原型對象上添加一個方法,這樣這個方法就會被所有的Person對象共享:

Person.prototype. introduceSelf=function(){

alert('I am '+this.name+' , I am '+this.age +' years old.');

}

var p1=new Person('sam',28);

var p2=new Person('bob',30);

p1.introduceSelf() //輸出結果爲:I am sam,I am 28 years old

p2.introduceSelf() //輸出結果爲:I am bob,I am 30 years old

3.多態

JavaScript允許我們將任意一個函數(function)分配給對象的一個屬性。當使用 obj.function 的語法調用函數時,將把函數原來定義this 的指向當前這個對象obj(就像它在構造函數中的那樣)。所以,我們可以通過定義有相同名字的方法的對象,來簡單地實現多態性(polymorphism)。

//定義一個dogSpeek函數

function dogSpeek(){

alert('I am '+this.name);

}

//定義一個Dog類

function Dog(){

this.name='dog';

this.speek= dogSpeek;//將dogSpeek 函數賦給Dog的speek屬性

}

//定義一個catSpeek函數

function catSpeek(){

alert('I am '+this.name);

}

//定義一個Cat類

function Cat(){

this.name='cat';

this.speek= catSpeek; //將catSpeek ()函數賦給Cat的speek屬性

}

var dog=new Dog;

dog.speek();//輸出“I am dog”

var cat=new Cat;

cat.speek();//輸出“I am cat”

對於同一個方法,不同類的對象就展現出不同的行爲,這樣就實現了多態性。

4.繼承

繼承是面向對象開發的又一個重要概念,在JavaScript中通過原型鏈機制來實現類的繼承,當然也可以通過將一個類的prototype拷貝到另外一個類來實現繼承。

function Base(x)                      // 定義一個父類

{

this.x = x;

}

Base.prototype.doIt = function()  //在父類中添加一個方法

{

this.x += 1;

}

function Sub(x,y)               //定義一個子類

{

Base.call(this,x);              // 調用父類的構造函數(非必需)

this.y = y;

}

Sub.prototype = new Base;   // 將Sub的原型對象修改爲Base的實例,這是實現繼承的關鍵一步

//因爲Sub類的原型對象是由構造函數Base產生的,所以它的constructor屬性是Base,

//我們需要把它改成Sub

Sub.prototype.constructor=Sub;

var obj = new  Sub(1,1);

 alert(obj.x);    //輸出1

alert(obj.y);     //輸出1

obj.doIt();   

alert(obj.x);     //輸出2

從上面的例子我們可以看到,Sub類的實例obj繼承了Base類的屬性x,以及方法doIt。

我們還可以通過拷貝父類中的方法來實現繼承。

function Inherit(superclass, subclass) {

var from = superclass.prototype;  // 父類的原型對象

var to = subclass.prototype;       // 子類的原型對象

for(m in from) {  //搜索原型對象中的所有屬性

if (typeof from[m] != "function") continue; // 忽略非函數

to[m] = from[m];  // 拷貝方法

}

}

下面我們用這個函數來實現繼承。

function Base(x){

this.x=x;

}

Base.prototype.doIt=function(){

this.x+=1;

}

function Sub(x,y){

Base.call(this,x);

this.y=y;

}

Inherit(Base,Sub);

var obj = new  Sub(1,1);

 alert(obj.x);    //輸出1

alert(obj.y);     //輸出1

obj.doIt();   

alert(obj.x);     //輸出2

通過上面的方式,同樣實現了繼承。對於第二種方式,說是繼承並不嚴格,因爲它只是借用Base類中的方法,它們之間沒有真正的繼承關係。我們可以使用instanceof方法來證明這一點。

對於第一種方式:

alert(obj instanceof  Base); //輸出true,說明Sub類對象是一個Base類的實例

對於第二種方式:

alert(obj instanceof  Base); //輸出false,說明Sub類對象不是一個Base類的實例

總結:JavaScript不同於Java、C++、C#等基於類的面嚮對象語言,它是基於原型對象的面嚮對象語言。它通過原型對象和構造函數可以很好地實現面向對象的開發,這爲使用JavaScript開發大型的、複雜的程序提供了可能。

 

 

總結:一、封裝   創建對象就可以

         二、 類     創建一個構造函數 通過new可以實例化對象  類的屬性等於一個方法可以定義方法  類的原型屬性定義爲一個方法也可以定義類的方法

        三、 多態  用不同函數賦值給多個類的相同的屬性名上作爲方法 就可以用   類的實例.方法名來調用不同的實際方法實現多態.

         四、 繼承 方法一、子類的原型賦值爲父類的實例對象 ,並且把構造器指向子類;

                        方法二、複製原型 通過一個額外方法轉換原型

       

       

發佈了22 篇原創文章 · 獲贊 6 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章