javascript的面向對象詳解

每次說到javascript到面向對象,總感受本身內心懂,可是殊不知道該怎麼說,這就是似懂非懂到表現,因而乎,每次一說,就要處處去查找資料,零零碎碎到看了一些,感受有懂了,可是過段時間,好像又不知道是怎麼回事了,因而乎,又處處找資料,然道是我原本就缺對象?纔不理解對象是啥,以致於現實中找找對象,javascript中也在找對象!哎,好尷尬啊!直到我看到了一個妹紙寫到「不可不知的javascript面向對象」,我才明白麪向對象是什麼,這是否是說我要找到對象就是這個妹紙呢😄,先記錄一下備忘吧,下面是妹紙寫到主要內容:javascript

對象的建立:

1 建立一個面向對象
var obj = new Object(); 
obj.name = 'haha';
obj.showName = function(){ 
  alert(obj.name);
}
obj.showName();

 

缺點:當咱們想建立多個面向對象的時候,重複代碼過多,須要封裝,因此有了工廠方法。java

2 工廠方式app

function CreatePerson(name){ 
   var obj = new Object();   //原料
   obj.name = name;         //加工
   obj.showName = function(){
     alert(this.name);
 } 
   return obj;//出廠
}
var p1 = CreatePerson('haha');
p1.showName();
var p2 = CreatePerson('hehe');
p2.showName();
//其實就是簡單的封裝函數,整個過程像工廠的流水線,因此叫工廠方式

 

缺點:沒法識別建立的對象的類型。由於所有都是Object,沒有區分度,不像Date、Array等,所以出現了構造函數模式。函數

3 構造函數模式測試

function CreatePerson(name){ 
   this.name = name; 
   this.showName = function(){ 
     alert(this.name);
   } 
} 
var p1 =new CreatePerson('haha'); 
p1.showName();
var p2 = new CreatePerson('hehe'); 
p2.showName();

 

咱們經過這二個方面來改變:
1 函數名首字母大寫
這是爲了區別於普通的函數,構造函數自己其實就是普通的函數,只是咱們專門用它來實現了構造的功能,因此專門起了一個名字叫構造函數,任何函數均可以成爲構造函數,這取決於你調用函數的方式,當使用了New的方式調用就成了構造函數。
2 New 關鍵字調用
調用函數的時候用了 New關鍵字,那麼New到底作了什麼?用不用New有什麼區別?再來看下面的例子網站

function CreatePerson(name){
   this.name = name;
   this.showName = function(){
     alert(this.name); 
   }; 
    console.log(this);
} 
new CreatePerson('haha'); //CreatePerson
CreatePerson('haha');  //window

 

咱們會發現當用New去調用一個函數的時候,this的指向會不同。其實New主要作了下面這些事,不過下面寫的只是大概的行爲,並非內部源碼。this

function CreatePerson(name){ 
   var obj = {}; //聲明一個空對象obj 
   obj._proto_= CreatePerson.prototype;
   //把這個對象的_proto_屬性指向構造函數的原型對象,這樣obj就能夠調用CreatePerson原型對象下的全部方法 ,這裏原型先知道結論,下面會講。
    CreatePerson.apply(obj);   //用apply方法讓this指向obj對象
    this.name = name;   //obj對象添加屬性,方法
    this.showName = function(){ 
       alert(this.name);
      }; 
    return obj;//返回這個對象
}

 

函數構造模式存在的問題:spa

alert(p1.showName==p2.showName);//false

 

缺點:可見這兩個對象並非共用一個方法,每new一次,系統都會新建立一個內存,這兩個對象各自有各自的地盤,但他們具備相同的功能,還不共用,確定不是咱們所但願的。因此就有了下一種方法,原型+構造模式prototype

4 原型+構造模式指針

原型:每一個函數都有一個prototype屬性,它是一個對象,也稱做原型對象,咱們能夠把方法和屬性寫在它上面(不過原型對象不只僅有咱們寫的屬性和方法,還有別的,下面會介紹),而經過這個函數建立出來的實例對象,都能共享這個原型對象下的方法和屬性。因此咱們只須要把想要共享的東西放在函數的prototype下,不想共享的東西經過構造函數來建立就能夠了。
看個栗子(原型+構造)

function CreatePerson(name){ 
  this.name = name;
}
CreatePerson.prototype.showName = function(){ 
   alert(this.name);
}
var p1 =new CreatePerson('haha');
p1.showName();
var p2 = new CreatePerson('hehe');
p2.showName();
alert(p1.showName==p2.showName);//true

 

測試爲true,可見showName()方法是共享的,也就是說他們共用一個內存,更進一步的說它們存在引用關係,也就是說你更改了p1的showName也會影響p2的showName。

