常用的Javascript设计模式
2018-09-09 本文已影响4人
puxiaotaoc
一、构造函数模式
// 构造一个动物的函数
function Animal(name,color){
this.name = name;
this.color = color;
this.getName = function(){
return this.name;
}
}
// 实例一个对象
var cat = new Animal('大毛','white');
console.log(cat.getName()); // 大毛
二、工厂模式
function Animal(name,color){
var o = new Object();
o.name = name;
o.color = color;
o.getName = function(){
return this.name;
};
return o;
}
var cat = new Animal('大毛','white');
console.log(cat.getName()); // 大毛
三、模块模式
var Car = (function(){
var name = '大毛';
function getName(){
console.log(name);
}
function getColor(color){
console.log(color);
}
return {
name: getName,
color: getColor
}
})();
Car.name(); // 大毛
Car.color('red'); // red
四、混合模式
// 混合模式 = 原型模式 + 构造函数模式
function create(parentObj) {
function F() {}
F.prototype = parentObj;
return new F();
}
function Animal(name, color) {
this.name = name;
this.color = color;
}
Animal.prototype.getName = function() {
return this.name;
}
function Cat(name, color) {
Animal.call(null, name, color);
this.color = color;
}
Cat.prototype = create(Animal.prototype);
Cat.prototype.getColor = function() {
return this.color;
}
var cat = new Cat('大毛', 'white');
console.log(cat.getColor()) // white
五、单例模式
var Single = (function() {
var instance;
function init() {
// 生成单例的构造函数的代码
return {};
}
return {
// 如果该实例存在,则直接返回,否则就对其实例化
getInstance: function() {
if (!instance) {
instance = init();
}
return instance;
}
}
})();
var obj1 = Single.getInstance();
var obj2 = Single.getInstance();
console.log(obj1 === obj2); // true
// 保证一个类仅有一个实例,并提供一个访问它的全局访问点,例如:线程池,全局缓存,登录浮窗
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../my_ajax.js"></script>
</head>
<body>
<form action="">
<input type="text" name="user">
<input type="password" name="pwd">
<input type="submit" value="登录">
</form>
<script type="text/javascript">
var submitObj = {
form: document.forms[0],
submitUrl: "data2.php",
_init: function() {
this.handSubmit();
},
handSubmit: function() {
var that = this;
this.form.onsubmit = function(e) {
e.preventDefault(); //阻止表单提交默认行为
if (!that.checkForm()) return;
that.ajaxSubmit();
}
},
checkForm: function() {
return true; //可使用正则表达式验证
},
ajaxSubmit: function() {
my_ajax.post(this.submitUrl, new FormData(this.form), this.submitResult)
},
submitResult: function(result) {
console.log(result);
}
}
submitObj._init();
</script>
</body>
</html>
// 需要把单例的逻辑代码单独提取,然后使用惰性单例的方式,也就是返回方法;
// 只有在点击的时候,才会进行执行,javascript的单例;
// 跟类不一样,无需创建多余的构造函数这些,直接创建全局变量即可;
!(function() {
//管理单例的逻辑代码,如果没有数据则创建,有数据则返回
var getSingle = function(fn) { //参数为创建对象的方法
var result;
return function() { //判断是Null或赋值
return result || (result = fn.apply(this, arguments));
};
};
//创建登录窗口方法
var createLoginLayer = function() {
var div = document.createElement('div');
div.innerHTML = '我是登录浮窗';
div.style.display = 'none';
document.body.appendChild(div);
return div;
};
//单例方法
var createSingleLoginLayer = getSingle(createLoginLayer);
//使用惰性单例,进行创建
document.getElementById('loginBtn').onclick = function() {
var loginLayer = createSingleLoginLayer();
loginLayer.style.display = 'block';
};
})()
六、发布订阅者模式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button id="btn1">按钮一</button>
<button id="btn2">按钮二</button>
<script type="text/javascript">
var ObserverEvent = (function() {
var clientList = [],listen, trigger, remove;
listen = function(key, fn) {
if (!clientList[key]) {
clientList[key] = [];
}
clientList[key].push(fn);
};
trigger = function() {
var key = Array.prototype.shift.call(arguments),
fns = clientList[key];
if (!fns || fns.length === 0) {
return false;
}
for (var i = 0, fn; fn = fns[i++];) {
fn.apply(this, arguments);
}
};
remove = function(key, fn) {
var fns = clientList[key];
if (!fns) {
return false;
}
if (!fn) {
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);
}
}
}
};
return {
listen: listen,
trigger: trigger,
remove: remove
}
})();
ObserverEvent.listen('squareMeter88', fn1 = function(price) {
console.log('价格=' + price);
});
ObserverEvent.listen('squareMeter100', function(price) {
console.log('价格=' + price);
});
ObserverEvent.trigger('squareMeter88', 200000);
ObserverEvent.trigger('squareMeter100', 300000);
ObserverEvent.remove('squareMeter88', fn1);
ObserverEvent.trigger('squareMeter88', 200000);
</script>
</body>
</html>
var EventCenter = (function() {
var events = {};
// 绑定事件 添加回调
function on(evt, handler) {
events[evt] = events[evt] || [];
events[evt].push({
handler: handler
})
}
function fire(evt, arg) {
if (!events[evt]) {
return
}
for (var i = 0; i < events[evt].length; i++) {
events[evt][i].handler(arg);
}
}
function off(evt) {
delete events[evt];
}
return {
on: on,
fire: fire,
off: off
}
}());
var number = 1;
EventCenter.on('click', function(data) {
console.log('click 事件' + data + number++ + '次');
});
EventCenter.off('click'); // 只绑定一次
EventCenter.on('click', function(data) {
console.log('click 事件' + data + number++ + '次');
});
EventCenter.fire('click', '绑定');
七、适配器模式
适配器模式(Adapter)是将一个类(对象)的接口(方法或属性)转化成客户希望的另外一个接口(方法或属性),适配器模式使得原本由于接口不兼容而不能一起工作的那些类(对象)可以一些工作,俗称包装器(wrapper);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../my_ajax.js"></script>
</head>
<body>
<p id="p1"></p>
<p id="p2"></p>
<script type="text/javascript">
//适配器模式:在不修改旧的模式的前提下,来适应新的变化
my_ajax.get("data3.json",function (result) {
showMsg(JSON.parse(result),p1);
})
my_ajax.get("data4.json",function (result) {
showMsgAdapter(JSON.parse(result),p2);
})
function showMsg(obj,p) {
p.innerHTML = obj.name;
}
function showMsgAdapter(arr,p) {
showMsg(arr[0],p2);
}
</script>
</body>
</html>
八、代理模式
当客户不方便直接访问一个对象的时候,需要提供一个替身对象来控制对这个对象的访问,即把对一个对象的访问, 交给另一个代理对象来操作,代理模式分为:虚拟代理和保护代理:
- 虚拟代理:把一些开销很大的对象,延迟到真正需要它的时候才去创建;
- 保护代理:用于控制不同权限的对象对目标对象的访问;
图片预加载
使用虚拟代理可以完成图片预加载功能,先用一张loading图片占位,然后用异步方式加载图片,等图片加载完毕后填充到img节点里;
// javascript事件均为异步事件,当执行proxyImage时,会先设置loading.gif;
// 等图片加载完毕后,会执行myImage操作;
var myImage = (function() {
var imgNode = document.createElement('img');
document.body.appendChild(imgNode);
return {
setSrc: function(src) {
imgNode.src = src;
}
};
})();
//预加载方法
var proxyImage = (function() {
var img = new Image();
img.onload = function() {
myImage.setSrc(this.src);
}
return {
setSrc: function(src) {
myImage.setSrc("loading.gif");
img.src = src;
}
};
})();
proxyImage.setSrc('实际图片.jpg'); //预加载
myImage.setSrc('实际图片'.jpg); //普通加载
// 加载方法和预加载方法,必须使用立即执行函数,否则setSrc方法调用不到;
// 如果不使用代理模式,会执行加载图片和预加载操作,当我们不需要预加载功能时,无法进行快速隔离
// 这种懒加载方法不用代理模式也可以实现,代理模式可以让 myImage 只做一件事,即只负责将实际图片加入到页面中;
// 而loading图片交给proxyImage去做,从而降低代码的耦合度;
// 当不想用loading的时候,可以直接调用 myImage 方法,即不需要代理对象的话,直接可以换成本体对象调用该方法即可;
参考链接:
常见的6种JavaScript设计模式
Javascript设计模式
常用的Javascript设计模式
js中的几种设计模式