所謂的對象,就是抽象化的數據自己
我發現Javascript這門語言每次翻開都會帶給人新感覺,尤爲是看完其餘語言的面向對象再來看它,可是若是你也是過來人就必定記得教科書裏面冗長乏味的面向對象,全部的書上都會跟你這麼說:面向對象是對要解決的問題的一種抽象,好比很經典的Java或者C++,class就是根基,而後類實例化出對象balabala....初學者來看的話實際上是很難接受的,可是挺過這個時期,就會產生一種理所應當的感受,就會以爲:面向對象固然須要類啦固然須要實例化啦,否則怎麼繼承的之類的。固然基本上主流的面嚮對象語言都會提供基本相同的概念。可是有個異類就是JavaScript,若是你用其餘語言的概念來理解這個世界裏的對象可能會找不着北。由於這個世界裏沒有「類」這個東西,全部的東西都是對象。
朝下看的時候,我建議學過面向對象的人忘掉「類」這個東西,否則很容易就會搞混。程序員
那到底什麼是對象呢?這個問題問淺了是個傻問題問深了又變成了一個哲學問題,每一個語言甚至每一個人都有不一樣的答案,可是若是你只有十天來設計一門語言的話,你確定也是想把這個東西設計的越簡單越好,因此對JavaScript來講,對象就是屬性+方法,簡單來講就是這樣:安全
var Person={ name :"XXX", age:18, address:"YYY", gender:0, eat:function(){ console.log("食"); }, wear:function(){ console.log("衣"); }, live:function(){ console.log("住"); }, walk:function(){ console.log("行"); } } Person.eat();
這樣一個對象就寫好了,不只如此,咱們在運行時還能夠動態的修改內部對象的屬性,以及增長方法等等很是自由。第一眼看上去很是的直觀,可是仔細想一想看,問題其實不少,好比,屬性這樣無限制的訪問一點安全性都沒有,再好比我想再要生成一我的可是名字叫小紅的,就很費勁,再再好比我怎麼實現繼承?問題不少咱們一個一個來講函數
其實繼承說白了,要作的事情就是把兩個絕不相干的人創建父子關係,可是怎麼創建呢?Javascript語言的對象生來都有一個特殊屬性叫__proto__,咱們能夠用這個屬性來關聯其餘對象,就像這樣:工具
var Teacher={ //這裏添加老師的屬性和方法 __proto__:Person } Teacher.eat();
這樣,兩個對象之間就創建了聯繫,人類(對象)是老師(對象)的原型,用圖表示就是:測試
這樣一條鏈條把對象之間聯繫起來,這樣使用Teacher調用eat方法的時候找不到就順着鏈子朝上找一直找到頭若是沒有就報錯,看起來很完美。this
可是你們都知道高級語言都是要吸引別人來用的,這個方式實在是和其餘語言不太同樣,怎麼吸引其餘人來用呢?本着不行就封裝一層的原則因而語言提供了構造函數,可是這個構造函數到這裏爲止仍是和其餘語言有徹底不一樣的意義(固然使用上區別不大)。spa
function Person(){ this.name=name; this.eat=function(){ console.log(this.name+" eat food now"); } } ming =new Student("xiaoming"); hong =new Student("xiaohong");
這個構造函數的無論從調用方式仍是內部寫法就都頗有Java Class的感受,可是從用途上來講,它其實更靠近的概念是Java中的工廠方法。並且使用的時候還使用了new這個關鍵字,同時解決了上面的那個不用生成一個對象就寫一大串代碼的尷尬。同時還很靈活,你仍是能夠在ming或者hong這個對象上動態添加方法。prototype
可是上面的操做有個很操蛋的問題就是,由於這個語言沒有類只有對象,因此你構造函數裏寫的方法會原封不動的出如今新生成的對象當中,這意味着每一個新生成的對象都有相同的函數,這就很浪費並且對象多了還吃內存。設計者固然也想到了這個問題,他用純對象的思考方式想了一個解決辦法,舉個例子:
如今有一個Person構造函數(上面那樣的),一般使用它來生成新的對象(小紅小明等等)來經過原型鏈來訪問父對象的方法,基於這個模型那我索性就再生成一個對象掛在Person下面,用一個屬性指向它(你們都知道我說的就是prototype這個屬性啦),公共的方法徹底能夠都放在這個對象裏面,可是怎麼調用這些方法呢?其實你們既然都是對象,小明小紅的__proto__裏面寫的只要是Person.prototype就完事了呀,說了這麼多用一個圖來標識一下:設計
對咱們來講只須要關注橫着一排的原型鏈就好了,至於Person構造函數是一個函數天然也是一個對象(函數也是對象),一樣天然有本身的原型鏈可是這裏和主體無關就不體如今圖上了。3d
若是原型鏈的知識都差很少了的話我以爲就能夠放出下面這張廣爲人知的圖來記憶一番了:
而後寫了這麼多,你就發現其實繼承在Javascript當中原來也是一個謊話--只要把新的對象掛上原型鏈就算是「繼承」了。那問題就變成了怎麼構建原型鏈,咱們仍是放上道爺發明的一種方式來實現繼承(方法多種多樣,我以爲道爺的這種橋接而且不污染上下文環境的方式至關好用)
//父類 function Student(props){ this.name=props.name||"unnamed"; } Student.prototype.hello=function(){ console.log('Hello, ' + this.name + '!'); } //子類 function PrimaryStudent(props) { Student.call(this, props); this.grade = props.grade || 1; } //使用一個空構造函數來橋接 function F(){} F.prototype=Student.prototype; //把本來指向Function.prototype的指針指向父類 PrimaryStudent.prototype=new F();//橋接子類對象到一個F的匿名對象上 PrimaryStudent.prototype.constructor=PrimaryStudent;//糾正構造函數指向 PrimaryStudent.prototype.getGrade=function(){ return this.grade; } // 開始測試 var xiaoming=new PrimaryStudent({ name:'xiaoming', grade:2 }); console.log(xiaoming.__proto__===PrimaryStudent.prototype); console.log(xiaoming.__proto__.__proto__===Student.prototype); console.log(xiaoming instanceof PrimaryStudent); console.log(xiaoming instanceof Student);
使用一張圖展現上面的代碼作了什麼:
done!關係就是這麼橋接好的,咱們若是再在外面包一層函數就徹底能夠作工具函數來用
很明顯,上面的這個作法很費勁,或者說,不直觀無法吸引別人來用,因此ES6標準里加了一個很Java的語法糖,就像下面這麼寫:
class Person { constructor(name){ this.name=name; } hello(){ console.log(`${this.name} say hello to you!`); } } class Teacher extends Person{ constructor(name,gender){ super(name); this.gender=gender; } myGender(){ console.log(`${this.name}'s gender is ${this.gender}`); } } var x=new Teacher("niuguangzhe","nan"); x.myGender();
能夠說是Java味道十足,可是別忘了,其實語言的內部仍然是原型鏈。
到這裏,全部關於繼承的東西講完了,接下來準備準備說說Javascript當中的封裝