_proto_屬性:
同一個函數造出來的實例對象能共享這個函數的prototype下的方法和屬性,可是它是如何作到的呢?這裏要出場的就是_proto_屬性.
每一個實例化對象都有_proto_屬性,它是一個指針,指向函數的prototype,也就是保存了它的地址。(JS中任何對象的值都是保存在堆內存中,咱們聲明的變量只是一個指針,保存了這個對象的實際地址,因此有了地址就能找到對象),
因此總得來講,每一個實例化對象都有_proto_屬性,保存了構造函數的原型對象的地址,經過這個屬性就能夠擁有原型對象下的全部屬性和方法,_proto_屬性實際就是實例化對象和原型對象之間的鏈接

原型鏈:
每一個函數均可以成爲構造函數,每一個函數都有原型對象,每一個原型對象也能夠是一個實例化對象,好比,你建立了一個函數fun,它是構造函數function的實例化對象,而function的原型對象,又是Object的實例對象。因此fun有個_proto_屬性能夠訪問到function的原型對象,function原型對象也是個實例對象,也有個_proto_屬性,能夠訪問到Object的原型對象,因此經過_proto_屬性,就造成了一條原型鏈。每一個實例化對象均可以訪問到鏈子上方的方法和屬性,因此fun是能夠訪問Object原型對象下的方法和屬性的。實際上全部對象均可以訪問到Object的原型對象。

原型鏈的訪問規則:先在自身的下面尋找,再去一級一級的往原型鏈上找。
以下:

function Aaa(){}
Aaa.prototype.num = 3;
var a1 = new Aaa();
a1.num =10;
alert(a1.num); //10

 

原型對象:
原型對象下可能有三種屬性:
1 原型對象所帶方法和屬性 2 constructor 3_proto_屬性
constructor:構造函數屬性,每一個函數的原型對象都有的默認屬性,指向函數。
每一個實例化對象自己是沒有constructor屬性的,他們下面默認只有一個_proto_屬性,用來鏈接原型對象,而和構造函數自己是沒有直接的聯繫的。因此它的constructor是訪問的原型對象上的。因此當原型對象的constructor變化了,實例化對象的constructor也會改變。可是若是這個對象自己既是原型對象,又是實例化對象,那就擁有了constructor屬性,無需從原型對象上面訪問。**

看下面的例子,來驗證咱們所說的:

function CreatePerson(name){ 
   this.name = name;
}
CreatePerson.prototype.showName = function(){ 
   console.log(this.name);
 };
var p1 =new CreatePerson('haha');
p1.showName();
console.log(p1.constructor); // CreatePerson 來自CreatePerson.prototype

console.log(CreatePerson.prototype); 
// {showName:{},constructor:CreatePerson,__proto__:Object.prototype}
//可見,原型對象保存了
      1 自身添加的方法,
      2 構造函數constructor 
      3 _proto_(和上一層構造函數原型對象的鏈接)

console.log(CreatePerson.prototype.__proto__===Object.prototype);
// true 這個原型對象自己又是object的實例化對象,全部_proto_指向Object的原型對象

console.log(CreatePerson.prototype.__proto__===Object);
// false 可見是和構造函數下原型對象的鏈接,不是構造函數

console.log(CreatePerson.prototype.constructor);
//CreatePerson CreatePerson.prototype是Object實例化對象,也是原型對象,因此自身擁有constructor屬性

console.log(Object.prototype.__proto__); 
// null 原型鏈的終點是null

console.log(CreatePerson.__proto__); //function.prototype
// CreatePerson自己既是構造函數又是function的實例化對象,擁有_proto_屬性,指向function的原型對象

console.log(CreatePerson.constructor); 
// function 繼承自function.prototype

console.log(CreatePerson.prototype instanceof CreatePerson ) 
//驗證是否在一條原型鏈上 false

 

字面量法定義原型:
爲了建立對象的代碼更方便,你必定見過這樣的代碼,就是字面量法:

function Aaa(){}
Aaa.prototype = { 
  showName:function(){},
  showSex:function(){}
}; 
var a1 = new Aaa();
console.log(Aaa.prototype);
//{showName:function(){},_proto_} 
//你會發現constructor不見了,由於這種方式至關於從新賦值了Aaa.prototype 

console.log(Aaa.prototype.constructor);
//Object 由於自身沒有了constructor屬性,就去上級原型對象找,找到了Object
console.log(a1.constructor );
//Object 也變了,驗證了它是訪問的原型對象上的

 

所以咱們在寫的時候須要修正一下原型的指向:

function Aaa(){}
Aaa.prototype = { 
constructor:Aaa, 
num1:function(){alert(10);}
}
var a1 = new Aaa();
a1.constructor    // Aaa

 

理解了這些,之後的繼承就很好理解了,未完待續。若有錯誤,歡迎糾正。

 

轉載出處: http://www.jianshu.com/p/845ad9b78201

 

感受簡書,掘金等這樣的博客類網站看的舒服一點,我是否是要轉移陣地了!你以爲呢?

相關文章
相關標籤/搜索