在ES5繼承的實現非常有趣的,由于沒有傳統面向對象類的概念,javascript利用原型鏈的特性來實現繼承,這其中有很多的屬性指向和需要注意的地方。
原型鏈的特點和實現已經在之前的一篇整理說過了,就是通過將子類構造函數的原型作為父類構造函數的實例(sub.PRototype=new super),這樣就連通了子類-子類原型-父類,原型鏈的特點就是逐層查找,從子類開始一直往上直到所有對象的原型Object.prototype,找到屬性方法之后就會停止查找,所以下層的屬性方法會覆蓋上層。
一個基本的基于原型鏈的繼承過程大概是這樣的:
//先來個父類,帶些屬性function Super(){ this.flag = true;}//為了提高復用性,方法綁定在父類原型屬性上Super.prototype.getFlag = function(){ return this.flag;}//來個子類function Sub(){ this.subFlag = false;}//實現繼承Sub.prototype = new Super;//給子類添加子類特有的方法,注意順序要在繼承之后Sub.prototype.getSubFlag = function(){ return this.subFlag;}//構造實例var es5 = new Sub;原型鏈實現的繼承主要有幾個問題:1、本來我們為了構造函數屬性的封裝私有性,方法的復用性,提倡將屬性聲明在構造函數內,而將方法綁定在原型對象上,但是現在子類的原型是父類的一個實例,自然父類的屬性就變成子類原型的屬性了;這就會帶來一個問題,我們知道構造函數的原型屬性在所有構造的實例中是共享的,所以原型中屬性的改變會反應到所有的實例上,這就違背了我們想要屬性私有化的初衷;2、創建子類的實例時,不能向父類的構造函數傳遞參數function Super(){ this.flag = true;}function Sub(){ this.subFlag = false;}Sub.prototype = new Super;var obj = new Sub();obj.flag = flase; //修改之后,由于是原型上的屬性,之后創建的所有實例都會受到影響var obj_2 = new Sub();console.log(obj.flag) //false;為了解決以上兩個問題,有一個叫借用構造函數的方法只需要在子類構造函數內部使用apply或者call來調用父類的函數即可在實現屬性繼承的同時,又能傳遞參數,又能讓實例不互相影響
function Super(){ this.flag = true;}function Sub(){ Super.call(this) //如果父類可以需要接收參數,這里也可以直接傳遞}var obj = new Sub();obj.flag = flase;var obj_2 = new Sub();console.log(obj.flag) //依然是true,不會相互影響結合借用構造函數和原型鏈的方法,可以實現比較完美的繼承方法,可以稱為組合繼承:function Super(){ this.flag = true;}Super.prototype.getFlag = function(){ return this.flag; //繼承方法}function Sub(){ this.subFlag = flase Super.call(this) //繼承屬性}Sub.prototype = new Super;var obj = new Sub();Sub.prototype.constructor = Sub;Super.prototype.getSubFlag = function(){ return this.flag;}function Box(age) {this.name = ['Lee', 'Jack', 'Hello']this.age = age;}Box.prototype.run = function () {return this.name + this.age;};function Desk(age) {Box.call(this, age); //對象冒充}Desk.prototype = new Box(); //原型鏈繼承var desk = new Desk(100);alert(desk.run());這里還有個小問題,Sub.prototype = new Super; 會導致Sub.prototype的constructor指向Super;然而constructor的定義是要指向原型屬性對應的構造函數的,Sub.prototype是Sub構造函數的原型,所以應該添加一句糾正:Sub.prototype.constructor = Sub;
看完ES5的實現,再來看看ES6的繼承實現方法,其內部其實也是ES5組合繼承的方式,通過call借用構造函數,在A類構造函數中調用相關屬性,再用原型鏈的連接實現方法的繼承
class B extends A { constructor() { return A.call(this); //繼承屬性 }}A.prototype = new B; //繼承方法ES6封裝了class,extends關鍵字來實現繼承,內部的實現原理其實依然是基于上面所講的原型鏈,不過進過一層封裝后,Javascript的繼承得以更加簡潔優雅地實現class ColorPoint extends Point { constructor(x, y, color) { super(x, y); // 等同于parent.constructor(x, y) this.color = color; } toString() { return this.color + ' ' + super.toString(); // 等同于parent.toString() }}通過constructor來定義構造函數,用super調用父類的屬性方法轉至:http://www.jianshu.com/p/342966fdf816
新聞熱點
疑難解答