js设计模式

一 单例模式

单例模式的定义是产生一个类的唯一实例,但js本身是一种“无类”语言。很多讲js设计模式的文章把{}当成一个单例来使用也勉强说得通。因为js生成对象的方式有很多种,我们来看下另一种更有意义的单例。

有这样一个常见的需求,点击某个按钮的时候需要在页面弹出一个遮罩层。比如web.qq.com点击登录的时候.

JavaScript

var createMask = function(){
 
   return document,body.appendChild(  document.createElement(div)  );
 
}

JavaScript

$( 'button' ).click( function(){
 
   Var mask  = createMask();
 
   mask.show();
 
}) 

问题是, 这个遮罩层是全局唯一的, 那么每次调用createMask都会创建一个新的div, 虽然可以在隐藏遮罩层的把它remove掉. 但显然这样做不合理.

再看下第二种方案, 在页面的一开始就创建好这个div. 然后用一个变量引用它.

JavaScript

var mask = document.body.appendChild( document.createElement( ''div' ) );
 
$( ''button' ).click( function(){
 
   mask.show();
 
} ) 

这样确实在页面只会创建一个遮罩层div, 但是另外一个问题随之而来, 也许我们永远都不需要这个遮罩层, 那又浪费掉一个div, 对dom节点的任何操作都应该非常吝啬.

如果可以借助一个变量. 来判断是否已经创建过div呢?

JavaScript

var mask;
 
var createMask = function(){
 
if ( mask ) return mask;
 
else{
 
mask = document,body.appendChild(  document.createElement(div)  );
 
return mask;
 
}
 
} 

看起来不错, 到这里的确完成了一个产生单列对象的函数. 我们再仔细看这段代码有什么不妥.

首先这个函数是存在一定副作用的, 函数体内改变了外界变量mask的引用, 在多人协作的项目中, createMask是个不安全的函数. 另一方面, mask这个全局变量并不是非需不可. 再来改进一下.

JavaScript

var createMask = function(){
  var mask;
  return function(){
       return mask || ( mask = document.body.appendChild( document.createElement('div') ) )
  }
}();

用了个简单的闭包把变量mask包起来, 至少对于createMask函数来讲, 它是封闭的.

前面那个单例还是有缺点. 它只能用于创建遮罩层. 假如我又需要写一个函数, 用来创建一个唯一的xhr对象呢? 能不能找到一个通用的singleton包装器.

js中函数是第一型, 意味着函数也可以当参数传递. 看看最终的代码.

var singleton = function( fn ){
    var result;
    return function(){
        return result || ( result = fn .apply( this, arguments ) );
    }
}
 
var createMask = singleton( function(){
 
return document.body.appendChild( document.createElement('div') );
 
 });

用一个变量来保存第一次的返回值, 如果它已经被赋值过, 那么在以后的调用中优先返回该变量. 而真正创建遮罩层的代码是通过回调函数的方式传人到singleton包装器中的. 这种方式其实叫桥接模式. 关于桥接模式, 放在后面一点点来说.

然而singleton函数也不是完美的, 它始终还是需要一个变量result来寄存div的引用. 遗憾的是js的函数式特性还不足以完全的消除声明和语句.

二 简单工厂模式

简单工厂模式是由一个方法来决定到底要创建哪个类的实例, 而这些实例经常都拥有相同的接口. 这种模式主要用在所实例化的类型在编译期并不能确定, 而是在执行期决定的情况。 说的通俗点,就像公司茶水间的饮料机,要咖啡还是牛奶取决于你按哪个按钮。

<meta charset="utf-8"/>
<script type="text/javascript">
    //工厂该有厂长来决定到底运行哪条产品线
    //消费者=》子类
    var gongchang={};
    gongchang.chanyifu=function(){
        this.gongren=50;
        alert('我们有'+this.gongren+'个工人');
    };
    gongchang.chanxie=function(){
        alert('产鞋子~');
    };
    gongchang.yunshu=function(){
        alert('运输~');
    };
    gongchang.changzhang=function(para){
        //new js 使用了构造函数模式 单例模式
        new gongchang[para]();
    };
    var me=gongchang.changzhang('chanyifu');
</script>

模式作用:

  1.对象的构建十分复杂;

  2.需要依赖具体的环境创建不同的实例;

  3.处理大量具有相同属性的小对象;

注意事项:

  1.不能滥用工厂,有时候仅仅是给代码增加复杂度;

三 观察者模式

观察者模式( 又叫发布者-订阅者模式 )应该是最常用的模式之一. 在很多语言里都得到大量应用. 包括我们平时接触的dom事件. 也是js和dom之间实现的一种观察者模式.

 先看看生活中的观察者模式,好莱坞有句名言. “不要给我打电话, 我会给你打电话”. 这句话就解释了一个观察者模式的来龙去脉。 其中“我”是发布者, “你”是订阅者。

再举个例子,我来公司面试的时候,完事之后每个面试官都会对我说:“请留下你的联系方式, 有消息我们会通知你”。 在这里“我”是订阅者, 面试官是发布者。所以我不用每天或者每小时都去询问面试结果, 通讯的主动权掌握在了面试官手上,而我只需要提供一个联系方式。观察者模式可以很好的实现2个模块之间的解耦。

原文地址:https://www.cnblogs.com/Iona/p/4729554.html