JavaScript面向对象轻松入门之封装(demo by ES5、ES6、TypeScript)
本章默认大家已经看过作者的前一篇文章 《JavaScript面向对象轻松入门之抽象》
为什么要封装?
封装(Encapsulation)就是把对象的内部属性和方法隐藏起来,外部代码访问该对象只能通过特定的接口访问,这也是面向接口编程思想的一部分。
封装是面向对象编程里非常重要的一部分,让我们来看看没有封装的代码是什么样的:
function Dog(){
this.hairColor = '白色';//string
this.breed = '贵宾';//string
this.age = 2;//number
}
var dog = new Dog();
console.log(dog.breed);//log: '贵宾'
看似没有什么问题,但如果breed属性名修改了怎么办?比如换成this.type = ‘贵宾’,那所有使用Dog类的代码都要改变。
如果类的代码和使用类的代码都是你写的,并且使用这个类的地方不多,你这么写无所谓。
但如果使用这个类的地方比较多,或者协同开发时其它人还要使用你的类,那这样做就会让代码很难维护,正确的做法是:
function Dog(){
this.hairColor = '白色';//string
this.age = 2;//number
this._breed = '贵宾';//string
}
Dog.prototype.getBreed = function(){
return this._breed;
}
Dog.prototype.setBreed = function(val){
this._breed = val;
}
var dog = new Dog();
console.log(dog.getBreed());//log: '贵宾'
dog.setBreed('土狗');
getBreed()就是接口,如果内部的属性变化了,比如breed换成了type ,那只需要改变getBreed()里的代码就可以了,并且你可以监听到所有获取这个属性的操作。
所以封装有很多好处:
1、只要接口不改变,内部的实现可以任意改变;
2、使用者使用起来很方便,不用关系内部是如何实现;
3、降低代码之间的耦合;
4、满足大型应用程序和多人协同开发;
用getter/setter来封装私有属性
其实还有另一种封装属性的方法,那就是用getter/setter,如下demo,本章不讲原理,只讲使用,原理可自行查资料:
function Dog(){
this.hairColor = '白色';//string
this.age = 2;//number
this._breed = '贵宾';//string
Object.defineProperty(this, 'breed', {//传入this和属性名
get : function () {
console.log('监听到了有人调用这个get breed')
return this._breed;
},
set : function (val) {
this._breed = val;
/*
如果不设置setter的话默认这个属性是不可设置的
但有点让人诟病的是,浏览器并不会报错
所以即使你想让breed是只读的,你也应该设置一个setter让其抛出错误:
throw 'attribute "breed" is read only!';
*/
}
});
}
var dog = new Dog();
console.log(dog.breed);
/*log:
'监听到了有人调用这个get breed接口'
'贵宾'
*/
dog.breed = '土狗';
console.log(dog.breed);
/*log:
'监听到了有人调用这个get breed接口'
'土狗'
*/
但这种方法写起来比较繁琐,作者一般是用getBreed()这种方法,getter/setter一般用在readonly的属性和一些比较重要的接口,以及重构没有封装接口的属性操作。
还可以用闭包封装私有属性,是最安全的,但会产生额外的内存开销,所以作者不是很喜欢用,大家可自行了解。
公有/私有概念
前两小节我们简单的了解了下封装,但这些肯定是不够用的,下面的我们先来了解下几个概念:
私有属性:即只能在类的内部调获取、修改的属性,不允许外部访问。
私有方法:仅供类内部调用的方法,禁止外部调用。
公有属性:可供类外部获取、修改的属性。理论上讲类的所有属性都应该是私有属性,只能通过封装的接口访问,但一些比较小的类,或者使用次数比较少的类,你觉得比较方便的话也可以不封装接口。
公有方法:可供外部调用的方法,实现接口的方法如getBreed()就是公有方法,以及对外暴露的行为方法。
静态属性、静态方法:类本身的属性和方法。这个就没必要区分公有私有了,所有的静态属性、静态方法都必须是私有的,一定要通过封装接口访问,这也是上一章中作者为什么要用getInstanceNumber()来访问Dog.instanceNumber属性。
ES5 demo如下:
function Dog(){
/*公有属性*/
this.hairColor = null;//string
this.age = null;//number
/*私有属性,人们共同约定私有属性、私有方法前面加上_以便区分*/
this._breed = null;//string
this._init();
/*属性的初始化最好放一个私有方法里,构造函数最好只用来声明类的属性和调用方法*/
Dog.instanceNumber++;
}
/*静态属性*/
Dog.instanceNumber = 0;
/*私有方法,只能类的内部调用*/
Dog.prototype._init = function(){
this.hairColor = '白色';
this.age = 2;
this._breed = '贵宾';
}
/*公有方法:获取属性的接口方法*/
Dog.prototype.getBreed = function(){
console.log('监听到了有人调用这个getBreed()接口')
return this._breed;
}
/*公有方法:设置属性的接口方法*/
Dog.prototype.setBreed = function(breed){
this._breed = breed;
return this;
/*这是一个小技巧,可以链式调用方法,只要公有方法没有返回值都建议返回this*/
}
/*公有方法:对外暴露的行为方法*/
Dog.prototype.gnawBone = function() {
console.log('这是本狗最幸福的时候');
return this;
}
/*公有方法:对外暴露的静态属性获取方法*/
Dog.prototype.getInstanceNumber = function() {
return Dog.instanceNumber;//也可以this.constructor.instanceNumber
}
var dog = new Dog();
console.log(dog.getBreed());
/*log:
'监听到了有人调用这个getBreed()接口'
'贵宾'
*/
/*链式调用,由于getBreed()不是返回this,所以getBreed()后面就不可以链式调用了*/
var dogBreed = dog.setBreed('土狗').gnawBone().getBreed();
/*log:
'这是本狗最幸福的时候'
'监听到了有人调用这个getBreed()接口'
*/
console.log(dogBreed);//log: '土狗'
console.log(dog);
ES6 demo(新手可不看ES6和TypeScrpt实现部分):
class Dog{
constructor(){
this.hairColor = null;//string
this.age = null;//number
this._breed = null;//string
this._init();
Dog.instanceNumber++;
}
_init(){
this.hairColor = '白色';
this.age = 2;
this._breed = '贵宾';
}
get breed(){
/*其实就是通过getter实现的,只是ES6写起来更简洁*/
console.log('监听到了有人调用这个get breed接口');
return this._breed;
}
set breed(breed){
/*跟ES5一样,如果不设置的话默认breed无法被修改,而且不会报错*/
console.log('监听到了有人调用这个set breed接口');
this._breed = breed;
return this;
}
gnawBone() {
console.log('这是本狗最幸福的时候');
return this;
}
getInstanceNumber() {
return Dog.instanceNumber;
}
}
Dog.instanceNumber = 0;
var dog = new Dog();
console.log(dog.breed);
/*log:
'监听到了有人调用这个get breed接口'
'贵宾'
*/
dog.breed = '土狗';//log:'监听到了有人调用这个set breed接口'
console.log(dog.breed);
/*log:
'监听到了有人调用这个get breed接口'
'土狗'
*/
ES5、ES6中虽然我们把私有属性和方法用“_”放在名字前面以区分,但外部还是可以访问到属性和方法的。
TypeScrpt中就比较规范了,可以声明私有属性,私有方法,并且外部是无法访问私有属性、私有方法的:
class Dog{
public hairColor: string;
readonly age: number;//可声明只读属性
private _breed: string;//虽然声明了private,但还是建议属性名加_以区分
static instanceNumber: number = 0;//静态属性
constructor(){
this._init();
Dog.instanceNumber++;
}
private _init(){
this.hairColor = '白色';
this.age = 2;
this._breed = '贵宾';
}
get breed(){
console.log('监听到了有人调用这个get breed接口');
return this._breed;
}
set breed(breed){
console.log('监听到了有人调用这个set breed接口');
this._breed = breed;
}
public gnawBone() {
console.log('这是本狗最幸福的时候');
return this;
}
public getInstanceNumber() {
return Dog.instanceNumber;
}
}
let dog = new Dog();
console.log(dog.breed);
/*log:
'监听到了有人调用这个get breed接口'
'贵宾'
*/
dog.breed = '土狗';//log:'监听到了有人调用这个set breed接口'
console.log(dog.breed);
/*log:
'监听到了有人调用这个get breed接口'
'土狗'
*/
console.log(dog._breed);//报错,无法通过编译
dog._init();//报错,无法通过编译
注意事项:
1、暴露给别人的类,多个类组合成一个类时,所有属性一定都要封装起来;
2、如果你来不及封装属性,可以后期用getter/setter弥补;
3、每个公有方法,最好注释一下含义;
4、在重要的类前面最好用注释描述所有的公有方法;
后话
如果你喜欢作者的文章,记得收藏,你的点赞是对作者最大的鼓励;
作者会尽量每周更新一章,下一章是讲继承;
大家有什么疑问可以留言或私信作者,作者尽量第一时间回复大家;
如果老司机们觉得那里可以有不恰当的,或可以表达的更好的,欢迎指出来,我会尽快修正、完善。
JavaScript面向对象轻松入门之封装(demo by ES5、ES6、TypeScript)的更多相关文章
- JavaScript面向对象轻松入门之概述(demo by ES5、ES6、TypeScript)
写在前面的话 这是一个JavaScript面向对象系列的文章,本篇文章主要讲概述,介绍面向对象,后面计划还会有5篇文章,讲抽象.封装.继承.多态,最后再来一个综合. 说实话,写JavaScript面向 ...
- JavaScript面向对象轻松入门之继承(demo by ES5、ES6)
继承是面向对象很重要的一个概念,分为接口继承和实现继承,接口继承即为继承某个对象的方法,实现继承即为继承某个对象的属性.JavvaScript通过原型链来实现接口继承.call()或apply()来实 ...
- JavaScript面向对象轻松入门之多态(demo by ES5、ES6、TypeScript)
多态(Polymorphism)按字面的意思就是"多种状态",同样的行为(方法)在不同对象上有不同的状态. 在OOP中很多地方都要用到多态的特性,比如同样是点击鼠标右键,点击快捷方 ...
- JavaScript面向对象轻松入门之抽象(demo by ES5、ES6、TypeScript)
抽象的概念 狭义的抽象,也就是代码里的抽象,就是把一些相关联的业务逻辑分离成属性和方法(行为),这些属性和方法就可以构成一个对象. 这种抽象是为了把难以理解的代码归纳成与现实世界关联的概念,比如小狗这 ...
- JavaScript面向对象轻松入门之综合
javascrpit面向对象之综合 这一章是对前几章的一个总结,通过一个案例来综合认识javascript面向对象的基本用法 需求: 几乎所有的web应用都需要保存数据一些到本地,那么我们就来 ...
- Javascript 面向对象编程—继承和封装
前 言 Javascript是一种基于对象(object-based)的语言,你遇到的所有东西几乎都是对象.但是,它又不是一种真正的面向对象编程(OOP)语言,因为它的语法中没有class(类) ...
- JavaScript面向对象编程入门
来源极客网 function Person() { var _this = {} //创建一个空的对象,接着我们利用这个"空的对象"承载Person的属性和方法 _this.say ...
- Javascript面向对象三大特性(封装性、继承性、多态性)详解及创建对象的各种方法
Javascript基于对象的三大特征和C++,Java面向对象的三大特征一样,都是封装(encapsulation).继承(inheritance )和多态(polymorphism ).只不过实现 ...
- Javascript 面向对象编程1:封装
Javascript是一种基于对象(object-based)的语言,你遇到的所有东西几乎都是对象.但是,他又不是一种真正的面向对象编程语言,因为它的语法中没有class(类). 那么,如果我们要把& ...
随机推荐
- Ruby中有意思的块
块:是在调用方法时,能与参数一起传递的多个处理的集合 简单点说,跟在方法执行后面的do |变量| end就是一个块,这个块会被传入方法中去执行! 这个非常厉害,非常有意思! 在ruby中,如果需要便利 ...
- hadoop+hive+spark搭建(三)
一.spark安装 因为之前安装过hadoop,所以,在“Choose a package type”后面需要选择“Pre-build with user-provided Hadoop [can ...
- yum 安装vim编辑器
在我们使用的vi编辑器中没有配色不是很好用,我们安装一个vim富文本编辑器方便我们的文本编辑. [root@localhost ~]# yum install vim -y已加载插件:fastestm ...
- Sublime 3 如何使用列编辑模式
前言 作为一名运维人员,文本IDE的列编辑模式对工作效率的提升很大.以前总用UE,觉得UE的列编辑模式设置很人性化(alt+c).后来接触了sublime,个人觉得它的列编辑模式使用对比UE略有差距. ...
- Bootstrap Flat UI的select下拉框显示不出来 问题解决
Bootstrap Flat UI的select下拉框显示不出来?看这里,恰巧今天我也遇到了这个问题: 点击Messages后并没有出现下拉列表,然而官网的index.html却能显示出来. 经过一番 ...
- 将 Eclipse 的配色改为黑底白字
1.先到 eclipsecolorthemes下载一个主题. 2.Eclipse File-->Import 3.Import视窗内选择 General-->Preferences 4.选 ...
- TPshop入门大纲
笔记大纲: tpshop目录结构 功能模块 函数库 重要配置 助手函数 插件 模板 1.TPshop目录结构 看这个图,目录结构一目了然. 下面要讲的内容也是根据这个图展开的. 2.功能模块 前几天刚 ...
- Servlet的线程安全
Servlet的线程安全 一.什么是Servlet的线程安全 1.在Servlet的整个生命周期中,构造方法只被执行一次.也就是说,在Servlet的整个生命周期中,只存在一个Servlet实例对象. ...
- Oracle 12C 新特性之非分区表转分区表online clause(不停业务+索引有效)
12c以前非分区表需要转换为分区, 如果不停业务的话可以使用在线重定义,只有在表进行切换的时候会有短暂的锁表. 12c 中alter table online clause 实现了表上现有的索引有效, ...
- css文字溢出隐藏,及强制断句
只显示一行文字,便溢出隐藏 text-overflow: ellipsis; white-place: nowrap; overflow: hidden; 显示 n 行文字后便溢出隐藏 displa ...