相信每一個學習過其餘語言的同窗再去學習JavaScript時就會感受到諸多的不適應,這真是一個顛覆咱們之前的編程思想的一門語言,先不要說它的各類數據類型以及表達式的不一樣了,最讓咱們頭疼,恐怕就是面向對象的部分了,在JavaScript中,是沒有給定一個建立對象的關鍵詞的,它不像Java中一個class就能夠建立一個對象,在JavaScript中,對象是一個十分鬆散的的key-value對的組合,一般,咱們在建立對象時,能夠經過{}來直接生成一個對象,就像咱們以前所學的,對象中有屬性,有行爲,這裏咱們稱爲方法,那咱們就先來建立一個對象:javascript
var zhangSan ={name:"張三",age:14}java
這就是一個簡單的對象,這種方式的優勢是簡單直接,一眼就能夠看出來他的屬性和方法可是,這種方法的缺點也很明顯,當咱們要建立多個對象時,咱們就須要一個一個的去建立,一個一個的去賦值,這是十分麻煩並且也不太顯示的方法,那麼咱們能夠用工廠法來建立:編程
1 function createPeople(name,food){
2 var people = new object(); 3 people.name=name; 4 people.eat=function(food){ 5 alert(food); 6 } 7 return people; 8 } 9 var zhangSan=createPeople("zhangSan","豆腐"); 10 zhangSan.eat();//這時會彈出豆腐.
這種方法能夠建立多個對象,用個俗一點的話說,就是你想要造多少我的就能夠造多少我的,哈哈,那麼你覺得你這是很好的建立對象的方法了嗎?錯了,如今咱們又有一個問題,就是,咱們如今用的這種工廠方法,你並不知道你建立的是一個什麼樣的類,你建立的people歸根底仍是由new object()來建立的,和用字面量的方式定義一個類的方法並無本質上的區別,一樣的,我也能夠由這個方法建立一條狗,一隻貓,一頭大象,一條板凳,等等,這樣子,類的意義還有什麼存在的必要呢,這時,就出現了用構造方法建立類的方法見代碼2:閉包
1 function createPeople(name,age,food){
2 this.name=name; 3 this.age=age; 4 this.food=food; 5 this.eat=function(){ 6 alert(this.name+"愛吃"+this.food; 7 } 8 } 9 var zhangSan = new createPeople("張三",13,"豆腐"); 10 zhangSan.eat();//這是會彈出張三愛吃豆腐這句話
採用這種方法,咱們能夠避免了上邊說的那種不知道建立的是什麼類型的對象的那種狀況,這裏咱們要來介紹一下構造方法來建立對象,若是不加new,createPeople()只是一個很是普通的函數,可是在它前邊加上new以後,這一切都會變得不一樣,代碼2中第九行的那句話能夠分解爲兩句話來理解:app
1 var zhangSan={};//第一步首先建立一個空對象, 2 createPeople.apply(zhangSan arguments);//這一步將函數中的this綁定到zhangSan上,而後就是執行函數中的代碼
好了,採用構造函數建立對象,咱們能夠將這個函數理解成一個類,由這個類能夠創造屬於它的對像,而不是像上邊的工廠方法那樣每一個對象都沒有明確的分類,瞭解javascript內存管理機智的咱們應該都知道,在Javascript中,你每次建立一個對象,堆中就要爲這個對象的方法和屬性分配一個內存空間,可是對於不少對象來講,它們的屬性大都不盡相同,可是不少時候它們的不少方法都是相同的,不一樣的只是傳入的參數的不一樣而已,這樣子是很浪費咱們的內存的,這時候,咱們就先去了解一下對象的原型鏈:函數
每一個函數內都有一個prototype屬性,這個prototype屬性執行它們的prototype對象,而每個函數的protocoltype對象中都有一個constructor屬性,這個屬性指向這個函數自己,學習
因此問題就來,這是一個很繞口的問題,那麼咱們就來張圖理解一下:this
那你說這跟我建立的每個對象上的方法有神馬關係呢?固然有關係了,當咱們使用new一個構造方法來建立一個對象的時候,咱們建立的那個對象中,也會有一個_proto_屬性,這個屬性指向構造函數的原型對象,也就是說,在咱們對象實例和構造函數之間,它們都指向對象原型,咱們每個對象中均可以繼承構造函數原型對象中的屬性和方法.那麼這就引出了咱們要說的內容,就是將咱們每一個對象都須要的方法放到咱們的prototype對象中:spa
1 function createPeo(userName,hobby){
2 this.userName=userName; 3 this.hobby=hobby; 4 createPeo.prototype.eat=function (){//在對象原型上定義函數,來節省內存; 5 alert(this.userName+"喜歡吃"+this.hobby); 6 } 7 } 8 var shang = new createPeo("張珊","炒麪"); 9 var liSi = new createPeo("李四","擼串");//這樣就能夠極大的節省了咱們的內存,並且每個對象都能調用到咱們的方法 .
好了,如今咱們已經成功的建立了一個類,而且由這個類建立了我麼所須要的對象,那麼咱們都知道,有了類,有了對象,咱們老是會下意識的去背了起來,累的三大特徵:封裝,繼承,多態,那麼在JavaScript中,類的封裝是怎麼來實現的呢?這個在明天講述閉包的時候咱們再進行討論,今天咱們先來討論一下類中都有的繼承,在咱們JavaScript中,類的繼承並不像Java中的那樣用一個class...extend就好了,在JavaScript中,咱們並無一個class繼承的概念,咱們用的是很隨意的一個原型鏈的執行繼承,就是經過prototype屬性來指向想要繼承的對象的原型對象,那麼咱們就開始想,那麼我直接用prototype屬性指向不就好了嗎?prototype
1 function Dog(name,age){
2 this.name=name; 3 this.age=age; 4 } 5 function LittleDog(){} 6 LittleDog.prototype=Dog.prototype;//你覺得這樣就完成了原型繼承,只不過,這樣子直接指向原型鏈的話,你在修改子類的時候,相應的就會修改父類.而且,既然他兩個都指向一個原型,那你直接用Dog來建立不就完了了嗎,這就失去了繼承的意義.
因此,直接將原型鏈指向父類是不對的,說到這裏,咱們就能夠用一個空函數來做爲中轉完成原型鏈的繼承:
function Dog(name,age){
this.name=name; this.age=age; Dog.prototype.eat=function(){alert("我今年"+this.age+"歲"); } } function LittleDog(name,age){ this.name=name; this.age=age; } function F(){} F.prototype=Dog.prototype; LittleDog.prototype=new F(); LittleDog.prototype.constructor=LittleDog; var xiaoGou=new LittleDog("旺財",13); xiaoGou.eat();//這時用小狗調用父類中的方法,返回一個彈框,說明繼承成功.
經過這種中間函數來完成類的繼承.它並無改變原有Dog中的原型鏈條,同時也完成了繼承,若是想要在LitteDog中添加原型方法,就能夠在new F()中建立方法和屬性,固然,咱們也能夠將這種繼承方法封裝成一個函數,這樣,咱們程序的執行效率和美觀程度就大大的提高了:
1 function inherits(child,parent){
2 var f = function(){} 3 f.prototype = parent.prototype; 4 child.prototype = new f(); 5 child.prototype.constructor=child; 6 }//這是一個封裝函數,再進行類的繼承時,能夠用這個函數來套用.
在類的繼承中,除了咱們上邊說的這種原型繼承,還有類繼承,以及類繼承與原型繼承的混用,那麼我將在下一章給你們講解.本博文是博主自創,若是轉載請說明出處,謝謝!在下學藝不精,文中若是有什麼錯誤還請高手指正,多謝!