javaScript设计模式

1.实现单例模式:

保证一个类仅有一个实例,并提供一个访问它的全局访问点

var Singleton = function( name ){
this.name = name; this.instance = null; }; Singleton.prototype.getName = function(){
alert ( this.name ); }; Singleton.getInstance = function( name ){
if ( !this.instance ){   this.instance = new Singleton( name );
}  
return this.instance;
};
var a = Singleton.getInstance( 'sven1' ); var b = Singleton.getInstance( 'sven2' ); alert ( a === b ); // true

 策略模式是一种常用且有效的设计模式,本章提供了计算奖金、缓动动画、表单校验这三个

例子来加深大家对策略模式的理解。从这三个例子中,我们可以总结出策略模式的一些优点。
  •   策略模式利用组合、委托和多态等技术和思想,可以有效地避免多重条件选择语句。

  •   策略模式提供了对开放—封闭原则的完美支持,将算法封装在独立的 strategy 中,使得它

       们易于切换,易于理解,易于扩展。
    
  •   策略模式中的算法也可以复用在系统的其他地方,从而避免许多重复的复制粘贴工作。

  •   在策略模式中利用组合和委托来让 Context 拥有执行算法的能力,这也是继承的一种更轻

    便的替代方案。

var tween = {
    linear: function( t, b, c, d ){
        return c*t/d + b; },
    easeIn: function( t, b, c, d ){ 
        return c * ( t /= d ) * t + b;
    },
    strongEaseIn: function(t, b, c, d){
        return c * ( t /= d ) * t * t * t
    },
    strongEaseOut: function(t, b, c, d){
        return c * ( ( t = t / d - 1) * t
    },
    sineaseIn: function( t, b, c, d ){
        return c * ( t /= d) * t * t + b; },
    sineaseOut: function(t,b,c,d){
        return c * ( ( t = t / d - 1) * t
    }
}


var Animate = function(dom){
    this.dom=dom;    //    进行运动的 dom 节点
    this.startTime = 0; // 动画开始时间
    this.startPos = 0; // 动画开始时,dom 节点的位置,即 dom 的初始位置
    this.endPos = 0; // 动画结束时,dom 节点的位置,即 dom 的目标位置
    this.propertyName = null// dom 节点需要被改变的 css 属性名
    this.easing = null; // 缓动算法
    this.duration = null;// 动画持续时间
}
<body>
    <div style="position:absolute;background:blue" id="div">我是 div</div>
</body>

Animate.prototype.start = function( propertyName, endPos, duration, easing ){                     this.startTime = +new Date; // 动画启动时间
    this.startPos = this.dom.getBoundingClientRect()[ propertyName ]; // dom 节点初始位置 this.propertyName = propertyName; // dom 节点需要被改变的 CSS 属性名
    this.endPos = endPos; // dom 节点目标位置 this.duration = duration; // 动画持续事件 this.easing = tween[ easing ]; // 缓动算法
    var self = this;
    var timeId = setInterval(function(){ // 启动定时器,开始执行动画
        if ( self.step() === false ){ // 如果动画已结束,则清除定时器 
            clearInterval( timeId );
        }
    }, 19 );
};
Animate.prototype.step = function(){
    var t = +new Date; // 取得当前时间
    if ( t >= this.startTime + this.duration ){ // (1)
        this.update( this.endPos ); // 更新小球的 CSS 属性值
        return false; 
    }
    var pos = this.easing( t - this.startTime, this.startPos, this.endPos - this.startPos, this.duration );
    // pos 为小球当前位置
     this.update( pos ); // 更新小球的 CSS 属性值 7 
};

Animate.prototype.update = function( pos ){ 
    this.dom.style[ this.propertyName ] = pos + 'px';
};
//如果不嫌麻烦,我们可以进行一些小小的测试:
var div = document.getElementById( 'div' ); 
var animate = new Animate( div );
animate.start( 'left', 500, 1000, 'strongEaseOut' ); 
// animate.start( 'top', 1500, 500, 'strongEaseIn' );
```
Animate.prototype.start 方法接受以下 4 个参数。
 propertyName:要改变的 CSS 属性名,比如'left'、'top',分别表示左右移动和上下移动。  endPos: 小球运动的目标位置。
 duration: 动画持续时间。
 easing: 缓动算法。

策略方式2

<form action="http:// xxx.com/register" id="registerForm" method="post"> 请输入用户名:<input type="text"
            name="userName" />
        请输入密码:<input type="text" name="password" /> 请输入手机号码:<input type="text" name="phoneNumber" />
        <button>提交</button>
    </form>

 

 /***********************策略对象**************************/
        var strategies = {
            isNonEmpty: function (value, errorMsg) {
                if (value === '') {
                    return errorMsg;
                }
            },
            minLength: function (value, length, errorMsg) {
                if (value.length < length) {
                    return errorMsg;
                }
            },
            isMobile: function (value, errorMsg) {
                if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
                    return errorMsg;
                }
            }
        };
        /***********************Validator 类**************************/
        var Validator = function () {
            this.cache = [];
        };
        Validator.prototype.add = function (dom, rules) {
            var self = this;
            for (var i = 0, rule; rule = rules[i++];) {
                (function (rule) {
                    var strategyAry = rule.strategy.split(':');
                    var errorMsg = rule.errorMsg;
                    self.cache.push(function () {
                        var strategy = strategyAry.shift();
                        strategyAry.unshift(dom.value);
                        strategyAry.push(errorMsg);
                        return strategies[strategy].apply(dom, strategyAry);
                    });
                })(rule)
            }
        };
        Validator.prototype.start = function () {
            for (var i = 0, validatorFunc; validatorFunc = this.cache[i++];) {
                var errorMsg = validatorFunc();
                if (errorMsg) {
                    return errorMsg;
                }
            }
        };
        /***********************客户调用代码**************************/
        var registerForm = document.getElementById('registerForm');
        var validataFunc = function () {
            var validator = new Validator();
            validator.add(registerForm.userName, [{
                strategy: 'isNonEmpty',
                errorMsg: '用户名不能为空'
            }, {
                strategy: 'minLength:6',
                errorMsg: '用户名长度不能小于 10 位'
            }]);
            6 7 8
            validator.add(registerForm.password, [{
                strategy: 'minLength:6',
                errorMsg: '密码长度不能小于 6 位'
                9
            }]);
            validator.add(registerForm.phoneNumber, [{
                strategy: 'isMobile',
                errorMsg: '手机号码格式不正确'
            }]);
            var errorMsg = validator.start();
            return errorMsg;
        }
        registerForm.onsubmit = function () {
            var errorMsg = validataFunc();
            if (errorMsg) {
                alert(errorMsg);
                return false;
            }
        };

 

迭代器模式

迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象 5 的内部表示。迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺序访问其中的每个元素

//jQuery 中的迭代器
$.each( [1, 2, 3], function( i, n ){ console.log( '当前下标为: '+ i );         
   console.log( '当前值为:' + n );
});
//实现自己的迭代器

var each = function( ary, callback ){
    for ( var i = 0, l = ary.length; i < l; i++ ){
         callback.call( ary[i], i, ary[ i ] );
    } 
};
each( [ 1, 2, 3 ], function( i, n ){ 
    alert ( [ i, n ] );
});

 var Iterator = function (obj) {
            var current = 0;
            var next = function () {
                current += 1;
            };
            var isDone = function () {};
            var getCurrItem = function () {
                return obj[current];
            };
            return {
                next: next,
                isDone: isDone,
                getCurrItem: getCurrItem
            }
        };
        var compare = function (iterator1, iterator2) {
            while (!iterator1.isDone() && !iterator2.isDone()) {
                if (iterator1.getCurrItem() !== iterator2.getCurrItem()) {
                    throw new Error('iterator1 和 iterator2 不相等');
                }
                iterator1.next();
                iterator2.next();
            }
            alert('iterator1 和 iterator2 相等');
        }
        var iterator1 = Iterator([1, 2, 3]);
        var iterator2 = Iterator([1, 2, 3]);
        compare(iterator1, iterator2); // 输出:iterator1 和 iterator2 相等

 

发布—订阅模式

发布—订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状 态发生改变时,所有依赖于它的对象都将得到通知。在 JavaScript 开发中,我们一般用事件模型 来替代传统的发布—订阅模式。

DOM 事件

实际上,只要我们曾经在 DOM 节点上面绑定过事件函数,那我们就曾经使用过发布—订阅模式,来看看下面这两句简单的代码发生了什么事情

    var event = {
        clientList: [],
        listen: function (key, fn) {
            if (!this.clientList[key]) {
                this.clientList[key] = [];
            }
            this.clientList[key].push(fn) // 订阅的消息添加进缓存列表 
            var key = Array.prototype.shift.call(arguments), // (1);
                fns = this.clientList[key];
            if (!fns || fns.length === 0) { // 如果没有绑定对应的消息 
                return false;
            }
            for (var i = 0, fn; fn = fns[i++];) {
                fn.apply(this, arguments); // (2) // arguments 是 trigger 时带上的参数
            }
        }
    };
    event.remove = function (key, fn) {
        var fns = this.clientList[key];
        if (!fns) { // 如果 key 对应的消息没有被人订阅,则直接返回 
            return false;
        }
        if (!fn) { // 如果没有传入具体的回调函数,表示需要取消 key 对应消息的所有订阅
            fns && (fns.length = 0);
        } else {
            for (var l = fns.length - 1; l >= 0; l--) { // 反向遍历订阅的回调函数列表 
                var _fn = fns[l];
                if (_fn === fn) {
                    fns.splice(l, 1);
                    var salesOffices = {};
                    var installEvent = function (obj) {
                        for (var i in event) {
                            obj[i] = event[i];
                        }
                    }
                    installEvent(salesOffices);
                    // 删除订阅者的回调函数
                }
            }
        }
    };
    var salesOffices = {};
    //再定义一个 installEvent 函数,这个函数可以给所有的对象都动态安装发布—订阅功能:
    var installEvent = function (obj) {
        for (var i in event) {
            obj[i] = event[i];
        }
    };
    installEvent(salesOffices);
    //再来测试一番,我们给售楼处对象 salesOffices 动态增加发布—订阅功能:
    salesOffices.listen('squareMeter88', fn1 = function (price) {
        console.log('价格= ' + price);
    });
    salesOffices.listen('squareMeter88', fn2 = function (price) {
        console.log('价格= ' + price);
    });
    salesOffices.remove('squareMeter88', fn1); // 删除小明的订阅
    // 小明订阅消息
    // 小红订阅消息
    salesOffices.trigger('squareMeter88', 2000000); // 输出:2000000

 

posted @ 2021-02-25 16:12  6NULL9  阅读(78)  评论(0)    收藏  举报