在所有面向對象的編程中,繼承是一個重要的話題。一般說來,在設計類的時候,我們希望能減少重復性的代碼,并且盡量弱化對象間的耦合(讓一個類繼承另一個類可能會導致二者產生強耦合)。關于“解耦”是程序設計中另一個重要的話題,本篇重點來看看在javascript如何實現繼承。
其它的面向對象程序設計語言都是通過關鍵字來解決繼承的問題(比如extend或inherit等方式)。但是javascript中并沒有定義這種實現的機制,如果一個類需要繼承另一個類,這個繼承過程需要程序員自己通過編碼來實現。
一、類式繼承的實現
1、創建一個類的方式:
//定義類的構造函數function Person(name) { this.name = name || '默認姓名';}//定義該類所有實例的公共方法Person.prototype.getName = function() { return this.name;}var smith = new Person('Smith');var jacky = new Person('Jacky');console.log( smith.getName(), jacky.getName() ); //Smith Jacky2、繼承這個類:
這需要分兩個步驟來實現,第1步是繼承父類構造函數中定義的屬性,第2步是繼承父類的prototype屬性
//定義類的構造函數function Person(name) { this.name = name || '默認姓名';}//定義該類所有實例的公共方法Person.prototype.getName = function() { return this.name;}function Author(name, books) { //繼承父類構造函數中定義的屬性 //通過改變父類構造函數的執行上下文來繼承 Person.call(this, name); this.books = books;}//繼承父類對應的方法Author.prototype = new Person(); //Author.prototype.constructor === PersonAuthor.prototype.constructor = Author; //修正修改原型鏈時造成的constructor丟失Author.prototype.getBooks = function() { return this.books;};//測試var smith = new Person('Smith');var jacky = new Author('Jacky', ['BookA', 'BookB']);console.log(smith.getName()); //Smithconsole.log(jacky.getName()); //Jackyconsole.log(jacky.getBooks().join(', ')); //BookA, BookBconsole.log(smith.getBooks().join(', ')); //Uncaught TypeError: smith.getBooks is not a function從測試的結果中可以看出,Author正確繼承了Person,而且修改Author的原型時,并不會對Person產生影響。這其中的關鍵一句就是 Author.prototype = new Person(),要與Author.prototype = Person.prototype區分開來。前者產生了一個實例,這個實例有Person.prototype的副本(這里先這么理解,后面有更詳細的解析)。后者是指將兩者的prototype指向同一個原型對象。
那么,這也意味著每次繼承都將產生一個父類的副本,肯定對內存產生消耗,但為了類式繼承這個內存開銷必須得支付,但還可以做得更節省一點:Author.prototype = new Person()這一句其實多執行了構造函數一次(而這一次其實只需在子類構造函數中執行即可),尤其是在父類的構造函數很龐大時很耗時和內存。修改一下繼承的方式,如下:
新聞熱點
疑難解答
圖片精選