`
enix2212
  • 浏览: 25368 次
  • 性别: 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 单例模式演示代码 javascript面向对象编程

    以下是一个简化的JavaScript单例模式的示例: ```javascript var Singleton = (function () { var instance; function createInstance() { var object = new Object(); object.property = "Hello, I am a ...

    通过javascript实现单例模式.rar

    在JavaScript中,单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取该实例。在JavaScript中实现单例模式通常涉及使用一个对象字面量或创建一个包含实例的...

    JavaScript的单例模式 (singleton in Javascript)

    ### JavaScript的单例模式详解 #### 一、引言 单例模式是设计模式中较为常见的一种模式,它确保一个类只有一个实例,并提供一个全局访问点。在JavaScript中实现单例模式同样非常重要,尤其是在需要控制资源消耗...

    javascript单例模式

    JavaScript 单例模式是一种设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。在JavaScript中,由于其动态特性和全局作用域,实现单例模式相对简单,但同样需要注意防止不必要的资源浪费和...

    javascript单例模式的简单实现方法

    在了解javascript单例模式的简单实现方法之前,我们首先要弄清楚什么是单例模式。单例模式(Singleton Pattern)是一种设计模式,它规定了一个类只能创建一个实例,并提供一个全局访问点供外部获取该实例。这样的...

    JavaScript设计模式中的单例模式和观察者模式.docx

    ### JavaScript设计模式详解:单例模式与观察者模式 #### 一、单例模式 **定义**:单例模式是一种常用的软件设计模式,其目的是确保某个类只有一个实例,并且能够提供一个全局访问点来访问该实例。在JavaScript中...

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

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

    深入理解JavaScript系列(25):设计模式之单例模式详解

    在JavaScript这样的动态语言中,单例模式的实现方式很多样,而且可以非常灵活。下面,我们就来深入探讨单例模式在JavaScript中的实现以及相关知识点。 首先,单例模式的基本概念是通过一个构造函数来封装创建一个...

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

    单例模式是软件工程中一种广为认知的设计模式,在JavaScript中,单例模式确保一个类仅有一个实例,并提供一个全局访问点。在JavaScript中实现单例模式,可以通过多种方式来完成,包括但不限于:利用对象字面量,封装...

    javascript 单例/单体模式(Singleton)

    JavaScript 中的单例模式实现多样,下面分别介绍几种常见的实现方式。 ##### 对象直接量实现 这是最简单的一种实现方式,直接定义一个对象包含所需的属性和方法: ```javascript var Singleton = { attr1: 1, ...

    基于JavaScript实现单例模式

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

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

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

    原生javascript单例模式的应用实例分析

    原生JavaScript中的单例模式是一种设计模式,它确保一个类只有一个实例,并提供全局访问点。这种模式在处理一些特定情况时非常有用,例如当需要控制一个类的实例化过程,或者当一个对象需要频繁创建和销毁时,可以...

    单例模式

    以下是一个简单的JavaScript单例模式示例: ```javascript var Singleton = (function() { var instance; function createInstance() { var object = new Object(); // 初始化对象... return object; } ...

    imaxue#progress#单例模式1

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

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

    在JavaScript编程中,设计模式是一种常见...总的来说,单例模式在JavaScript中的应用,特别是在处理数据交互时,能够有效地提高代码的复用性和模块化程度,使得前端开发者可以更高效地管理异步操作,从而提升用户体验。

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

    因此,这个单例模式同样适用于 JavaScript 开发环境。 7. **实际应用**: 单例模式常用于控制数据库连接、缓存管理、日志记录等场景,这些都需要在整个应用生命周期内只初始化一次。 8. **扩展阅读**: - **懒汉...

Global site tag (gtag.js) - Google Analytics