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

代碼:



/**//*****
*
* 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;
}