この章は、皆さんが著者の以前の記事を読んでいることを前提としています 「JavaScriptオブジェクト指向の簡単入門抽象化」
カプセル化とは、オブジェクトの内部プロパティとメソッドを隠すことであり、外部コードは特定のインターフェイスを介してのみオブジェクトにアクセスできます。これもインターフェイス指向プログラミングの考え方の一部です。
カプセル化はオブジェクト指向プログラミングの非常に重要な部分です。カプセル化されていないコードがどのようになるかを見てみましょう:
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 = ‘VIP’, に変更する場合、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を使用してプライベートプロパティをカプセル化します。実際、プロパティをカプセル化する別の方法があります。それは、以下の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
は一般にクロージャを使用してプライベート プロパティをカプセル化することもできます。これは最も安全ですが、追加のメモリ オーバーヘッドが発生するため、作成者はあまり使用しません。これについては自分で学習してください。 パブリック/プライベートの概念 最初の 2 つのセクションではカプセル化について簡単に理解しましたが、これだけでは絶対に十分ではありません。まず次の概念を理解しましょう:
プライベート属性: クラス外で取得、変更できる属性。理論的には、クラスのすべての属性はプライベート属性である必要があり、カプセル化されたインターフェイスを介してのみアクセスできます。ただし、一部の小さなクラスや使用頻度が低いクラスの場合、インターフェイスをカプセル化する必要はありません。
パブリック メソッド
: 外部から呼び出すことができるメソッド。getBreed() などのインターフェイスを実装するメソッドは、外部に公開される動作メソッドと同様にパブリック メソッドです。
静的プロパティ、静的メソッド
: クラス自体のプロパティとメソッド。パブリックとプライベートを区別する必要はありません。すべての静的プロパティと静的メソッドはプライベートである必要があり、カプセル化されたインターフェイスを通じてアクセスする必要があります。これが、前の章で作者がDog にアクセスするために getInstanceNumber() を使用した理由です。インスタンス番号プロパティ。
ES5 デモは次のとおりです:
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);
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 */
ES5、ES6中虽然我们把私有属性和方法用“_”放在名字前面以区分,但外部还是可以访问到属性和方法的。
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 中国語 Web サイトの他の関連記事を参照してください。