`
enix2212
  • 浏览: 23914 次
  • 性别: Icon_minigender_1
  • 来自: 北京
最近访客 更多访客>>
社区版块
存档分类
最新评论

[Javascript]单例模式(singleton )

 
阅读更多
Javascript中大家都很习惯用new运算符创建实例。现在看看另外一种创建实例的方法------单例模式。
单例模式的意思就是只有一个实例。单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
单例模式的特点有:
1,该类只有一个实例
2,该类自行创建该实例(在该类内部创建自身的实例对象)
3,向整个系统公开这个实例接口 。
下面我们分析一下实现的可行性
第1个特点:Javascript是不支持重载的,如果不能被new的话,多次调用会被覆盖所以第一点也很好实现
第2个特点:自行创建实例这点我们用匿名包装器也比较好实现
第2个特点:javascript是运行在宿主环境中的,我们向global对象公开就可以了,浏览器中window相当于global。
那么从上几点分析,是有可行性的。我们先实现一个雏形。我用一个拖拽列表控件来使用这个单例模式。
这是一个全局变量,我们可以用多种方式来做,如:
普通的:var sortList=(function(){})(); 
文艺的:window.sortList=(function(){})(); this.sortList=(function(){})()
xxx的:sortList=(function(){})(); 


文艺的第一种可以让大家一眼就看到他是一个全局变量。选取那种要看个人爱好和团队。
 
接下来设计我们的代码:
Html和css部分
1.俗话说html是基石,我们先弄好骨架。
<ol id="list">
[*]1 

[*]2 

[*]3 

[*]4 

[*]5 

[*]6 

</ol>

  
2.为了让html跟易用,给他穿上拉风的衣服。
<style type="text/css">
ol{ list-style: url(none) none; }
li{ width: 200px; height: 30px; margin-bottom: 5px; color: #000; text-indent: 25px; font: bold 14px/30px Arial, Helvetica, sans-serif; background: #EEE; border: 1px solid #ccc; border-ridus:5px; border-radius:25px; box-shadow: 4px 5px #fff inset; }
.tips { border:dashed 2px red;background:#EEE; }
#prara p { border-radius:3px; background:#EEE; padding: 10px; }
</style>

万事具备现在来用脚本控制行为。

Javascript部分

1.使用匿名包装器。
var sortList=(function(window,doc,undefined){})(window,document);
这样处理一下,可以把全局变量都变成我们这个单例的局部变量,以避免运行时访问全局变量对象以提高效率。
接下来我们声明各种要使用到的变量。
var sortList=(function(window,doc,undefined){
var lis=null,replace=null,origin=[0,0],parent=null,tag=null,tips=null,listWidth=0,toolkit={};
})(window,document);
Toolkit是我常用的一个微型类库,有常用的事件处理方法和坐标计算模拟用户事件等等。
写程序时我们要考虑到程序的易用性,就是给使用的人不用考虑内部是怎样实现的,只要会调用就可以了。同时还要考虑到灵活性不能过分依赖结构,只要用一些简单的钩子就可以起到css,html,dom三者关联起来。符合这样条件的有id和class,简直太方便了。Id与class既是html属性有可以当css的选择器有可以用dom的api来获取(getElementById/getElementsByClassName/selector API)。可拖动的标签也要通过参数可配置这样才够灵活。
我们在单体里面返回一个匿名函数,单体执行以后我们通过配置返回函数的参数来生成实例。

var sortList=(function(window,doc,undefined){
var lis=null,replace=null,origin=[0,0],parent=null,tag=null,tips=null,listWidth=0,toolkit={};
 return function(O){
};
})(window,document); 


2.设计方案
这个拖拽排序列表可能有多种实现方案,那么怎么自己去设计呢?
首先我们要使列表项成为可拖拽元素,因为他只要实现上下排序,所以我们只要处理他的top值就可以了,要实现这点要使这个元素绝对定位。这样有产生了一个问题,如果我们使一个列表项变为可绝对定位,那么他将不再占位这样会造成页面晃动。怎么处理这个问题呢?
我们在拖拽元素时克隆一下当前的元素当做占位,插入到当前元素的位置,并且在改元素移动时判断和其他列表项的位置关系,把他插入到相应的位置。释放鼠标时,用拖拽元素替换拖拽元素并移除拖拽元素。这样就解决了占位问题,用能够用做提示,一举两得这是不错。
有了理论做支持我们试着用代码去实现他。
3.如果在每个元素上绑定事件的话这样既浪费资源有不能解决动态添加的元素,事件委托是一个好同志。我们监听docment上的mousedown事件如果事件源在我们想要处理的范围内那么就派发事件处理程序。记录鼠标和元素的位置关系,以及该target和其他元素的位置关系。

var sortList=(function(window,doc,undefined){
var lis=null,replace=null,..................,toolkit={.......};
function mousedownSortableList(e){};
function creatReplaceElement(O,E,P){};
function mousemoveCheckThreshold(e){};
return function(O){
};
})(window,document);


4.当鼠标按下并移动时,我们设置目标元素target的top值移动拖拽到相应位置,并且克隆一个target生成一个占位元素。判断占位元素的位置把他插入到相应位置。
5.当鼠标释放时,用target去替换占位元素,并且移除占位元素。

var sortList=(function(window,doc,undefined){
  var lis=null,replace=null,..................,toolkit={.......};
  function mousedownSortableList(e){};
  function creatReplaceElement(O,E,P){};
  function mousemoveCheckThreshold(e){};
  function mouseupCancelThreshold(e){};
  return function(O){

  };
})(window,document);


如果作为一个架构师,工作已经完成了。下面的部分可以交给小弟完成了。但是一般前这样的小控件是不好意思申请小弟的。下面的工作就是填入代码让程序跑起来,具体工作就不一步步演示了。直接上源码。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>sortlist</title>
<style type="text/css">
ol{ list-style: url(none) none; }
li{ width: 200px; height: 30px; margin-bottom: 5px; color: #000; text-indent: 25px; font: bold 14px/30px Arial, Helvetica, sans-serif; background: #EEE; border: 1px solid #ccc; border-ridus:5px; border-radius:25px; box-shadow: 4px 5px #fff inset; }
.tips { border:dashed 2px red;background:#EEE; }
#prara p { border-radius:3px; background:#EEE; padding: 10px; }
</style>
</head>

<body>
<ol id="list">
  <li>
    1
  </li>
  <li>
    2
  </li>
  <li>
    3
  </li>
  <li>
    4
  </li>
  <li>
    5
  </li>
  <li>
    6
  </li>
</ol>

<div id="prara">
  
    1
  

  
    2
  

  
    3
  

  
    4
  

  
    5
  

  
    6
  

</div>
<script type="text/javascript">

var sortList = function(window,doc,undefined){
    var lis=null,replace=null,origin=[0,0],parent=null,tag=null,tips=null,listWidth=0,
    toolkit={getEvent:function(b){return b||window.event},getTarget:function(b){return b.srcElement||b.target},stopEvent:function(b){b=toolkit.getEvent(b);(b.returnValue||b.preventDefault)&&(b.returnValue=false||b.preventDefault());(b.cancelBubble||b.stopPropagation)&&(b.cancelBubble=false||b.stopPropagation())},getClinetRect:function(b){var g=b.getBoundingClientRect(),c=(c={left:g.left,right:g.right,top:g.top,bottom:g.bottom,height:(g.height?g.height:(g.bottom-g.top)),width:(g.width?g.width:(g.right-g.left))});return c},getScrollPosition:function(){var b=[0,0];window.pageYOffset?(b=[window.pageXOffset,window.pageYOffset]):(b=[document.documentElement.scrollLeft,document.documentElement.scrollTop]);return b},addEvent:function(f,e,d,c){var b=arguments.callee;f.attachEvent&&(b=function(i,h,g){i.attachEvent("on"+h,g)}).apply(this,arguments);f.addEventListener&&(b=function(i,h,g){i.addEventListener(h,g,c||false)}).apply(this,arguments);f["on"+e]&&(b=function(i,h,g){i["on"+h]=function(){g()}}).apply(this,arguments)},removeEvent:function(f,e,d,c){var b=arguments.callee;f.detachEvent&&(b=function(i,h,g){i.detachEvent("on"+h,g)}).apply(this,arguments);f.removeEventListener&&(b=function(i,h,g){i.removeEventListener(h,g,c||false)}).apply(this,arguments);f["on"+e]&&(b=function(i,h,g){i["on"+h]=null}).apply(this,arguments)}};
    
    
    function mousedownSortableList(e){
        e=toolkit.getEvent(e);
        var target=toolkit.getTarget(e),pos=null;
        
        while(target.nodeName.toLowerCase()!==tag){
            target=target.parentNode;
        };
        doc.currentTarget=target,pos=toolkit.getClinetRect(target),origin=[e.clientX-pos.left,e.clientY-pos.top],
                listWidth=target.offsetWidth;
        toolkit.addEvent(doc,'mousemove', mousemoveCheckThreshold, false);
          toolkit.addEvent(doc,'mouseup', mouseupCancelThreshold, false);
        toolkit.stopEvent(e);
    };
    function creatReplaceElement(O,E,P){
        replace || (
            replace=O.cloneNode(true),
            replace.style.cssText='height:'+(O.style.height-4)+'px;',
            replace.className=tips,
            replace.innerHTML='放在这里?'
        );
        (P===-1) && E.parentNode.insertBefore(replace,E);
        (P===1) && E.parentNode.insertBefore(replace,E.nextSibling);
    };
    function mousemoveCheckThreshold(e){
        e=toolkit.getEvent(e);
        var target=doc.currentTarget,i=lis.length,pos=null,scroll=toolkit.getScrollPosition();
        
        try{target.style.cssText='top:'+(e.clientY-origin[1]+scroll[1])+'px;position:absolute;z-index:100;opacity:.9;filter:alpha(opacity="90");width:'+listWidth+'px;overflow:hidden;';}catch(e){};
        for(;i>0;){
            lis[--i]!==target && (
                pos=toolkit.getClinetRect(lis[i]),
                ((e.clientY>=pos.top) && (e.clientY < pos.bottom))&&(
                    creatReplaceElement(target,lis[i],e.clientY<=(pos.top+lis[i].offsetHeight/2)?-1:1)
                )
            )
        };
        
    };
    function mouseupCancelThreshold(e){
        try{doc.currentTarget.style.cssText='';replace.parentNode.replaceChild(doc.currentTarget,replace)}catch(e){};
        toolkit.removeEvent(doc,'mousemove',mousemoveCheckThreshold,false);
        toolkit.removeEvent(doc,'mouseup',mouseupCancelThreshold,false);
        doc.currentTarget=null,replace=null;
        toolkit.stopEvent(e);
    };
    return function(O){
        parent=O.parent,tag=O.tag || 'li',tips=O.tips || 'sortListTips';
        if(!doc.getElementById(parent)) return false;
        lis=doc.getElementById(parent).getElementsByTagName(tag);
        var i=lis.length
        for(;i>0;toolkit.addEvent(lis[--i],'mousedown',mousedownSortableList,false),lis[i].style.cursor='move'){};
    };
}(window,document);

//sortList({parent:"list",tag:"li",tips:'tips'});
sortList({parent:"prara",tag:"p"});
//拖拽排序列表单例 最好不要同时启用两个。
</script>
</body>
</html>

复制代码运行代码

注意这是一个单例实现的控件,所以一个页面上只能有一个实例,有两个当然也没大的问题。Javascript没有重载导致覆盖。脚本内部也做了相应的容错,搞观看不同的实例,只能注释掉一个(当然移动调用顺序也可以)。大家都习惯了javascript的多实例,用构造函数创建一个类,用new运算符生成多个实例。这样使用一个单例的情况并不多,比如一个页面上今天可能用了一个选项卡,明天有可能有用两个。单例的局限性还是比较大的。要使用单例模式一定要明确单个window实例上是不是只使用一次某个功能,这样才适合使用单例。

这个例子是为了单例而单例的,当然也很容易改造成类式,有兴趣可以自己试一下。但是单例也有自己的优势,如果从其他语言转过来的同学,非常熟悉而且易上手,对原型继承不是很熟悉的可以尝试一下,关于单例的使用场景欢迎大家入群讨论。
1
2
分享到:
评论

相关推荐

    JavaScript的单例模式 (singleton in Javascript)

    JavaScript的单例模式 (singleton in Javascript)

    JavaScript设计模式之单例模式详解

    这一次重温一下《JavaScript设计模式与开发实践》,开篇为单例模式。 /** * pre 单例模式 * 定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点 * 应用:单例模式是一种常用的模式,有一些对象我们...

    基于JavaScript实现单例模式

    单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个...

    JS 设计模式之:单例模式定义与实现方法浅析

    单例模式(Singleton)属于创建型的设计模式,它限制我们只能创建单一对象或者某个类的单一实例。 通常情况下,使用该模式是为了控制整个应用程序的状态。在日常的开发中,我们遇到的单例模式可能有:Vuex 中的 ...

    javascript 单例模式详解及简单实例

    JS 单例模式 概要: 单例指一个类只有一个实例,这个类自行创建这个实例。 利用对象字面量直接生成一个单例: var singleton = { prop: 1, method: function(){ console.log(a); //1 } } 严格的说对象字面量...

    imaxue#progress#单例模式1

    单例模式保证某一个类只有一个实例存在简单的单例模式// 这是一个简单的单例模式可以理解一下意义所在,但是是有缺点的var Singleton = functio

    JS基于设计模式中的单例模式(Singleton)实现封装对数据增删改查功能

    主要介绍了JS基于设计模式中的单例模式(Singleton)实现封装对数据增删改查功能.结合实例形式分析了javascript基于单例模式结合ajax针对数据库进行增删改查的相关操作技巧,需要的朋友可以参考下

    javascript 单例/单体模式(Singleton)

    单例模式的三个特点: 1,该类只有一个实例 2,该类自行创建该实例(在该类内部创建自身的实例对象) 3,向整个系统公开这个实例接口 Java中大概是这个样子 代码如下: class Singleton { //私有,静态的类自身实例 ...

    单例的使用--弹出框

    使用原生JavaScript写的单例小模式,适用于大多数的form表单中。

    ConardLi#awesome-coding-js#单例模式1

    创建对象和管理单例的职责被分布在两个不同的方法中,这两个方法组合起来才具有单例模式的威力。使用闭包实现:var Singleton = function(nam

    Example-TypeScript-Singleton-Pattern:TypeScript 和单例模式

    示例 TypeScript 单例模式查看教程在 关于如何使用这些文件。 请务必查看我所有的。

    JavaScript编程的单例设计模讲解

    在Javascript中,单例模式是一种最基本又经常用到的设计模式,可能在不经意间就用到了单例模式。 本文将从最基础的理论开始,讲述单例模式的基本概念和实现,最后用一个例子来讲述单例模式的应用。 理论基础 概念 ...

    react-singleton-hook:从常规React钩子创建单例钩子

    React单例钩子 使用钩子管理 React 应用程序的全局状态。 安装 要在 React 应用程序中使用 React Singleton Hook,请将其安装为依赖项: # If you use npm: npm install react-singleton-hook # Or if you use ...

    JSDesignPattern:JavaScript设计模式测试代码

    单例模式 singleton 代理模式 proxy 迭代器模式 iterator 观察者模式 pubsub 命令模式 command 组合模式 composite 模板方法模式 template 享元模式 flyweight 装饰者模式 decorator 中介者模式 mediator

    SSP-simple-singleton-pattern:简单的单例模式

    简单的单例模式团队丹尼斯·卡拉赞斯莱昂纳多·派瓦鲁道夫·迪亚斯图利奥·菲利普 它是什么? Development &gt; Organization &gt; Good practices &gt; Standards... SSP 是一种模块化和组织项目的简单方法。 将 OOP 的想法...

    Javascript模块模式分析

    Douglas Crockford已经传授了一个有用的单例模式(singleton pattern)实现此规则,我认为他的模式有益于你基于YUI的那些应用。Douglas叫它模块模式(module pattern)。它是如下工作的: 创建一个命名空间对象:...

    Javascript的一种模块模式

    Douglas Crockford已经传授了一个有用的单例模式(singleton pattern)实现此规则,我认为他的模式有益于你基于YUI的那些应用。Douglas叫它模块模式(module pattern)。它是如下工作的: 1、创建一个命名空间对象:...

    MetaSetter:Singleton Object可动态更新客户端应用程序中的元标记

    单例模式每当您require('meta-setter') ,它将在所有位置返回相同的javascript对象。 这称为。为什么这有用? MetaSetter对象是一个简单的单例对象,具有3种方法:init,updateMetaTags和Reset。 MetaSetter在客户端...

Global site tag (gtag.js) - Google Analytics