狹義的抽象,也就是代碼裏的抽象,就是把一些相關聯的業務邏輯分離成屬性和方法(行爲),這些屬性和方法就能夠構成一個對象。javascript
這種抽象是爲了把難以理解的代碼概括成與現實世界關聯的概念,好比小狗這樣一個對象:屬性能夠概括出「毛色」、「品種」、「年齡」等等;方法(行爲)能夠概括出「叫」、「跑」、「啃骨頭」等。java
注意:這裏的抽象不是指抽象類,抽象類我認爲放封裝一節講比較合適。瀏覽器
Javascript裏建立一個對象有不少種方法,也很是簡單,就以小狗這個對象爲例:函數
1 var dog = { 2 hairColor: '白色', 3 breed: '貴賓', 4 age: 2, 5 shout: function() { 6 console.log('汪!汪!汪!'); //這裏是你的業務邏輯代碼,這裏我就簡單用這個來代替 7 }, 8 run: function() { 9 console.log('吃我灰吧,哈哈!'); 10 }, 11 gnawBone: function() { 12 console.log('這是本狗最幸福的時候'); 13 } 14 };
很是便捷,但這時候有個問題:我要建立不少只dog怎麼辦?每建立一隻dog我都var一遍嗎?this
因而這時候咱們就引入了類(class)的概念,類即爲相似,具備相同特徵的對象的原型,它的做用是建立對象(實例),類自己並不存在內存中,當運行類的代碼時,一個對象(實例/instance)就被建立在內存中了,能夠簡單的理解類爲創造對象的工廠。spa
Javascript(ES5)裏沒有類(class)這東西,它是經過構造函數來實現類的:prototype
1 /*類的建立*/ 2 function Dog() { 3 //構造函數:人們一致協定把構造函數的名字(即類名),首字母大寫,以便區分 4 this.hairColor = '白色'; 5 /*this指向被創造的對象(實例),若是不明白能夠簡單的理解爲給對象(this)賦予hairColor這個屬性 6 */ 7 this.breed = '貴賓'; 8 this.age = 2; 9 this.runSpeed = null; //string 10 /*屬性的聲明必定要放在構造函數的最頂部; 11 有的屬性可能一開始沒有初始值,會在方法裏才賦值,但你必定要在構造函數裏聲明一下 12 有必要的話再聲明一下屬性的類型 13 */ 14 } 15 Dog.prototype.shout = function() { 16 /*咱們把方法追加到構造函數的prototype屬性,而不是直接在構造函數裏用this.shout = function(){}; 17 這樣的好處是會讓Dog創造的全部對象都共享一個方法,從而節約內存; 18 通常來講屬性在構造函數裏賦予,方法在prototype裏賦予; 19 更多prototype的知識就看書去吧,這裏不會深講,做者要保持本章知識的封裝性; 20 */ 21 console.log('汪!汪!汪!我是一隻' + this.age + '歲的' + this.hairColor + this.breed); 22 //方法裏經過this能夠訪問屬性 23 } 24 Dog.prototype.run = function() { 25 this.runSpeed = '10m/s'; 26 console.log('吃我灰吧,哈哈!本狗的速度但是有' + this.runSpeed); 27 } 28 Dog.prototype.gnawBone = function() { 29 console.log('這是本狗最幸福的時候'); 30 } 31 /*對象(實例)的建立與使用*/ 32 var dog1 = new Dog(); // 33 console.log(dog1.breed); //log: '貴賓' 34 dog1.shout(); //log: '汪!汪!汪!我是一隻2歲的白色貴賓' 35 var dog2 = new Dog(); //建立多隻dog(對象/實例) 36 var dog3 = new Dog(); 37 /*dog一、dog二、dog3這些對象的屬性是各自的,但方法是共享的*/ 38 dog1.hairColor = '黑色'; //修改dog1的屬性 39 console.log(dog1.hairColor); //log: '黑色';dog1屬性已被修改; 40 console.log(dog2.hairColor); //'白色';其它對象不受影響; 41 console.log(dog3.hairColor); //log: '白色' 42 console.log(dog1.shout === dog2.shout); //log: true;dog1的shout方法和dog2的是同一個方法;
但新的問題又來了:我想建立一個棕色的泰迪怎麼辦呢?建立的時候傳遞參數給類的構造函數就能夠解決這問題。
上個案例中說了,類建立的各個對象(實例)的方法的共享的,屬性是各自的,但我就是想建立一個共享的屬性怎麼辦呢?好比說我想建立一個instanceNumber屬性來記錄程序中dog對象(實例)的個數。code
這時候就能夠給這個Dog類建立一個靜態屬性,靜態屬性是屬於類的,因此它是不會隨對象(實例)的變化而變化,能夠用來設置該類的全局變量,全局參數配置等。對象
下面是新代碼:blog
1 function Dog(hairColor, breed, age) { 2 this.hairColor = hairColor; //string,這種依賴參數的屬性最好聲明下類型或接口; 3 this.breed = breed; //string 4 this.age = age; //number 5 this.runSpeed = null; //string 6 Dog.instanceNumber++; 7 } 8 Dog.instanceNumber = 0; //建立靜態屬性 9 Dog.prototype.shout = function() { 10 console.log('汪!汪!汪!我是一隻' + this.age + '歲的' + this.hairColor + this.breed); 11 } 12 Dog.prototype.run = function() { 13 this.runSpeed = '10m/s'; 14 console.log('吃我灰吧,哈哈!本狗的速度但是有' + this.runSpeed); 15 } 16 Dog.prototype.gnawBone = function() { 17 console.log('這是本狗最幸福的時候'); 18 } 19 Dog.prototype.getInstanceNumber = function() { //爲訪問靜態屬性封裝方法 20 return Dog.instanceNumber; 21 } 22 var dog1 = new Dog('白色', '貴賓', 2); 23 console.log(Dog.instanceNumber); //log: 1;雖然能夠這樣訪問靜態屬性,而且還能夠修改它,但堅定不推薦這樣作 24 console.log(dog1.getInstanceNumber()); //log: 1;正確的作法!爲何要這樣作,在封裝一節會詳細講 25 var dog2 = new Dog('棕色', '泰迪', 1); 26 console.log(dog1.getInstanceNumber()); //log: 2; 27 var dog3 = new Dog('黑色', '土狗', 3); 28 console.log(dog1.getInstanceNumber()); //log: 3; 29 dog1.shout(); //log: '汪!汪!汪!我是一隻2歲的白色貴賓' 30 dog2.shout(); //log: '汪!汪!汪!我是一隻1歲的棕色泰迪' 31 dog3.shout(); //log: '汪!汪!汪!我是一隻3歲的黑色土狗'
接下來是ES6的類的建立方法,這段代碼能夠直接在Chrome瀏覽器運行,新手可不用管這部分,包括再下面的TypeScript代碼。
1 class Dog { 2 constructor(hairColor, breed, age) { //表明這個類的構造函數 3 this.hairColor = hairColor; //string 4 this.breed = breed; //string 5 this.age = age; //number 6 this.runSpeed = null; //string 7 Dog.instanceNumber++; 8 } 9 shout() { 10 console.log('汪!汪!汪!我是一隻' + this.age + '歲的' + this.hairColor + this.breed); 11 } 12 run() { 13 this.runSpeed = '10m/s'; 14 console.log('吃我灰吧,哈哈!本狗的速度但是有' + this.runSpeed); 15 } 16 gnawBone() { 17 console.log('這是本狗最幸福的時候'); 18 } 19 getInstanceNumber() { 20 return Dog.instanceNumber; 21 } 22 }//ES6類的建立就比較舒服了,class把整個類用{}包裹在一塊兒,寫法也比較方便。 23 Dog.instanceNumber = 0;//遺憾的是ES6裏也沒有規範靜態屬性,仍是跟ES5同樣的用法,聽說ES7已有一個靜態屬性的提案 24 let dog1 = new Dog('白色', '貴賓', 2); 25 let dog2 = new Dog('棕色', '泰迪', 1); 26 let dog3 = new Dog('黑色', '土狗', 3); 27 dog1.shout(); //log: '汪!汪!汪!我是一隻2歲的白色貴賓' 28 dog2.shout(); //log: '汪!汪!汪!我是一隻1歲的棕色泰迪' 29 dog3.shout(); //log: '汪!汪!汪!我是一隻3歲的黑色土狗' 30 console.log(dog1.getInstanceNumber()); //log: 3;
TypeScript建立對象,TypeScript還有許多有用的特性,但本章不方便介紹更多。
class Dog { hairColor: string;//class的全部屬性必須在頂部所有聲明,不然沒法經過編譯,這讓類的建立更加規範; breed: string;//而且能夠聲明屬性類型,若是給屬性賦值的時候類型不正確也沒法經過編譯,固然,你也能夠不聲明類型或聲明爲any任何類型; age: number; runSpeed: string; static instanceNumber: number = 0;//TS靜態變量的聲明就比較舒服了,在class的{}裏面,保證了代碼的乾淨 constructor(hairColor, breed, age) { this.hairColor = hairColor; this.breed = breed; this.age = age; Dog.instanceNumber++; } shout() { console.log('汪!汪!汪!我是一隻' + this.age + '歲的' + this.hairColor + this.breed); } run() { this.runSpeed = '10m/s'; console.log('吃我灰吧,哈哈!本狗的速度但是有' + this.runSpeed); } gnawBone() { console.log('這是本狗最幸福的時候'); } getInstanceNumber() { return Dog.instanceNumber; } } let dog1 = new Dog('白色', '貴賓', 2); let dog2 = new Dog('棕色', '泰迪', 1); let dog3 = new Dog('黑色', '土狗', 3); dog1.shout();//log: '汪!汪!汪!我是一隻2歲的白色貴賓' dog2.shout();//log: '汪!汪!汪!我是一隻1歲的棕色泰迪' dog3.shout();//log: '汪!汪!汪!我是一隻3歲的黑色土狗' console.log(dog1.getInstanceNumber());//log: 3;
上面的例子是咱們明確知道要建立一個對象(實例)dog,但實際開發當中是沒有人告訴咱們須要建立哪些對象的,領導給咱們的只有需求,因此咱們要分析需求的業務邏輯,把需求分解成一個個對象。
好比說如今領導給咱們一個需求:作一個超市收銀系統,分爲兩種角色:收銀員和管理員,收銀員能夠查詢物品信息、統計價格、錄入帳單信息、打印小票,管理員能夠查看帳單信息、統計帳單信息。
注意需求中的名詞:收銀員、管理員、物品信息、帳單、小票,這些就是自然的對象,這是最初步的抽象。
讓咱們再注意動詞:查詢、統計、錄入、打印,咱們是否是也能夠抽象成對象?查詢器?統計器?
而後咱們開始coding吧,不要糾結本身的抽象是否完美,做者很贊同Facebook的一句標語:Done is better than perfect(比完美更重要的是完成).
當某個對象的代碼不斷膨脹,慢慢超出控制的時候(做者本身的的標準是一個對象儘可能不超過300行代碼),這時候你就得考慮更深層次的抽象了,查找這個對象裏代碼比較多的屬性、方法、而後抽象成另外一個對象,把新對象做爲原先對象的成員(屬性)。
function Dog(){ this._tail = new Tail();//把尾巴tail抽象成另外一個對象,做爲dog的一個屬性; }
固然,你成了老司機後能夠一開始就把一個對象再抽象出許多成員對象,隨你喜歡。
若是你喜歡做者的文章,記得收藏,你的點贊是對做者最大的鼓勵;
做者會盡可能每週更新一章,下一章是講封裝;
你們有什麼疑問能夠留言或私信做者,做者儘可能第一時間回覆你們;
若是老司機們以爲那裏能夠有不恰當的,或能夠表達的更好的,歡迎指出來,做者會盡快修正、完善。