首頁 > web前端 > js教程 > 主體

JavaScript物件導向輕鬆入門之封

巴扎黑
發布: 2017-06-26 11:52:47
原創
978 人瀏覽過

  本章預設大家已經看過作者的前一篇文章 JavaScript物件導向輕鬆入門之抽象》

為什麼要封裝?

  封裝(Encapsulation)就是把物件的內部屬性和方法隱藏起來,外部程式碼存取該物件只能透過特定的介面訪問,這也是面向介面程式設計思想的一部分。

  封裝是物件導向程式設計裡非常重要的一部分,讓我們來看看沒有封裝的程式碼是什麼樣的:

1     function Dog(){2         this.hairColor = '白色';//string3         this.breed = '贵宾';//string4         this.age = 2;//number5     }6     var dog = new Dog();7     console.log(dog.breed);//log: '贵宾'
登入後複製

 

#  看似沒有什麼問題,但如果breed屬性名修改了怎麼辦?例如換成this.type = ‘貴賓’,那所有使用Dog類別的程式碼都要改變。

  如果類別的程式碼和使用類別的程式碼都是你寫的,並且使用這個類別的地方不多,你這麼寫無所謂。

  但如果使用這個類別的地方比較多,或者協同開發時其它人還要使用你的類,那這樣做就會讓代碼很難維護,正確的做法是:

 1     function Dog(){ 2         this.hairColor = '白色';//string 3         this.age = 2;//number 4         this._breed = '贵宾';//string 5     } 6     Dog.prototype.getBreed = function(){ 7         return this._breed; 8     } 9     Dog.prototype.setBreed = function(val){10         this._breed = val;11     }12     var dog = new Dog();13     console.log(dog.getBreed());//log: '贵宾'14     dog.setBreed('土狗');
登入後複製

 

  getBreed()就是接口,如果內部的屬性改變了,例如breed#換成了 type ,那隻需要改變getBreed()裡的程式碼就可以了,而且你可以監聽到所有取得這個屬性的操作。

  所以封裝有很多好處:

  1、只要介面不改變,內部的實作可以任意改變;

  2、使用者使用起來很方便,不用關係內部是如何實現;

  3、降低程式碼之間的耦合;

  4、滿足大型應用程式和多人協同開發;

#用
getter

/setter

來封裝私有屬性  其實還有另一種封裝屬性的方法,那就是用getter/setter,如下demo,本章不講原理,只講使用,原理可自行查資料:

##
 1     function Dog(){ 2         this.hairColor = '白色';//string 3         this.age = 2;//number 4         this._breed = '贵宾';//string 5         Object.defineProperty(this, 'breed', {//传入this和属性名 6             get : function () { 7                 console.log('监听到了有人调用这个get breed') 8                 return this._breed; 9             },10             set : function (val) {11                 this._breed = val;12                 /*13                 如果不设置setter的话默认这个属性是不可设置的14                 但有点让人诟病的是,浏览器并不会报错15                 所以即使你想让breed是只读的,你也应该设置一个setter让其抛出错误:16                 throw 'attribute "breed"  is read only!';17                 */18             }19         });20     }21     var dog = new Dog();22     console.log(dog.breed);23     /*log:24         '监听到了有人调用这个get breed接口'25         '贵宾'26     */27     dog.breed = '土狗';28     console.log(dog.breed);29     /*log:30         '监听到了有人调用这个get breed接口'31         '土狗'32     */
登入後複製

  但這個方法寫起來比較繁瑣,作者一般是用getBreed()這個方法,getter

/setter

一般用在

readonly

的屬性和一些比較重要的接口,以及重構沒有封裝接口的屬性操作。

  還可以用閉包封裝私有屬性,是最安全的,但會產生額外的記憶體開銷,所以作者不是很喜歡用,大家可自行了解。

公有

/#私有概念

#  前兩小節我們簡單的了解下封裝,但這些肯定是不夠用的,下面的我們先來了解下幾個概念:  私有屬性:即只能在類別的內部調取得、修改的屬性,不允許外部存取。

  私有方法:僅供類別內部呼叫的方法,禁止外部呼叫。   公有屬性:可供類別外部取得、修改的屬性。理論上講類的所有屬性都應該是私有屬性,只能透過封裝的介面來訪問,但一些比較小的類,或者使用次數比較少的類,你覺得比較方便的話也可以不封裝介面。   公有方法:可供外部呼叫的方法,實作介面的方法如

getBreed()

就是公有方法,以及對外暴露的行為方法。

  靜態屬性、靜態方法

:類別本身的屬性與方法。這個就沒必要區分公有私有了,所有的靜態屬性、靜態方法都必須是私有的,一定要透過封裝介面訪問,這也是上一章中作者為什麼要用

getInstanceNumber()

來訪問Dog.instanceNumber

屬性。
######    ###ES5 demo如下###:######
 1     function Dog(){ 2         /*公有属性*/ 3         this.hairColor = null;//string 4         this.age = null;//number 5         /*私有属性,人们共同约定私有属性、私有方法前面加上_以便区分*/ 6         this._breed = null;//string 7         this._init(); 8         /*属性的初始化最好放一个私有方法里,构造函数最好只用来声明类的属性和调用方法*/ 9         Dog.instanceNumber++;10     }11     /*静态属性*/12     Dog.instanceNumber = 0;13     /*私有方法,只能类的内部调用*/14     Dog.prototype._init = function(){15         this.hairColor = '白色';16         this.age = 2;17         this._breed = '贵宾';18     }19     /*公有方法:获取属性的接口方法*/20     Dog.prototype.getBreed = function(){21         console.log('监听到了有人调用这个getBreed()接口')22         return this._breed;23     }24     /*公有方法:设置属性的接口方法*/25     Dog.prototype.setBreed = function(breed){26         this._breed = breed;27         return this;28         /*这是一个小技巧,可以链式调用方法,只要公有方法没有返回值都建议返回this*/29     }30     /*公有方法:对外暴露的行为方法*/31     Dog.prototype.gnawBone = function() {32         console.log('这是本狗最幸福的时候');33         return this;34     }35     /*公有方法:对外暴露的静态属性获取方法*/36     Dog.prototype.getInstanceNumber = function() {37         return Dog.instanceNumber;//也可以this.constructor.instanceNumber38     }39     var dog = new Dog();40     console.log(dog.getBreed());41     /*log:42         '监听到了有人调用这个getBreed()接口'43         '贵宾'44     */45     /*链式调用,由于getBreed()不是返回this,所以getBreed()后面就不可以链式调用了*/46     var dogBreed = dog.setBreed('土狗').gnawBone().getBreed();47     /*log:48         '这是本狗最幸福的时候'49         '监听到了有人调用这个getBreed()接口'50     */51     console.log(dogBreed);//log: '土狗'52     console.log(dog);
登入後複製
###### ######  ###ES6 demo###(新手可不看ES6和TypeScrpt實作部分):######
 1     class Dog{ 2         constructor(){ 3             this.hairColor = null;//string 4             this.age = null;//number 5             this._breed = null;//string 6             this._init(); 7             Dog.instanceNumber++; 8         } 9         _init(){10             this.hairColor = '白色';11             this.age = 2;12             this._breed = '贵宾';13         }14         get breed(){15             /*其实就是通过getter实现的,只是ES6写起来更简洁*/16             console.log('监听到了有人调用这个get breed接口');17             return this._breed;18         }19         set breed(breed){20             /*跟ES5一样,如果不设置的话默认breed无法被修改,而且不会报错*/21             console.log('监听到了有人调用这个set breed接口');22             this._breed = breed;23             return this;24         }25         gnawBone() {26             console.log('这是本狗最幸福的时候');27             return this;28         }29         getInstanceNumber() {30             return Dog.instanceNumber;31         }32     }33     Dog.instanceNumber = 0;34     var dog = new Dog();35     console.log(dog.breed);36     /*log:37         '监听到了有人调用这个get breed接口'38         '贵宾'39     */40     dog.breed = '土狗';//log:'监听到了有人调用这个set breed接口'41     console.log(dog.breed);42     /*log:43         '监听到了有人调用这个get breed接口'44         '土狗'45     */
登入後複製
####

 

  ES5ES6中虽然我们把私有属性和方法用“_”放在名字前面以区分,但外部还是可以访问到属性和方法的。

  TypeScrpt中就比较规范了,可以声明私有属性,私有方法,并且外部是无法访问私有属性、私有方法的:

 

 1     class Dog{ 2         public hairColor: string; 3         readonly age: number;//可声明只读属性 4         private _breed: string;//虽然声明了private,但还是建议属性名加_以区分 5         static instanceNumber: number = 0;//静态属性 6         constructor(){ 7             this._init(); 8             Dog.instanceNumber++; 9         }10         private _init(){11             this.hairColor = '白色';12             this.age = 2;13             this._breed = '贵宾';14         }15         get breed(){16             console.log('监听到了有人调用这个get breed接口');17             return this._breed;18         }19         set breed(breed){20             console.log('监听到了有人调用这个set breed接口');21             this._breed = breed;22         }23         public gnawBone() {24             console.log('这是本狗最幸福的时候');25             return this;26         }27         public getInstanceNumber() {28             return Dog.instanceNumber;29         }30     }31     let dog = new Dog();32     console.log(dog.breed);33     /*log:34         '监听到了有人调用这个get breed接口'35         '贵宾'36     */37     dog.breed = '土狗';//log:'监听到了有人调用这个set breed接口'38     console.log(dog.breed);39     /*log:40         '监听到了有人调用这个get breed接口'41         '土狗'42     */43     console.log(dog._breed);//报错,无法通过编译44     dog._init();//报错,无法通过编译
登入後複製

 

注意事项:

  1、暴露给别人的类,多个类组合成一个类时,所有属性一定都要封装起来;

  2、如果你来不及封装属性,可以后期用getter/setter弥补;

  3、每个公有方法,最好注释一下含义;

  4、在重要的类前面最好用注释描述所有的公有方法;

后话

  如果你喜欢作者的文章,记得收藏,你的点赞是对作者最大的鼓励;

  作者会尽量每周更新一章,下一章是讲继承;

  大家有什么疑问可以留言或私信作者,作者尽量第一时间回复大家;

  如果老司机们觉得那里可以有不恰当的,或可以表达的更好的,欢迎指出来,我会尽快修正、完善。

 

以上是JavaScript物件導向輕鬆入門之封的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!