Javascript對象繼承

      在Javascript中,一切皆是對象,所謂的類也只是用來模擬其它面嚮對象語言的class的對象而已,例如:

function MyClass() {}//定義一個類
MyClass.call();//其實這個類本身也是一個對象


      在上面的代碼中,並沒有爲MyClass定義call方法,但是卻可以調用call方法,其原因就是JavaScript的原生對象在發揮作用。通過function關鍵字定義一個類,實質上就是實例化一個Function的實例,因爲Javascript在Function的原生對象中定義了call方法,所以即使MyClass的定義是空的,但是還是可以調用call方法!由此,我們就可以看到繼承的影子!
    在原生對象中定義的方法call被繼承到了MyClass中,那麼只要修改一下原生對象,那麼就可以模擬出面向對象的繼承機制了。

    function MyBaseClass() {
        this.Add = function(a, b) {
            return a + b;
        }
    }
    function MyClass() {

    }
    MyClass.prototype = new MyBaseClass();
    var obj = new MyClass();
    var X = obj.Add(3, 4);

只要把MyClass的原生對象指向基類MyBaseClass的實例對象,那麼MyClass就可以繼承MyBaseClass中的方法,從而實現了繼承機制!

多繼承

    任何對象的直接原生對象都只有一個,那麼如果想實現多繼承該怎麼辦呢?可以採用兩種辦法:
方法一:鏈狀繼承

    function MyBaseClassA() {
        this.Add = function(a, b) {
            return a + b;
        }
    }
    function MyBaseClassB() {
        this.Sub = function(a, b) {
            return a - b;
        }
    }
    function MyClass() {
    }
    MyBaseClassB.prototype = new MyBaseClassA();
    MyClass.prototype = new MyBaseClassB();
    var obj = new MyClass();
    var X = obj.Add(3, 4);
    var Y = obj.Sub(9, 4);

方法二:拷貝繼承

    function MyBaseClassA() {
        this.Add = function(a, b) {
            return a + b;
        }
    }
    function MyBaseClassB() {
        this.Sub = function(a, b) {
            return a - b;
        }
    }
    function MyClass() {
    }
    var A = new MyBaseClassA();
    for (var method in A) {
        MyClass.prototype[method] = A[method];
    }
    var B = new MyBaseClassB();
    for (var method in B) {
        MyClass.prototype[method] = B[method];
    }
    var obj = new MyClass();
    var X = obj.Add(3, 4);
    var Y = obj.Sub(9, 4);

繼承的本質就是屬性拷貝,所以只要手動拷貝基類的所有屬性,那麼就可以實現多繼承了,對於拷貝屬性的代碼可以封裝一下,便於調用:

    var Extend = function(DeriveClass,BaseClass) {
        var o = new BaseClass();
        for (var method in o) {
            DeriveClass.prototype[method] = o[method];
        }
    }
    Extend(MyClass, MyBaseClassA);
    Extend(MyClass, MyBaseClassB);
構造參數:

繼承是實現了,但是如果有構造參數有又該怎麼處理呢?答案就是在派生類中調用基類構造就可以了:

function MyBaseClass(a, b) {
        this.A = a;
        this.B = b;
        this.Add = function() {
            return this.A + this.B;
        }
    }
    function MyClass(a,b,c) {
        MyBaseClass.call(this, a, b);//調用基類的構造
    }
    var Extend = function(DeriveClass, BaseClass) {
        var o = new BaseClass();
        for (var method in o) {
            DeriveClass.prototype[method] = o[method];
        }
    }
    Extend(MyClass, MyBaseClass);
    var obj = new MyClass(4,5,8);
    var X = obj.Add();
    alert(X);		//結果:9

     call和apply方法是經常用到的兩個方法,其作用就是在另一個域內調用方法!

    繼承機制帶來的另一個問題就是同名方法隱藏問題!Javascript對象都是由原型對象生成,原型對象也有其自己的原型對象,這樣就構成了一條原型對象鏈,對象鏈的頂端指向Object原生對象,末端就是對象本身,如果對象鏈中存在同名的方法,那麼對象鏈末端的方法訪問優先級高於頂端的優先級,因此派生類的方法總是隱藏基類的方法:

var BaseClass=function(){
	this.Hello=function(){
		return "A";
	}
	this.say=function(){
		return "Saying A";
	}
}
var BaseObj = new BaseClass();
var X = BaseObj.Hello();
alert(X);		//結果:A
BaseClass.prototype.Hello=function(a,b){
		return "B";
	}
var Y = BaseObj.Hello();
alert(Y);		//結果:A
var MyClass = function(){
	this.Hello=function(){
		return "C";
	}
}
var Extend = function(DeriveClass, BaseClass) {
        var o = new BaseClass();
        for (var method in o) {
            DeriveClass.prototype[method] = o[method];
        }
    }
Extend(MyClass, BaseClass);
var MyObj = new MyClass();
var Z = MyObj.Hello();
alert(Z);		//結果:C
MyClass.prototype.Hello=function(){
		return "D";
	}
MyClass.prototype.say=function(){
		return "Saying D";
	}
var K = MyObj.Hello();
alert(K);		//結果:C
var L = MyObj.say();
alert(L);		//結果:Saying D

方法隱藏的唯一準則就是原型對象鏈的順序,方法是否會被隱藏與實現繼承的機制相關!

以後再說一說Javascript面向對象的多態特性!

發佈了34 篇原創文章 · 獲贊 1 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章