本文深入解析JavaScript中的单例模式与策略模式。通过创建DOM节点和年终奖计算等具体案例,演示如何保证类仅有一个实例并提供全局访问点。重点讲解惰性单例的实现,结合代理模式分离创建与管理职责,避免资源浪费。同时介绍策略模式封装算法,实现表单验证与业务逻辑的灵活替换,提升代码可维护性。
单例模式基础与实现
一、单例模式
1、概念
确保一个类只有一个实例,并提供全局访问点
2、单例模式的实现 -- 以创建div节点为例
var createDiv = (function(){
var instance;
var createDiv = function(html){
if(instance){
return instance
}
this.html = html;
this.init();
return instance = this;
};
createDiv.prototype.init = function(){
var div = document.createElement('div');
div.innerHTML = this.html;
document.body.appendChild(div)
}
return createDiv;
})()
var a =new createDiv('one');
var b =new createDiv('two');
console.log(a===b); //true上述例子存在缺陷:
(1)采用匿名自执行函数,增加了函数复杂度
(2)createDiv函数承担两项任务:创建对象与管理单例,违背单一职责原则
改进方案:使用代理实现
//代理模式--改进
//只负责创建div的一个类
var createDiv = function (html) {
this.html = html;
this.init();
};
createDiv.prototype.init = function () {
var div = document.createElement('div');
div.innerHTML = this.html;
document.body.appendChild(div)
}
//引入代理类
var proxySingle = (function(){
var instance;
return function(html){
if(!instance){
instance = new createDiv(html)
}
return instance
}
})();
var a = new proxySingle('one');
var b = new proxySingle('two');
console.log(a === b); //true3、惰性单例(使用)
仅在需要时创建对象实例
结合登录功能需求,登录弹窗仅出现一次,在点击登录按钮时触发
两种实现方式
(1)页面加载完成时预先创建浮窗,初始状态隐藏,用户点击登录时显示。缺点:浪费DOM节点。
(2)用户点击登录按钮时再创建浮窗:代码如下:
var createLoginer = function(){
var login = document.createElement("div");
login.innerHTML='登录浮窗';
login.style.display = 'none';
document.body.appendChild(login);
return login
}
//点击登陆按钮
btn.onclick = function(){
var loginer = createLoginer();
loginer.style.display = 'block';
}虽达到惰性目的,但失去单例效果——再次修改如下:
使用变量判断是否已创建登录浮窗
//创建登陆浮窗
var createLoginer = function () {
var login = document.createElement("div");
login.innerHTML = '登录浮窗';
login.style.display = 'none';
document.body.appendChild(login);
return login
}
var loginer;
var getSingle = function (fn) {
return function () {
console.log(this)
return loginer || (loginer = fn.apply(this))
}
}
//createSingleLoginer接下来作为函数调用,所以getSingle得返回一个函数,并且这个函数得返回登录浮窗
var createSingleLoginer = getSingle(createLoginer);
//点击登陆按钮
var btn = document.getElementById("btn");
btn.onclick = function () {
var loginer = createSingleLoginer();
loginer.style.display = 'block';
}getSingle用于管理单例
惰性单例:仅在合适时机创建对象,且仅创建唯一实例。将创建对象与管理单例的职责分散至不同方法,组合后发挥真正单例模式威力。
策略模式与算法封装
二、策略模式
1、概念:
定义一系列算法,分别封装,并可相互替换
2、实例
年终奖发放,按等级与基础工资乘积计算
var strage = {
S:function(salary){
return salary*4
},
A:function(salary){
return salary*3
},
B:function(salary){
return salary*2
}
}
var calculateBounus = function(level,salary){
return strage[level](salary)
};
console.log(calculateBounus('S',2000)) //8000
console.log(calculateBounus('A',2000)) //60003、策略模式用于封装算法,更广义应用:表单验证登录算法
将校验方式封装为策略对象
var validatorFunc = function () {
var validator = new Validator();
validator.add(loginForm.userName, "isEmpty", '用户名不能为空')
validator.add(loginForm.passWord, "minLength:6", '密码长度不能少于6位')
validator.add(loginForm.phone, "phoneValidator", '手机号码格式错误')
var errMsg = validator.start();
return errMsg
}
var loginForm = document.getElementById("login")
function login() {
var errMsg = validatorFunc();
if (errMsg) {
alert(errMsg);
return false; //阻止表单提交
}
}
var Validator = function () {
//缓存校验规则
this.cache = [];
}
Validator.prototype.add = function (dom, rule, err) {
var ary = rule.split(':');
this.cache.push(function () {
var strategy = ary.shift();
ary.unshift(dom.value);
ary.push(err)
return loginStrage[strategy].apply(dom, ary)
})
}
Validator.prototype.start = function () {
for (var i = 0; i < this.cache.length; i++) {
var err = this.cache[i]()
if (err) {
alert(err)
}
}
}运行页面: