JavaScript 中雖然有對象的概念,但它並非一門嚴格意義上的面向對象編程的語言。javascript
儘管 ES6 引入了 class 關鍵字,可是本質上仍然是對原型鏈的操做。java
經過修改 JavaScript 的原型,能夠實現類之間的繼承關係。編程
首先用 function 關鍵字定義一個 ParentClasside
1 function ParentClass(props) { 2 this.alpha = props.alpha || 1.0; 3 this.color = props.color || [0.8, 0.8, 0.8]; 4 console.log("ParentClass constructor"); 5 }; 6 7 ParentClass.prototype = { 8 constructor: ParentClass, 9 10 init: function(gl) { 11 console.log("ParentClass.proptotype.init"); 12 }, 13 14 paint: function(gl, addon) { 15 console.log("ParentClass.proptotype.paint"); 16 } 17 }
而且在 ParentClass 的原型上定義了 init 和 paint 函數。注意這裏的構造器仍然是 ParentClass。函數
再用 function 定義 ChildClassthis
1 function ChildClass(props) { 2 ParentClass.call(this, props); 3 this.sides = props.sides || 24; 4 console.log("ChildClass constructor"); 5 }
這裏的 ParentClass 的 call 函數至關於在 ChildClass 繼承了 ParentClass 以後調用 super 函數,也就是調用父類 ParentClass 的構造器。spa
固然這裏尚未讓 ChildClass 繼承 ParentClass,若是調用 ChildClass 的 init 方法就會報錯。prototype
在調用 ChildClass 對象的方法時,首先就會在 ChildClass 的原型上查找是否有同名的方法,若是找不到方法,就會到原型鏈的上一層查找,直到 Object,code
若是這個時候仍然找不到對應名稱的方法,就會報錯了。原型鏈過長的話就會影響運行速度。對象
要作到對象間的繼承,就要修改 ChildClass 的原型鏈,把 ChildClass 的原型鏈指向 ParentClass。
這裏直接給出廖雪峯的繼承代碼
1 /** 2 * this function is copied from liaoxuefeng's javascript tutorial 3 **/ 4 inherits = function(child, parent) { 5 var F = function() {}; 6 F.prototype = parent.prototype; 7 child.prototype = new F(); 8 child.prototype.constructor = child; 9 }
經過一箇中間函數 F,把它的原型鏈指向 parent 的父類,再把子類的原型改成 F 函數 new 出來的對象。child 的構造器固然還應該是 child 本身。
注意這裏的 child 和 parent 應該都是指向 function 的對象(而不是用 new 關鍵字創造的對象)
1 inherits(ChildClass, ParentClass); // ChildClass 繼承 ParentClass
建立子類的對象
1 var child = new ChildClass({});
那麼就會輸出兩行內容,分別是:
ParentClass constructor
ChildClass constructor
再看看子類中相應的屬性或方法:
1 console.log(child.alpha); // 1 2 3 child.paint(); // ParentClass.prototype.paint
默認的 alpha 值是1,paint 繼承於 ParentClass,因此分別輸出 1 和 ParentClass.prototype.paint
要重寫 ChildClass 的 paint 方法,直接修改原型上的 paint 屬性,指向別的函數就行了:
1 ChildClass.prototype.paint = function() { console.log("ChildClass.prototpye.paint")}; 2 child.paint(); // ChildClass.prototype.paint 3 ChildClass.paint; // undefined
再調用 child.paint(),輸出的就是 ChildClass.prototype.paint
關於 ChildClass 原型上的屬性,只有在 new 出了對象後,由對象調用,而 ChildClass 自己是沒有 paint 屬性的。
再看一個例子:
1 ChildClass.testPaint = function() {console.log("ChildClass.testPaint")}; 2 child = new ChildClass({}); 3 child.testPaint // undefined
若是直接給 ChildClass 添加 testPaint 屬性(方法),new 出來的對象不能訪問相應的屬性(方法)。
那麼就能夠這樣理解,ChildClass 原型上的屬性能夠被 new 出來的對象訪問,至關於 Java 中類中的普通方法;
而直接在 ChildClass 上添加屬性,只能被 ChildClass 訪問,而不能被 new 出來的對象訪問,至關於 Java 中類的靜態方法。
另外,我嘗試了下直接將子類(姑且這麼叫吧)的原型直接指向父類的對象中,好像沒有問題:
1 function SecChildClass(props) { 2 ParentClass.call(this, props); 3 this.sides = props.sides || 24; 4 console.log("SecChildClass constructor"); 5 } 6 7 SecChildClass.prototype = new ParentClass({}); // ParentClass constructor 8 SecChildClass.constructor = SecChildClass;
固然在修改 SecChildClass 的原型鏈,指向 ParentClass 的對象時,就會執行一遍 ParentClass 的構造器,因此這個方式確實並很差。
再看看相應對象的屬性
1 var c2 = new SecChildClass({}); 2 // ParentClass constructor 3 // SecChildClass constructor 4 5 c2.alpha; // 1 6 c2.paint(); // ParentClass.prototype.paint
創造對象時就會輸出兩行內容,由於在子類的構造器裏首先調用的是父類的 call 方法,因此首先執行的是父類的構造器。
值得一提的是,若是在子類的構造器中就調用 inherits 函數,傳入 this 是沒有用的,由於 this 指向的是子類的對象而非 ChildClass,這樣的繼承是沒有效果的。
reference