js原型與原型鏈

1、prototype與__proto__javascript

  一、prototype(顯式原型):每個函數在建立以後都會擁有一個名爲prototype的屬性,這個屬性指向函數的原型對象。它只存在於函數裏,例以下面的例子中:java

function Person(name) {
            this.name = name;
            this.showMe = function() {
                alert(this.name);
            }
        };

        Person.prototype.from = function() {
            alert('I come from prototype.');
        }
 var p = new Person('js');
 p.from();

  Person擁有prototype對象,咱們向對象中添加了一個from方法,構造函數中有一個prototype的屬性,改屬性是一個指針,指向對應的prototype對象(注意區分一個是屬性一個是對象),這裏還有一個重要的屬性,就是prototype對象中有一個屬性constructor,該屬性指向對應的構造函數,是一種對應的關係。函數

  p是Person構造函數新建的一個實例,new這個方法分爲3個部分,最重要的一步是第2步,就本例子來講 this

    <1> var p={}; 也就是說,初始化一個對象p。spa

    <2> p.__proto__=Person.prototype;prototype

    <3> Person.call(p);也就是說構造p,也能夠稱之爲初始化p。3d

  p這個對象沒有prototype屬性,可是有__proto__屬性,這個在下面會提到。指針

 

 

2、__proto__code

  隱式原型,每個對象都擁有該屬性,上面提到新建的實例p,在實例化的時候,能夠得出p.__proto__=Person.prototype。那麼當咱們調用p.Say()時,首先p中沒有Say這個屬性, 因而,他就須要到他的__proto__中去找,也就是Person.prototype,而咱們在上面定義了 Person.prototype.Say=function(){}; 因而,就找到了這個方法。對象

  具體的例子以下圖:

  

1.構造函數Foo()
構造函數的原型屬性Foo.prototype指向了原型對象,在原型對象裏有共有的方法,全部構造函數聲明的實例(這裏是f1,f2)均可以共享這個方法。

2.原型對象Foo.prototype
Foo.prototype保存着實例共享的方法,有一個指針constructor指回構造函數。

3.實例
f1和f2是Foo這個對象的兩個實例,這兩個對象也有屬性__proto__,指向構造函數的原型對象,這樣子就能夠像上面1所說的訪問原型對象的全部方法。

另外:
構造函數Foo()也有__proto__屬性,
指向它的構造函數的原型對象。函數的構造函數就是Function,所以這裏的__proto__指向了Function.prototype。

原型對象的__proto__屬性
指向它的構造函數的原型對象。這裏是Object.prototype.

最後,Object.prototype的__proto__屬性指向null。
 
所以,這幾個屬性的具體總結是:
  一、__proto__:指向其構造函數的原型對象,全部的對象都擁有該屬性
  二、prototype:指向函數的原型對象,只有函數擁有
  三、constructor:這個屬性從字面上來理解,就是指向它的構造函數,其本意也能夠這麼理解。實例的構造函數是Person,Person的構造函數是Function等等
 
3、原型鏈與繼承
  繼承實際是就是已原型鏈爲基礎,經過__proto__屬性的一層層查找,來找到父對象原型中的方法,獲取使用
  先看代碼:
  
function Person(name) {
			this.name = name;
			this.showMe = function() {
				alert(this.name);
			}
		};

		Person.prototype.from = function() {
			alert('I come from prototype.');
		}
 
  
		function SubPer() {}
		SubPer.prototype = new Person('js');   
		

		var son = new SubPer();
		son.showMe(); //js   
		son.from(); //I come from prototype.   
		alert(son.constructor); 

  代碼裏Person具備原型方法,SubPer經過SubPer.prototype = new Person('js')這句代碼實現了繼承,new的新實例son能夠獲取Person的方法。

  

  繼承實現的方法就是:把子類的 prototype設置爲父類的一個(實例化)對象;
  SubPer.prototype = new Person();
  利用以前學到的new方法,能夠轉換爲
  SubPer.prototype.__proto__ = Person.prototype;
  由圖上能夠看出   實例的__proto__指向prototype,因此
  p.__proto__.__proto__ = Person.prototype;
  這樣就造成了一個已__proto__爲鏈接的原型鏈。
 
  其實只從圖上觀察,也能夠看到__proto__造成的一條線路,SubPer實例——SubPer的prototype(Person實例)——Person的prototype,這就一條原型鏈。
  
4、繼承過程當中的constructor
  咱們知道構造函數的原型中有一個constructor值,指向其構造函數。在書寫原型的過程當中,若是採用以下所示重寫原型的方法,就可能會形成constructor值得異常
		function Person(name) {
			this.name = name;
			this.showMe = function() {
				alert(this.name);
			}
		};
		Person.prototype = {        //重寫原型     
			alertNum:function(){
				alert(1111);
			}
		}  
		function SubPer() {}
		SubPer.prototype = new Person('js');   	
		var son = new SubPer();        

  咱們運行代碼,查看son.constructor的值,發現結果以下:

  

  這兩個對象的constructor的值都指向了Function,由於咱們在這裏重寫了一個原型對象,每當新建立一個對象時,就會同時建立它的prototype對象,注意這裏的對象是由__proto__指向的,這個對象也得到了constructor屬性,對象字面量建立的對象其constructor指向Object,因此由Person new的實例,其constructor也指向了Object

  代碼作以下的修改:

		function Person(name) {
			this.name = name;
			this.showMe = function() {
				alert(this.name);
			}
		};
		Person.prototype = {
			alertNum:function(){
				alert(1111);
			}
		}
		function SubPer() {}
		SubPer.prototype = new Person('js');   	
		SubPer.prototype.constructor = SubPer;    //修改constructor
		var son = new SubPer();

  

  constructor的值被修改成Suber構造函數,constructor的值能夠被修改覆蓋。

相關文章
相關標籤/搜索