JavaScript原型與繼承

來源:互聯網
上載者:User

標籤:個性   init   ==   語言   ext   尋找   概念   ceo   影響   

原型鏈

JavaScript中的每個對象,都內建一個_proto_屬性,這個屬性是編程不可見的,它實際上是對另一個對象或者null的引用。

當一個對象引用一個屬性時,JavaScript引擎會先從此對象自身屬性工作表中尋找,如果找到則進行相應讀寫操作,若沒有在自身的屬性工作表中找到,則在_proto_屬性引用的對象的性表中尋找,如此往複,直到找到這個屬性或者_proto_屬性指向null為止。

這個_proto_的引用鏈,被稱作原型鏈。

注意,此處有一個效能最佳化的問題:往原型鏈越深處搜尋,耗費的時間越多。

 

原型鏈和建構函式

JavaScript是一種物件導向的語言,並且可以進行原型繼承。

JavaScript中的函數有一個屬性prototype,這個prototype屬性是一個對象,它的一個屬性constructor引用該函數自身。即:

 func.prototype.constructor === func; // ==> true  

這個屬性有什麼用呢?我們知道一個,一個函數使用new操作符調用時,會作為建構函式返回一個新的對象。這個對象的_proto_屬性引用其建構函式的prototype屬性。

因此這個就不難理解了:

var obj = new Func();obj.constructor == Func; // ==> true

還有這個:

obj instanceof Func; // ==> true  

也是通過尋找原型鏈上的constructor屬性實現的。

建構函式產生的不同執行個體的_proto_屬性是對同一個prototype對象的引用。所以修改prototype對象會影響所有的執行個體。

“子類”繼承實現的幾種方式

之所以子類要加引號,是因為這裡說“類”的概念是不嚴謹的。JavaScript是一門物件導向的語言,但是它跟Java等語言不同,在ES6標準出爐之前,它是沒有類的定義的。

但是熟悉Java等語言的程式員,也希望使用JavaScript時,跟使用Java相似,通過類產生執行個體,通過子類複用代碼。那麼在ES6之前,怎麼做到像如下代碼一樣使用類似"類"的方式呢?

var parent = new Parent("Sam");var child = new Children("Samson");parent.say(); // ==> "Hello, Sam!"child.say(); // ==> "Hello, Samson! hoo~~"child instanceof Parent; // ==> true 

我們看到,這裡我們把建構函式當做類來用。

以下我們討論一下實現的幾種方式:

最簡單的方式

結合原型鏈的概念,我們很容易就能寫出這樣的代碼:

function Parent(name){    this.name = name;}Parent.prototype.say = function(){    console.log("Hello, " + this.name + "!");}function Children(name){    this.name = name;}Children.prototype = new Parent();Children.prototype.say = function(){    console.log("Hello, " + this.name + "! hoo~~");}

這個方式缺點很明顯:作為子類的建構函式需要依賴一個父類的對象。這個對象中的屬性name根本毫無用處。

第一次改進
// ...Children.prototype = Parent.prototype;// ...

這樣就不會產生無用的父類屬性了。

然而,這樣的話子類和父類的原型就引用了同一個對象,修改子類的prototype也會影響父類的原型。

這時候我們發現:

parent.say(); // ==> "Hello,Sam! hoo~~"

這第一次改進還不如不改。

第二次改進——臨時建構函式/Object.create()
function F(){  // empty  }F.prototype = Parent.prototype;Children.prototype = new F();// ...parent.say(); // ==> "Hello, Sam!"child.say();  // ==> "Hello, Samson! hoo~~"

這樣一來,修改子類的原型只是修改了F的一個執行個體的屬性,並沒有改變Parent.prototype,從而解決了上面的問題。

在ES5的時代,我們還可以直接這樣:

Children.prototype = Object.create(Parent.prototype);

這裡的思路是一樣的,都是讓子類的prototype不直接引用父類prototype。目前的現代瀏覽器幾乎已經添加了對這個方法的支援。(但我們下面會仍以臨時建構函式為基礎)

但是細細思考,這個方案仍有需要最佳化的地方。例如:如何讓父類的建構函式邏輯直接運用到子類中,而不是再重新寫一遍一樣的?這個例子中只有一個name屬性的初始化,那麼假設有很多屬性且邏輯一樣的話,豈不是沒有做到代碼重用?

第三次改進——建構函式方法借用

使用apply/call,實現“方法重用”的思想。

function Children(name){    Parent.apply(this, arguments);    // do other initial things}
“聖杯”模式

現在完整的代碼如下:

function Parent(name){    this.name = name;}Parent.prototype.say = function(){    console.log("Hello, " + this.name + "!");}function Children(name){    Parent.apply(this, arguments);    // do other initial things}function F(){  // empty  }F.prototype = Parent.prototype;Child.prototype = new F();Children.prototype.say = function(){    console.log("Hello, " + this.name + "! hoo~~");}

這就是所謂“聖杯”模式,聽著很高大上吧?

以上就是ES3的時代,我們用來實現原型繼承的一個近似最佳實務。

“聖杯”模式的問題

“聖杯”模式依然存在一個問題:雖然父類和子類執行個體的繼承的prototype對象不是同一個執行個體,但是這兩個prototype對象上面的屬性引用了同樣的對象。

假設我們有:

Parent.prototype.a = { x: 1};// ...

那麼即使是“聖杯”模式下,依然會有這樣的問題:

parent.x // ==> 1child.x  // ==> 1child.x = 2;parent.x // ==>2

問題在於,JavaScript的拷貝不是 深拷貝(deepclone)

要解決這個問題,我們可以利用屬性遞迴遍曆,自己實現一個深拷貝的方法。這個方法在這裡我就不寫了。

ES6

ES6極大的支援了工程化,它的標準讓瀏覽器內部實作類別和類的繼承:

class Parent {    constructor(name) { //建構函式          this.name = name;    }    say() {          console.log("Hello, " + this.name + "!");    }}class Children extends Parent {    constructor(name) { //建構函式        super(name);    //調用父類建構函式        // ...    }    say() {          console.log("Hello, " + this.name + "! hoo~~");    }}

從此走上強型別的不歸路。。。

原文連結:1190000006146779 侵刪

 

JavaScript原型與繼承

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.