意譯自KevLinDev上一篇文章,才疏學淺,望指正,英文原文可到這里查看。
javascript腳本語言是支持面向對象編程(Object Oriented Programming )的,只是javascript實現的方式比較特別,與C++和java中的實現方式不同。在javascript中我們需要借助prototype對象來訪問父類的方法,下面將討論在javascript中實現OOP中最基本的特征關系 --- 繼承。
首先,我們從最基本的開始:在javascript中創建對象。創建一個對象包括2步:
1) 創建一個和你想要創建的對象同名的函數(這里可以把這個函數理解為java中的構造函數);
2) 通過調用 new 上面創建的函數名 的方式創建一個對象實例;
// 創建同名函數
function Person(first, last) {
this.first = first;
this.last = last;
}
// 創建實例
var person = new Person("John", "Dough");
注意:構造函數中的this指針指向當前的創建的對象,這與java中的表示是一致的。通過this指針我們可以操作對象的屬性。
然后,設置對象的方法:
Person.prototype.toString() {
return this.first + " " + this.last;
}
alert( person.toString() ); // displays "John Dough"
alert( person ); // alert函數將默認調用toString()函數
在這里我們用到了javascript中的prototype屬性。所有的javascript對象都擁有一個prototype屬性,javascript就是通過這個屬性來實現繼承關系的。具體的實現機制是這樣的:當你訪問一個對象的屬性時,編譯器將先查找對象的屬性看有沒有匹配的。如果沒有找到,將繼續查找這個對象的prototype屬性所指的對象,看是否存在匹配的屬性。如果還是沒有找到,編譯器將檢查當前對象的prototype是否擁有prototype屬性,如果有將繼續查找下去。以此類推,直到查找完所有的prototype屬性。
從這個過程不難看出,存在著一個類似于繼承的訪問鏈,可以把prototype對象指向我們要繼承的父類,以此來訪問父類的方法。
接下來,我們新建一個類Employee來繼承上面的Person類,并且新增一個屬性id,在Employee的構造函數中只是初始化屬性id,而父類中的屬性將由Person的構造函數類設置:
function Person(first, last) {
if ( arguments.length > 0 )
this.init(first, last);
}
Person.prototype.init = function(first, last) {
this.first = first;
this.last = last;
}
Employee.prototype = new Person();
Employee.prototype.constructor = Employee;
Employee.superclass = Person.prototype;
function Employee(first, last, id) {
if ( arguments.length > 0 )
this.init(first, last, id);
}
Employee.prototype.init = function(first, last, id) {
// Call superclass method
Employee.superclass.init.call(this, first, last);
// init properties
this.id = id;
}
可以看到,我們把初始化屬性的操作提取出來放到了一個init函數中,這樣做是為了方便在子類中調用。注意其中的"Employee.superclass = Person.prototype;",這里是一個技巧,方便下面在子類中調用父類的方法。
另一個需要注意的地方是"Employee.superclass.init.call(this, first, last);":對于所有對象中的方法來說,都可以通過兩個方法來調用--"call" 和 "apply".
這里使用了call方法,其中第一個參數是將在調用的方法中訪問的對象,后面的參數與調用方法的參數一致。
apply方法的使用與call大致一致,不同在于除了第一個參數外,后面是一個參數數組。
下面附上完整的例子和類圖:
繼承關系圖如下:

代碼:



/**//*****
*
* Person constructor
*
*****/

function Person(first, last)
{
if ( arguments.length > 0 )
this.init(first, last);
}


/**//*****
*
* Person init
*
*****/

Person.prototype.init = function(first, last)
{
this.first = first;
this.last = last;
};


/**//*****
*
* Person toString
*
*****/

Person.prototype.toString = function()
{
return this.first + "," + this.last;
};



/**//*****
*
* Setup Employee inheritance
*
*****/
Employee.prototype = new Person();
Employee.prototype.constructor = Employee;
Employee.superclass = Person.prototype;


/**//*****
*
* Employee constructor
*
*****/

function Employee(first, last, id)
{
if ( arguments.length > 0 )
this.init(first, last, id);
}


/**//*****
*
* Employee init
*
*****/

Employee.prototype.init = function(first, last, id)
{
// Call superclass method
Employee.superclass.init.call(this, first, last);

// init properties
this.id = id;
}


/**//*****
*
* Employee toString
*
*****/

Employee.prototype.toString = function()
{
var name = Employee.superclass.toString.call(this);

return this.id + ":" + name;
};



/**//*****
*
* Setup Manager inheritance
*
*****/
Manager.prototype = new Employee;
Manager.prototype.constructor = Manager;
Manager.superclass = Employee.prototype;


/**//*****
*
* Manager constructor
*
*****/

function Manager(first, last, id, department)
{
if ( arguments.length > 0 )
this.init(first, last, id, department);
}


/**//*****
*
* Manager init
*
*****/

Manager.prototype.init = function(first, last, id, department)
{
// Call superclass method
Manager.superclass.init.call(this, first, last, id);

// init properties
this.department = department;
}


/**//*****
*
* Manager toString
*
*****/

Manager.prototype.toString = function()
{
var employee = Manager.superclass.toString.call(this);

return employee + " manages " + this.department;
}