對象和原型(上)


 

   面向對象是javascript核心內容之一,今天就來討論對象和原型.

    首先討論創建對象幾種常見的方式:

  (1) 最經典,最簡單的方法.利用object

      var o=new Object();//創建對象;

     o.name='jack';//添加屬性

    o.sayName=function(){alert(this.name);}//添加方法;

    點評:這種方法明顯不適合創建多個對象。

 (2)對象字面量形式:注意語法格式

    var o={

    name:'jack',

    sayName:function(){alert(this.name);}

}

 

    (3)一種設計模式 ‘工廠模式’

   function  createObject(name){

     var o=new Object();

      o.name=name;

     o.sayName=function(){alert(this.name);}

     return o;

}

      var obj=createObject('jack');   //創建對象

      alert(obj instanceof Object) //ture

     alert(obj instanceof createObject);//false;

      alert(type obj )//Object

 點評: 這種方式不難理解; 就是利用函數初始化一個Object對象,可以創建多個對象;

 缺點:無法判斷對象的具體類型, 都是object對象。

    (4)  構造方法

         function Person(name){

       this.name=name;

      this.sayName=function(){alert(this.name);}

}

      var o=new Person('jack');//創建對象;

       alert(o.name);//訪問屬性

       o.sayName();//訪問方法;

       alert(o instanceof Person);//true

       alert(o instanceof Object);//true

       alert(type o);  //object

 

     那么 這種構造方法是如何創建對象的?     

     var o=new Person('jack');//創建對象;  

     這時調用構造方法,同時自動創建(底層,看不見) var o=new Object();然后 this=o; 我們知道此時this就代表對象了,創建該對象的屬性和方法(執行代碼); 返回this對象(看不見);

    我們的o接受this的引用,指向了Object對象,同時也就可以訪問屬性和方法了! 

 

    4 構造方法結合原型------"默認"模式.

      我們知道通過構造函數創建對象是有一弊端的, 就是每一個創建的對象都有各自一份屬性和方法; 這里的弊端就是方法的重復。另外我們一旦寫好了構造函數,不能在外面為構造函數添加屬性和方法。為了解決這個問題  javascript為我們提供了prototype的一個屬性。值得注意的是這個構造函數(Function 對象)的屬性,不是對象實例的屬性(注意這句話)。

     具體是這樣的:

        function Person(name){

       this.name=name;}

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

    同時使用構造函數和原型的好處是:可以節省內存。這種模式基本上就創建對象的默認模式。

     5 其他方法;

    

 

      現在問題來了:原型是怎么回事? 

      先看一張圖---------來自《javascript高級程序設計》 這是很有意思的圖

     

    解釋:

     在這里Person是一個構造函數, 我們看到有一個prototype屬性,這個就是原型屬性。他其實是一個指針, 指向一個對象,  這個對象叫原型對象  在這里是   Person.prototype  

     Person創建person1 person2 二個實例 ,圖上我們可以看到實例中的 [[prototype]]屬性,這是內部屬性 (基本上不能外部訪問) 這個對象是指向原型對象的(關鍵)。我們還可以看到

     原型對象的constructor屬性是指向Person的。圖中我們可以看到以上這些。 這樣 Person.prototype.sayName=function(){alert(this.name);} 就是向原型對象中添加方法,我們通過person1.sayName();

  為什么可以訪問方法?這就與[[prototype]]有關了,首先person1先找實例屬性有沒有叫sayName的方法,結果沒有,那么就通過 [[prototype]] 找到原型對象,查找有沒有sayName的方法,結果有那么就拿到了。

   因為一個構造函數只有一個原型對象與之對應,所有實例的[[prototype]]都是指向同一個原型對象的。所以原型里面的屬性是公有屬性,方法也是共有的。這個[[prototype]]在chrome ff中是 __proto__ 我們這樣

   alert(person1.__proto__); 結果是[Object] ;  我們是不能通過實例直接為原型添加方法和屬性的,只有通過構造函數才可以。值得注意的是Object.getPrototypeOf('object');在這里 Object.getPrototypeOf(person1)

  返回值是原型對象,這也是原型鏈繼承的關鍵之處(這里先不討論)。原型就是這樣的。

     

  一個問題:

    說到原型,我們為一個構造函數添加方法時可以這樣!

   Person.prototype={

    school:'一中',

  saySchoo:function(){

        alert(this.school); 

}

  //code

};

  這是對象字面量形式,我們會看到一些程序員這樣寫。那么有問題嗎?

   有的,二個問題?

   (1)constructor屬性問題。看上面我們知道這樣寫本質上重寫了原型對象.constructor沒有說明指向誰,那么就是window,不再是Person。我們可以這樣

  Person.prototype={

    school:'一中',

  constructor:Person;

 saySchoo:function(){

   alert(this.school);  }

   //code};

我們一般這樣就可以了,不過和原來原型有一點個區別,就是constructor為可枚舉(for in打印出該屬性)。原來是不可以枚舉的。 要和原來原型對象一樣,那么應該這樣。

Person.prototype={

    school:'一中',

   saySchoo:function(){

    alert(this.school); 

}

 //code

};

 Object.defineProperty(Person.prototype,"constructor",{

value:Person,

enumerable:false; //不可以枚舉;

  });

這樣就接近了。我們也可以不去設置,假定沒有什么影響。

 

   (2)第二個問題就是

   function Person(){}

    var o=new Person();

Person.prototype={

    school:'一中',

    saySchoo:function(){

       alert(this.school); 

        }//code

};

o.saySchool(); //無法訪問,undefined

 原因是: o的[[prototype]]指向原來的 原型對象;現在重新寫了,原型對象的位置變了,(函數中prototype不再指向舊的原型對象),但是o實例環視原來的,原來就沒有這個方法,所有訪問不到。

 ps:可能這些內容過於詳細,不過了解還是好的,我們很多時候還是會使用這種模式重寫原型對象的。就可能會有上面的二個bug;

 

 

 

 

 

    

  

 

    


免責聲明!

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



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