1. 程式人生 > >關於原型,原型鏈和繼承的討論

關於原型,原型鏈和繼承的討論

寫部落格也有不短的時間了,經常做一些自己的總結和技術的分享。但是之前的由於之前的備用號碼丟失,也正好準備換個備用卡,所以直接登出了,沒有找回。所以就開了個新的部落格。以後,我會經常在這裡分享一下心得,和和大家做一些技術探討。今天就原型鏈做一個總結。

在剛接觸到js原型和原型鏈的時候,可能很多人都會有很多的困惑和疑問。這是很正常,因為當你有了這種狀態的時候,說明你已經到了王國維先生說的學習的三重境界中的第一重了,昨夜西風凋碧樹,獨上高樓,望斷天涯路。就是說你已經有了想要學的更透徹,追尋執行原理的想法了。是所謂獨上高樓,望斷天涯路。一定是有著窮其理的好奇心與幹勁。

一,基本釋義
所有型別(包括基本型別與函式,陣列,物件)都擁有__proto__屬性(隱式原型)

            所有函式擁有prototype屬性(顯式原型)最底層的Object也有prototype屬性。這也驗證了萬物皆物件,所有的型別都是從它中來。
            
		函式的prototype是個指標,它指向自己的原型物件,
 function Ball(words){	
        this.words = words;
    }
    Ball.prototype = {
    	a:10,
       play:function(){
       console.log(  this.a);
		}
    }
	var ball=new Ball();
    這個 Ball.prototype就是建構函式function Ball的原型物件;
	通過這個建構函式new 出來的新的物件ball中就有__proto__這個屬性,並且__proto__指向這個建構函式function Ball的原型物件 Ball.prototype

二,建構函式例項解析

    //建立例項
  	 function Ball(words){	
        this.words = words;//當前this 指向window,new出新的ball物件後,指向ball(在面向物件中就用這裡為原型物件傳引數)
    }
    Ball.prototype = {
    	a:10,
       play:function(){
   console.log(  this.a);//這裡this指向原型物件 Ball.prototype,在new 出新的例項後, this指向例項即	ball;
		}
    }
	var ball=new Ball();
	ball.play();//   a=10;
           play()方法是ball例項本身具有的方法,所以ball.play()列印a=10;ball.play()不屬於ball例項的方法,屬於建構函式的方法,因為例項繼承建構函式的方法。

            例項w的隱式原型指向它建構函式的顯式原型,指向的意思是恆等於
     ball.__proto__ === Ball.prototype
            當呼叫某種方法或查詢某種屬性時,首先會在自身呼叫和查詢,如果自身並沒有該屬性或方法,則會去它的__proto__屬性中呼叫查詢,也就是它建構函式的prototype中呼叫查詢。所以很好理解例項繼承建構函式的方法和屬性:

            ball本身沒有play()方法,所以會去Ball()的顯式原型中呼叫play(),即例項繼承建構函式的方法。         

三,原型和原型鏈
Object.prototype.play= “play”;
function Ball(){}
console.log(Ball); //function Ball()
let ball= new Ball();
console.log(ball); //Ball{} 物件
console.log(ball.play); //play

            解析:

            ball是Ball()的例項,是一個Ball物件,它擁有一個屬性值__proto__,並且__proto__是一個物件,包含兩個屬性值constructor和__proto__

    console.log(ball.__proto__.constructor);   //function Ball(){}
    console.log(ball.__proto__.__proto__);     //物件{},擁有很多屬性值
            我們會發現ball.__proto__.constructor返回的結果為建構函式本身,ball.__proto__.__proto__有很多引數
            原型鏈總結:
                      1.查詢屬性,如果本身沒有,則會去__proto__中查詢,也就是建構函式的顯式原型中查詢,如果建構函式中也沒有該屬性,因為建構函式也是物件,也有__proto__,那麼會去它的顯式原型中查詢,一直到null,如果沒有則返回undefined

            2.ball.__proto__.constructor  == function Ball(){}

            3.ball.___proto__.__proto__== Object.prototype

            4.ball.___proto__.__proto__.__proto__== Object.prototype.__proto__ == null          

            5.通過__proto__形成原型鏈而非protrotype
		console.log(ball.__proto__.__proto__.__proto__);   //null
    	console.log(Object.prototype.__proto__);        //null
          通過上述我們發現prototype下面是有constructor和__proto__的,也就是說建構函式也是有__proto__指向的,它就指向Object.prototype,而且Object.prototype下也有__proto__指向null;

所以利用這個原理,我們就能完成繼承。在繼承的類中,例如下列;

//原來的的類
	 function Ball(words){	
            this.words = words;//當前this 指向window,new出新的ball物件後,指向ball(在面向物件中就用這裡為原型物件傳引數)
        }
        Ball.prototype = {
        	a:10,
           play:function(){
     		 this.a===10;//這裡this指向原型物件 Ball.prototype,在new 出新的例項後, this指向例項即	ball;
			}
        };
    
//寫一個繼承函式
//subClass想要繼承的新類,supClass是原來的類
	        function extend(subClass,supClass) {
//         建立一個臨時類
         function F() {}
//         設定這個臨時的類的原型是父類的原型
         F.prototype=supClass.prototype;
//         子類的原型是例項化的這個臨時類
         subClass.prototype=new F();
//         設定子類原型中該子類的指標指向自己這個建構函式
         subClass.prototype.constructor=subClass;
//        設定子類的屬性superClass是父類的原型
         subClass.superClass=supClass.prototype;
//         如果父類原型中指標沒有指向父類的建構函式,仍然指向Object的指標時,
//         重設父類原型中指標指向父類的建構函式
         if(supClass.prototype.constructor===Object.prototype.constructor){
             supClass.prototype.constructor=supClass;
         }
     }
     
 //繼承得到的新類

     function Box() {
//         使用冒充將父類的物件屬性加入到子類的物件屬性中
         Ball.call(this);
     }
     extend(Box,Ball);
//     console.log(Box);
        Box.prototype.play=function () {
//            Box.superClass.play.call(this);//類似es6中super作用的語句
            console.log(this.a);
        };
         var box=new Box();
        box.play();	//列印的結果為1;(如果上面的super語句不註解的話那麼結果就為10;)
     這就是es5當中我們常用的繼承方法,是基於原型和原型鏈基礎之上來的。可以看到Box.prototype=new new F();   而  F.prototype=supClass.prototype;其中是通過new改變了Box中的__proto__的指向,讓其指向supClass.prototype;所以我們在新類中可以用到原來類的屬性和方法,也可以對Box.prototype.play進行原來類下某個方法和屬性進行覆蓋。(原理是物件屬性和方法的呼叫順序先找自身,再去原型鏈上找)但是注意不能對Box.prototype.__proto__進行修改,因為Box.prototype.__proto__指向Ball.prototype。