前言
hi大家,我又又又来更新文章了😊,最近在学习《JavaScript设计模式与开发实践》这本书,阅读了前两章以及关于发布-订阅模式这章节内容,对自己有了些些小的启发,所以想及时记录下来。在文章的最后还会聊聊关于自我学习的方向以及如何消除焦虑,内观自己,关注自我成长。好了,那就开始吧🙉
聊聊“多态”
先用一段代码来看看JavaScript中的多态是什么样子的。假设我们想输出不同动物的叫声,让我们想想,怎么实现这段逻辑呢,需要根据不同动物都实现“叫”的功能函数吗?能不能提炼出公共和不同的区别?不同动物区别就是他们的叫声不一样,共同点就是动物们都会“叫”。这就是基于对象的“多态”性来优化代码逻辑,即
多态背后的思想是将“做什么”和“谁去做以及怎样去做”分离开来,也就是将“不变的事物”与“可能改变的事物”分离开来。在这个故事中,动物都会叫,这是不变的,但是不同类型的动物具体怎么叫是可变的。把不变的部分隔离出来,把可变的部分封装起来,这给予了我们扩展程序的能力,程序看起来是可生长的,也是符合开放—封闭原则的,相对于修改代码来说,仅仅增加代码就能完成同样的功能,这显然优雅和安全得多。
代码中的makeSound函数是提炼出公共不变的部分,内部调用animal对象的sound方法,不同动物抽象成类,他们也有自己的sound方法,如果这时想增加狗狗的“汪汪汪”叫声,只需要保证狗狗的对象上存在sound方法,而不需要改动makeSound函数。
var makeSound = function( animal ){ animal.sound(); };
var Duck = function(){}
Duck.prototype.sound = function(){ console.log( ’嘎嘎嘎’ ); };
var Chicken = function(){}
Chicken.prototype.sound = function(){ console.log( ’咯咯咯’ ); };
makeSound( new Duck() ); //嘎嘎嘎
makeSound( new Chicken() ); //咯咯咯
JavaScript语言按照数据类型分类,属于动态类型语言,在编译的时候不需要检查变量类型,一个变量可以在运行时接受这个类型的对象,也可以接受另一种类型的对象,所以对于在JavaScript中实现多态性,是有先天的优势。让我们关注上面的例子,如果makeSound函数接受的参数animal,在静态编译期间就确定了类型,那除了传入指定类型的对象外,其他类型都会报错,反观JavaScript中,animal参数能接收duck与chicken对象,没有类型限制。那么对基于多态性原则的设计模式都提供了很大的便利性。
状态管理
在我们的项目管理中,不同数据会产生交互,如何管理我们的数据即常说的状态管理,常常使用的方式是事件触发与监听、单向数据流、响应式数据流的方式。发布-订阅模式就是基于事件触发与监听的实现。在Vue框架中的事件通信就是“发布-订阅”模式的最佳实践。
发布-订阅模式
先通俗的说说它的概念,发布消息大多时候是异步的,但是有很多功能会依赖异步获取的数据,那么双方可以约定一个事件名,依赖方可以在多个地方订阅这个事件,发布方只需要在合适的时机发布该事件,同时遍历注册这个事件的所有回调函数,把数据按照“推模型”都传给依赖方。常见的比如在购物网站中,用户信息是整个项目中公共的部分,其他功能模块,购物车、猜你喜欢都要依赖用户信息的拉取。
login.succ(function(data){
header.setAvatar( data.avatar); // 设置header模块的头像
nav.setAvatar( data.avatar ); // 设置导航模块的头像
message.refresh(); // 刷新消息列表
cart.refresh(); // 刷新购物车列表
});
如果在获取用户信息的回调函数中,每增加一个功能,就要改动这块的代码,那代码之间的耦合性太高了,只会将这个回调函数功能越垒越高。以下代码会展示如何使用发布-订阅模式优化。
$.ajax( 'http://xxx.com? login', function(data){ // 登录成功
login.trigger( 'loginSucc', data); // 发布登录成功的消息
});
var header = (function(){ // header模块
login.listen( 'loginSucc', function( data){ header.setAvatar( data.avatar ); });
return {
setAvatar: function( data ){
console.log( ’设置header模块的头像’ );
}
}
})();
var nav = (function(){ // nav模块
login.listen( 'loginSucc', function( data ){ nav.setAvatar( data.avatar ); });
return {
setAvatar: function( avatar ){
console.log( ’设置nav模块的头像’ );
}
}
})();
header模块与nav模块只需要关注自己功能实现的部分,同时都订阅了loginSucc事件,当用户登录成功后,发布loginSucc,那么所有订阅该事件的函数都会执行一遍。
看到这里,是不是联想到上面描述的“多态”的概念,将做什么和谁去做区分开来。发布-订阅模式的优势很明显,将时间和对象都进行解耦。但是如果项目中过多的依赖事件触发机制,就会造成事件满天飞,不清楚事件在哪里被调用和触发,后期想删除也“心有余悸”,维护管理很困难。
发布—订阅模式也不是完全没有缺点。创建订阅者本身要消耗一定的时间和内存,而且当你订阅一个消息后,也许此消息最后都未发生,但这个订阅者会始终存在于内存中。另外,发布—订阅模式虽然可以弱化对象之间的联系,但如果过度使用的话,对象和对象之间的必要联系也将被深埋在背后,会导致程序难以跟踪维护和理解。特别是有多个发布者和订阅者嵌套到一起的时候,要跟踪一个bug不是件轻松的事情。
以上只是对发布-订阅模式做了简单的介绍,还可以从以下几个方面思考,
- 能不能删除掉订阅的事件呢?
- 事件重名怎么办,能不能基于命名空间注册事件呢?
- 必须要先订阅再发布吗,假设有用户已经下线了,但是仍然接收了好友的消息,等下次再次上线后消息再次弹出来?
- 能订阅一次,下次发布就不再触发了吗?
Vue中关于发布-订阅的实践
Vue框架设计的事件机制就是基于发布-订阅模式,它是以当前实例作为事件管理中心,事件发布$emit,推送数据。通过$on订阅这个事件,也可以执行一次$once。有兴趣的可以看看vue关于实现这个模式的源码,这里就不贴出来了。
关于自我学习与消除焦虑
最近看了《认知觉醒》这本书,也触动到了自己。之前自己的学习属于“填鸭式”的,只要在一段时间内持续学习,或者读书,就认为自己也明白了也努力了。时间确实蕴含着巨大的能量,付出时间的人从长远来看就是在投资自己。但是成长的前提是,要做出改变,从学习-思考-行动-改变这条动线上来看,自己还处在无效努力和思考的阶段和层面上。
其实认识到上面这一点,也就不用再就纠结其他人取得了多大得成就,因为你们不是在一条赛道上,每个人都有自己的人生轨迹,当自己每次做出改变,就比前一刻得自己,更勇敢、坚定。🥰
结尾
最后总结一下,文中介绍了面向对象程序设计中的“多态”概念,让我们认识到,很多设计模式就是基于“多态”来实现的,同时介绍了发布-订阅模式的作用与实现,还留这几个思考问题,有时间的同学可以自己再研究研究。
我看的是《JavaScript设计模式与开发实践》这本书,会按照阅读的章节,来持续更新文章的,期待下次再续🤞