js繼承的實現(es5)

js對面向對象的支持很弱,因此在ES6以前實現繼承會繞比較多的彎(相似於對面向對象支持弱,而後強行拼湊面向對象的特性)
es5中實現繼承的幾種方式,父類定義爲Super
    function Super(name){
      this.name=name;
      this.age=25;
      this.array=[1,2,3];
      this.obj={a:'prop'};
      this.say=function(){
        console.log(this.name);
      }
    }
    Super.prototype.testInherit=function(){
      console.log('I am method of super prototype')
    }
 1.構造函數繼承
簡單的在子類構造函數調用父類構造函數,相似就是直接把父類構造函數執行一遍,屬性拷貝一份過來此種繼承方式致使原型鏈斷了,沒法實現真正意義上的繼承,
child1.testInherit();
這個調用會報錯,由於child1並無在Super的原型鏈上,致使沒法調用其原型的方法;同時由於是拷貝的一份父類的屬性方法,因此子類改動引用類型的屬性並不會影響其餘子類的屬性
    function Child1(name){
        Super.apply(this,arguments);
        this.name=name;
        this.sayName=function(){
            console.log(this.name);
        }
    }
    var parent=new Super('lucy');
    var child1=new Child1('jack');
    var child1=new Child1('jack2');
    console.log(parent,child1);
    console.log(child1.__proto__===Child1.prototype,child1 instanceof Super);//true flase
    child1.array.push(4);
    console.log(child1.array,child2.array,s1.array,);
    child1.testInherit();
2.使用原型鏈繼承
最基本的思想,把子類的原型設置爲父類的實例
   function Child2(name){
        this.name=name;
        this.sayName=function(){
            console.log(this.name);
        }
    }
    var parent=new Super('lucy');
    Child2.prototype=parent;
    var child1=new Child2('jack');
    var child2=new Child2('jack2');
    child1.array.push(4);
    child1.obj.b="prop2";
    console.log(child1.array,child2.array,child1.obj,child2.obj);
    console.log(child1.constructor);
修改一個實例繼承的引用屬性,能夠看到其餘全部實例所繼承的屬性都被修改了(他們引用的都是同一個地址),而且子類實例的構造函數被修改了
前面1和2是繼承的兩種基本模式,也是其餘繼承實現的基礎
 
3.使用組合繼承方式
保證明例繼承屬性私有化而且保證原型鏈不斷
   function Child3(name){
        Super.apply(this,arguments);
        this.name=name;
    }
    var parent=new Super('lucy');
    Child3.prototype=parent;
    var child1=new Child3('jack');
    var child2=new Child3('jack2');
    child1.array.push(5);
    console.log(child1.array,child2.array);
    console.log(child1.constructor);
 此種方式能夠實現繼承能夠保證原型回溯,同時實例繼承的引用類型的屬性互不影響;不過父類的構造函數調用了兩次,子類的實例的構造函數變成了Super,能夠作進一步優化
 
4. 針對上面的組合繼承父類的構造函數調用了2次的改進,能夠將子類的原型直接設置爲父類的原型,以下所示
    function Child4(name){
      Super.apply(this,arguments);
      this.test=44;
    }
    Child4.prototype=Super.prototype;//改進父類構造函數調用兩次問題
    Child4.prototype.constructor=Child4;
    var child1=new Child4('bob');
    var child2=new Child4('bob2');
    console.log(child1.__proto__===Child4.prototype);               
    console.log(child1.__proto__.constructor,'\n',Child4.prototype.constructor,'\n',Super.prototype.constructor); 

    console.log(Super.prototype.constructor); //這種方法改變了父類的構造函數
    for(var itm in Child4.prototype){
      console.log(itm);
    }  
    // 或者使用Object.create()建立一個過渡對象--這樣子類從新定義構造函數,就能使父類和子類各有本身的構造函數
    function Child5(name){
        Super.apply(this,arguments);
        this.test=55;
    }
       
    Child5.prototype=Object.create(Super.prototype);
    Child5.prototype.constructor=Child5;
    Object.defineProperty(Child5.prototype,'constructor',{//Child5的原型是一個實例對象,因此顯示的定義constructor會改變其不可枚舉的特性,這裏修正一下
        enumerable:false
    });        
     var child=new Child5('end');
    console.log(Child5.prototype.constructor,Super.prototype.constructor);
    console.log(child instanceof Child5,child instanceof Super);
    console.log(child.constructor,Child5.prototype.isPrototypeOf(child),Super.prototype.isPrototypeOf(child));         
     for(var itm in Child5.prototype){
        console.log(itm);
    } 
5.組合寄生繼承模式(js高程中推薦的實現)
其實這種模式跟上面的第4-2的實現沒有大的區別,不過上邊的中間對象是Object.create()建立的,這裏是本身建立
    function inheritProperty(sup,child){
        function F(){};
        F.prototype=sup.prototype;
        var inner=new F();
        inner.constructor=child;
        child.prototype=inner; 
        Object.defineProperty(child.prototype,'constructor',{
        enumerable:false
    });
    }
    function Child6(name){
        this.age=66;
        this.name=name;
    }
    inheritProperty(Super,Child6);
    Child6.prototype.sayAge=function(){
        return this.age;
    }
    var child=new Child6('end');
    console.log(child.constructor);
    console.log(Child6.prototype.constructor);
    console.log(child.sayAge());

以上繼承都是以1,2爲基礎的,具體說實現繼承的方式就這兩種,其餘只是對這兩種的組合改造優化app

相關文章
相關標籤/搜索