1. 程式人生 > >JavaScript面向物件原型鏈繼承詳解

JavaScript面向物件原型鏈繼承詳解

<html>
    <head>
        <meta charset="UTF-8"/>
        <title></title>
        <script type="text/javascript">
            //目標 以最高效能完成屬性和方法的繼承 建議拷貝到編輯器看 黑白看的實在是難受
            //本文是對JavaScript高階程式設計面向物件的一個小總結 請先看一看書 適合對JavaScript面向物件有一定理解的人看
            //如果有誤區麻煩大牛一定要指出來 不希望誤人!
            //必須看懂我說的每一種方法  不要跳躍!即使你會希望你也不要跳躍 順便幫我看看哪裡說的不對
            
            //原型繼承
            //這繼承方法雖然屬性和方法都繼承了 但是一身bug 修改一個引用型別的值所有的引用型別值(不知道的話去百度)都跟著變了
            //順便你試試SayName方法 他好使嗎 為什麼呢?
            //不賣關子了 對於諸位和我一樣的初學者來說 我們可以這麼理解:
            //Person.prototype.方法名        它是向原型鏈上新增一個方法
            //而Person.prototype=new 建構函式名();會對Person原型鏈進行破壞性的重寫(也就是我們說的繼承)可以理解為重灌一個乾淨的系統 裡面僅有我們繼承來的方法
            //那麼以前定義的SayName方法呢?當然是消失啦 一定要謹慎定義方法 一定要在重寫原型鏈之後 否則用的時候就沒了
            //但是非引用型別的值不會(修改age試試)面試可能會考引用型別 PS:順便說一句:基礎一定打好; 
            //此方法不大好 也不常用 看懂即可 我們往下看
            (function(){
                function Person(){
                    this.name='jack'
                }
                
                Person.prototype.SayName=function(){
                    alert(this.name);
                }
                
                function More(){
                    this.age=18,
                    this.color=['yellow','blue','red']
                }
                
                More.prototype.SayAge=function(){
                    alert(this.age);
                }
                
                Person.prototype=new More();
                //測試資料:取消註釋即可
//                var instance1=new Person();
//                instance1.color.push('green');
//                alert(instance1.color);

//                var instance2=new Person();
//                alert(instance2.color);
            }());
            
            //借用建構函式
            //嗯 下面利用函式我們得目標完成了 屬性的繼承成功了,但是方法怎麼辦 也寫進函式裡??No!違背了公用方法的本意
            //如果我們方法如果寫進函式裡,由於函式作用域兩種方法彼此看不見啊,後面的所有都用建構函式這種方法寫一遍?更No
            //如果你不明白More.call(this)什麼意思 仔細思考'借用建構函式實現屬性繼承'和下面這句話
            //將當前Person()裡this指向的物件(instance)給More(),讓More()裡的this有和Person裡的this一樣的指向
            //結果程式碼的實際意思就變成了instance.name='jack',instance.age=18,instance.color=['yellow','blue','red']
            //如果你函式基礎打的好 一定能明白的
            //而且我們呼叫了兩次建構函式 一次是在1的位置 一次是在2 這個我們一會接著說 這句話有個印象 先忽略
            (function(){
                function Person(){
                    this.name='jack',
                    More.call(this)//2
                }
                
                
                function More(){
                    this.age=18,
                    this.color=['yellow','blue','red']
                }
                
                More.prototype.SayAge=function(){
                    alert(this.age);
                }
                Person.prototype.SayName=function(){
                    alert(this.name);
                }
                var instance=new Person();
//                var instance1=new Person();//1
//                instance1.color.push('green');
//                alert(instance1.color);

//                var instance2=new Person();
//                alert(instance2.color);
            }());
            //寄生組合式繼承
            //那麼有沒有一種更好的形式,可以解決方法的繼承呢?當然有啦 請先看程式碼
            //最上面的原型繼承走一波!完美解決問題!不過你一定注意到了標註的1和2
            //1處通過原型繼承可以繼承 屬性 和 方法,2處通過建構函式繼承 屬性,嗯 看起來沒問題
            //但是我們好像用原型繼承,似乎多繼承了一次屬性啊。。多做了一次無用功消耗了機器的效能 下面我們看看為什麼會寫入(呼叫)兩次
            //1通過原型繼承的時候將 屬性 和 方法 寫入到了原型鏈
            //2通過建構函式繼承的時候將 屬性 寫入到了 原型(還記得 原型 遮蔽 原型鏈 嗎)
            //這個時候我們進行操作是訪問 原型中的屬性 原型鏈中的方法
            //(屬性和方法都是先在原型上找 在原型上找到了屬性 ok 結束,在原型上沒發現方法,繼續找,原型鏈上有,ok 結束,{如果還找不到 看看Object爸爸,還沒有就只能報錯啦})
            
            //我們可以看一下&&&&位置的alert 依然堅挺的輸出yellow,blue,red 不給你報undefined
            //因為我命令他不許訪問應該訪問的原型 我就想通過__proto__跳過去看看上面是否有垃圾,發現原型鏈上還真有 看來屬性絕對被寫入了兩次 一次在原型上,一次原型鏈上
            //看來寄生組合式繼承這種方法也有點業餘 不過完美可用啊 不過作為一個專業的程式設計師是不允出現垃圾的
            
            //還有指標 請看**** 繼承後原型的constructor指標為何指向了More呢,通過繼承後 Person原型的constructor指標不應該指向他自己的建構函式嗎
            //實際上它先指向了他自己沒錯 但是由於繼承 它還會向上級尋找 看看有沒有更多的繼承(除了頂層Object物件,會忽略掉)
            //最終停在離頂層物件Object最近的那個 也就是 More
            //constructor它才沒有那麼智慧,繼承後這貨看到自己家你不讓它停下去看看它真正的家,它是不會停的,啥時候快撞到Object這個頂層的爸爸,它才停
            //
            (function(){
                function Person(){
                    this.name='jack',
                    More.call(this)//2
                }
                
                
                
                function More(){
                    this.age=18,
                    this.color=['yellow','blue','red']
                }
                
                More.prototype.SayAge=function(){
                    alert(this.age);
                }
                
                Person.prototype=new More();//1
                
                Person.prototype.SayName=function(){
                    alert(this.name);
                }
//                var instance1=new Person();
//                instance1.color.push('green');
//                alert(instance1.color);
//                
//                var instance2=new Person();
//                instance2.color.push('purple');
//                alert(instance2.color);
//                
//                alert(Person.prototype.constructor.name);//****
//                alert(instance2.__proto__.color);//&&&&
            }());
            (function(){
                //終極寄生組合式繼承(終極完美繼承)
                /* 既然寄生組合式繼承會寫入(呼叫)兩次
                    那麼有沒有一種方法可以跳過屬性直接把方法寫入直接繼承原型鏈呢?
                    當然有!我們是利用一個空建構函式作為橋樑
                    PS:當然你可以不用這個方法直接Person.prototype=More.prototype試試 (他倆共享一個原型物件還繼承個什麼勁。。。
                    首先我們先大概看最下面的兩個函式object和inherit進行方法繼承 他們兩兄弟中的inherit順手修復constructor指標 讓它回家看看
                    這倆函式就不多解釋了 看不懂找個豆腐撞死自己算了 稍微解釋下
                    把More原型(More.prototype)上面想繼承的方法,給橋樑函式bridge原型(bridge.prototype)(這時候bridge的原型指向More原型 看下一行
                                                            bridge.prototype →→→More.prototype
                    這時候該例項化一下繼承啦 把bridge函式例項化順手修復一下指標再給Person.prototype去繼承
                                                        總:    Person.prototype →→→bridge.prototype →→→More.prototype
                    噹噹噹 完成了 如果你有個小疑問 搞了半天不就是Person.prototype=new More(); 真費事 我不想看了 用上個也挺好
                    那麼 如果這個被繼承的物件有10個屬性,100個屬性呢?他被呼叫10次 100次呢 我們會釋放多少機器的壓力呢
                    
                 */
                function Person(){
                    this.name='jack',
                    More.call(this)//2
                }
                
                
                
                function More(){
                    this.age=18,
                    this.color=['yellow','blue','red']
                }
                
                More.prototype.SayAge=function(){
                    alert(this.age);
                }
                
                inherit(Person,More);
                var instance1=new Person();
                
                Person.prototype.SayName=function(){
                    alert(this.name);
                }
//                instance1.SayAge();
                
                //封裝好的兩兄弟函式
                function object(one){
                    function bridge(){};
                    bridge.prototype=one;
                    return new bridge();
                }
                function inherit(main,more){
                    var proto=object(more.prototype);
                    proto.constructor=main;
                    main.prototype=proto;
                }
            }());
            
        </script>
    </head>
    <body>
        
    </body>
</html>