JS设计模式
节流模式
节流模式的核心思想是创建定时器,延迟程序的执行。
委托模式
给事件发生的上一层最近的父元素绑定事件 ,用e.target去寻找事件触发的具体对象 进行实现事件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>事件委托</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
list-style: none;
}
li {
width: 100px;
height: 30px;
line-height: 30px;
background-color: lightblue;
margin-bottom: 5px;
}
span {
float: right;
width: 20px;
height: 20px;
font-size: 20px;
cursor: pointer;
}
#btn {
width: 100px;
height: 30px;
}
</style>
</head>
<body>
<ul id="list">
<li><span>×</span></li>
<li><span>×</span></li>
<li><span>×</span></li>
</ul>
<button id="btn">添加li</button>
<script type="text/javascript">
// 要求: 点击btn 添加li 点击li 背景颜色改变, 点击span 删除li
// 获取元素
var ul = document.getElementById("list");
var btn = document.getElementById("btn");
// 添加点击事件
btn.onclick = function() {
ul.innerHTML += "<li><span>×</span></li>"
}
// 委托模式可以解决: 1 预言未来元素 2 防止内存泄漏 3 减少事件数量
ul.onclick = function(e) {
// console.log(e.target)
// 进行判定点击的是哪个元素
if (e.target.nodeName.toLowerCase() === "span") {
// 此时e.target是span
this.removeChild(e.target.parentNode);
} else if (e.target.tagName.toLowerCase() === "li") {
// 此时e.target是li
e.target.style.backgroundColor = "pink"
}
}
</script>
</body>
<html>
工厂模式
工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式。
工厂函数就是做一个对象创建的封装,并将创建的对象return出去
优点: 能解决多个相似的问题。
缺点: 不能知道对象识别的问题(对象的类型不知道)。
复杂的工厂模式定义是:将其成员对象的实列化推迟到子类中,子类可以重写父类接口方法以便创建的时候指定自己的对象类型。
父类只对创建过程中的一般性问题进行处理,这些处理会被子类继承,子类之间是相互独立的,具体的业务逻辑会放在子类中进行编写。
function Animal(opts){
var obj = new Object();
obj.color = opts.color;
obj.name= opts.name;
obj.getInfo = function(){
return '名称:'+ onj.name+', 颜色:'+ obj.color;
}
return obj;
}
var cat = Animal({name: '波斯猫', color: '白色'});
cat.getInfo();
构造函数模式
function Animal(name, color){
this.name = name;
this.color = color;
this.getName = function(){
return this.name;
}
}
// 实例一个对象
var cat = new Animal('猫', '白色');
console.log( cat.getName() );
迭代器模式
迭代器模式:在不暴露内部结构的情况下顺序的遍历内部数据
判断target是 数组还是对象
方式1:target instanceOf Array(用instanceof)
方式2:target.target.constructor
方式3:Object.prototype.toString.call(target)
<script>
function each(target, fn){
// 判断 target是数组还是对象
// 方式1 target instanceof Array 用instanceof
if(target instanceof Array){
for(var i = 0; i < target.length; i++){
fn(target[i], i);
}
}else if(target instanceof Object){
for(var i in target){
fn(target[i], i);
}
}
// 方式2 target.constructor
if(target.constructor == Array){
for(var i = 0; i < target.length; i++){
fn(target[i], i);
}
}else if(target.constructor == Object){
for(var i in target){
fn(target[i], i)
}
}
// 方式3 Object.prototype.toString.call(target) (推荐第三种)
if (Object.prototype.toString.call(target)=== '[object Array]') {
// 说明是数组
for (var i = 0; i < target.length; i++) {
fn(target[i], i);
}
} else if (Object.prototype.toString.call(target)=== '[object Object]') {
for (var i in target) {
fn(target[i], i)
}
}
}
var arr = ["a", "b", "c", 'd', "e", "f", "g"];
each(arr, function(value, index) {
console.log(value, index)
});
var obj = {
name: "xwen",
age: 3,
sex: "女"
};
each(obj, function(value, index) {
console.log(value, index)
});
</script>
策略模式
将定义的一组算法封装起来,使其相互之间可以替换。
封装的算法具有一定独立性,不会随客户端变化而变化。
策略模式主要是一 种用于处理多种分支判断的模式.
// 以下的都是策略(strategy)
var strategies = {
"S": function( salary ){
return salary * 4;
},
"A": function( salary ){
return salary * 3;
},
"B": function( salary ){
return salary * 2;
}
};
// 以下就是环境(context) 没有改变,和上面的一样
var Bonus = function(){
this.salary = null; // 原始工资
this.strategy = null; // 绩效等级对应的策略对象
};
Bonus.prototype.setSalary = function( salary ) {
this.salary = salary; // 设置员工的原始工资
}
Bonus.prototype.setStrategy = function( strategy ) {
this.strategy = strategy;
}
Bonus.prototype.getBonus = function() {
return this.strategy.calculate( this.salary );
// 把计算奖金的操作委托给对应的策略对象
}
// 以下是实例
var calculateBonus = function( level, salary ){
return strategies[ level ]( salary );
};
console.log(calculateBonus('S', 20000)); // 输出:80000
console.log(calculateBonus('A', 10000)); // 输出:30000
单例模式
利用闭包和立即执行函数来实现
单例模式: 保证一个类只有一个实例,一般先判断实例是否存在,如果存在直接返回,不存在则先创建再返回,这样就可以保证一个类只有一个实例对象。
作用:
(1)、保证某个类的对象的唯一性;
(2)、模块间通信;
(3)、防止变量污染
单例模式的核心:确保 一个特定类只有一个实例,并提供这个实例的全局访问
应用场景:
对象往往仅需要一个的时候
(1)windows的任务管理器
(2)多线程的线程池设计
(3)全局缓存
(4)浏览器的window对象
(5)登录页面中的浮窗,无论点击多少次,浮窗仅仅会被创建一次,那么这个button应该使用 单例模式来进行创建。
// 实现单体模式创建div
var createDiv= (function(){
var div;
return function(){
if(!div) {
div = document.createElement("div");
div.style.width = '100px';
div.style.height = '100px';
div.style.background = '#e4e4e4';
document.body.appendChild(div);
}
return div;
}
})();
var div1=createDiv();
var div2=createDiv();
console.log(div1===div2); //true
DOM2级观察者模式
观察者模式(又被称为发布-订阅(Publish/Subscribe)模式,属于行为型模式的一种,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。
使用场景
1.事件多级触发场景。
2.跨系统的消息交换场景,如消息队列、事件总线的处理机制。
3.观察者模式主要用于在关联行为之间建立一套触发机制的场景。
比如:微信朋友圈动态通知、GPser 生态圈消息通知、邮件通知、广播通知、桌面程序的事件响应等
优点:解除耦合,让耦合的双方都依赖于抽象,从而使得各自的变换都不会影响另一边的变换。
缺点:在应用观察者模式时需要考虑一下开发效率和运行效率的问题,程序中包括一个被观察者、多个观察者,开发、调试等内容会比较复杂,而且在Java中消息的通知一般是顺序执行,那么一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般会采用异步实现。
<script>
var Observer = (function () {
var _O = {
}
return {
on: function (name, fn) {
// 先判定对象的该属性是否已经是数组
if (_O[name]) {
// 如果已经是数组 直接把函数往里添加
_O[name].push(fn)
} else {
// 说明还没有值 设置为数组
_O[name] = [fn];
}
},
trigger: function (name, ...args) {
for (var i = 0; i < _O[name].length; i++) {
_O[name][i](...args)
}
}
}
})();
// 第一个人的代码
; (function () {
// 获取元素
var num = document.getElementById("num");
// 使用观察者模式 向消息管道中注册事件
Observer.on("publish", function () {
num.innerHTML++;
});
Observer.on('remove', function () {
num.innerHTML--;
});
})();
// 第二个人的代码
; (function () {
// 获取元素
var list = document.getElementById("list");
// 第二个人发现 代码不是立刻执行的 所以提前封装好业务逻辑 放入一个函数
//等用户点的时候 调用这个函数就好了 第三个人也只需要给这个函数传递参数即可
Observer.on("publish", function (msg) {
list.innerHTML += `<li><span>${msg}</span><i>×</i></li>`;
});
// 委托模式添加点击事件
list.onclick = function (e) {
if (e.target.tagName.toLowerCase() === "i") {
e.target.parentNode.remove();
Observer.trigger("remove");
}
}
})();
// 第三个人的代码
; (function () {
// 获取元素
var message = document.getElementById("message");
var btn = document.getElementById("btn");
// 添加事件
btn.onclick = function () {
// 获取数据
var value = message.value;
// 调用其他人提供的函数 并传递参数
Observer.trigger("publish", value)
}
})();
// 注: DOM的事件机制 本质也是用观察者模式实现的
</script>