談談javascript中的prototype與繼承


今天想談談javascript中的prototype.

通常來說,javascript中的對象就是一個指向prototype的指針和一個自身的屬性列表javascript創建對象時采用了寫時復制的理念。
只有構造器才具有prototype屬性,原型鏈繼承就是創建一個新的指針,指向構造器的prototype屬性。
prototype屬性之所以特別,是因為javascript時讀取屬性時的遍歷機制決定的。本質上它就是一個普通的指針。
 
構造器包括:
1.Object
2.Function
3.Array
4.Date
5.String
 
 


下面我們來舉一些例子吧
//每個function都有一個默認的屬性prototype,而這個prototype的constructor默認指向這個函數
//注意Person.constructor 不等於 Person.prototype.constructor. Function實例自帶constructor屬性
   function Person(name) { 
        this.name = name; 
    }; 
    Person.prototype.getName = function() { 
        return this.name; 
    }; 
    var p = new Person("ZhangSan"); 
   
    console.log(Person.prototype.constructor === Person); // true 
    console.log(p.constructor === Person);  // true ,這是因為p本身不包含constructor屬性,所以這里其實調用的是Person.prototype.constructor   

我們的目的是要表示
1.表明Person繼承自Animal
2. 表明p2是Person的實例
 
我們修改一下prototype屬性的指向,讓Person能獲取Animal中的prototype屬性中的方法。也就是Person繼承自Animal(人是野獸)
   function Person(name) { 
        this.name = name; 
    }; 
    Person.prototype.getName = function() { 
        return this.name; 
    }; 
    var p1 = new Person("ZhangSan"); 
    
    console.log(p.constructor === Person);  // true 
    console.log(Person.prototype.constructor === Person); // true 

     function Animal(){ }
    
     Person.prototype = new Animal();//之所以不采用Person.prototype  = Animal.prototype,是因為new 還有其他功能,最后總結。
     var p2 = new Person("ZhangSan");
    
//(p2 -> Person.prototype -> Animal.prototype, 所以p2.constructor其實就是Animal.prototype.constructor)
     
     console.log(p2.constructor === Person);  // 輸出為false ,但我們的本意是要這里為true的,表明p2是Person的實例。此時目的1達到了,目的2沒達到。

 

 
但如果我們這么修正
 
Person.prototype = new Animal();
Person.prototype.constructor = Person;
 
這時p2.consturctor是對了,指向的是Person,表示p2是Person類的實例,但是新問題出現了。此時目的2達到了,目的1沒達到。
目的1和目的2此時互相矛盾,是因為此時prototype表達了矛盾的兩個意思,
1表示父類是誰
2作為自己實例的原型來復制
 
因此我們不能直接使用prototype屬性來表示父類是誰,而是用getPrototypeOf()方法來知道父類是誰。
 
    Person.prototype = new Animal();
     
    Person.prototype.constructor = Person;
     
    var p2 = new Person("ZhangSan");
     
    p2.constructor     //顯示 function Person() {}
     
    Object.getPrototypeOf(Person.prototype).constructor     //顯示 function Animal() {}

 

就把這兩個概念給分開了 ,其實通過使用 hasOwnProperty()方法,什么時候訪問的是實例屬性,什么時候訪問的是原型屬性就一清二楚了。


 
new做了哪些事情?

當代碼var p = new Person()執行時,new 做了如下幾件事情:

創建一個空白對象

創建一個指向Person.prototype的指針

將這個對象通過this關鍵字傳遞到構造函數中並執行構造函數。

具體點來說,在下面這段代碼中,

  Person.prototype.getName = function() {  }

如果我們通過

var person = new Person(); 其實類似於 var person = new Object(); person.getName = Person.prototype.getName;

因此通過person.getName()調用方法時,this指向的是這個新創建的對象,而不是prototype對象。

 

這其實在給現有函數加上新功能的情況下會用到,我們可以這么擴展現有的方法:

//function myFunc 的寫法基本上等於 var myFunc = new Function();

function myFunc () {
}

myFunc = function(func){
  //可以在這里做點其他事情
return function(){
     //可以在這里做點其他事情
return func.apply(this,arguments); } }(myFunc)

 

也可以在Function.prototype方法里直接通過this來訪問上面代碼的myFunc所指向的對象

 
         
function myFunc () {
}

if
(!Function.prototype.extend) { Function.prototype.extend = function(){ var func = this; return function(){ func.apply(this,arguments); } } }; var myFunc = myFunc.extend();

 

 


最后總結一下:

如果采用Person.prototype  = Animal.prototype來表示Person繼承自Animal, instanceof方法也同樣會顯示p也是Animal的實例,返回為true.

之所以不采用此方法,是因為下面兩個原因:

1.new 創建了一個新對象,這樣就避免了設置Person.prototype.constructor = Person 的時候也會導致Animal.prototype.constructor的值變為Person,而是動態給這個新創建的對象一個constructor實例屬性。這樣實例上的屬性constructor就覆蓋了Animal.prototype.constructor,這樣Person.prototype.constructor和Animal.prototype.contructor就分開了。

2.Animal自身的this對象的屬性沒辦法傳遞給Person

 

但是像下面這樣直接調用構造函數又可能失敗,或者產生其他影響。

Person.prototype = new Animal();

 

為了避免這種情況,所以我們引入了一個中間函數。所以正確的做法應該是

Person.prototype = (funtion(){

  function F(){};

  F.prototype = Animal.prototype

  return new F();

})()

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM