外观模式
-
外观模式:为一组复杂的子系统接口提供一个更高级的统一接口,通过接口使得对子接口的访问更容易,是一种‘套餐式’的较为统一的接口,是对接口方法的外层包装,以供上层代码调用。
-
实现一个事件监听的'基础套餐'封装
function addEvent(dom,type,fn){
//首先优先选择DOM2级事件addEventListener
if(dom.addEventListener){
//事件类型、回调函数、事件冒泡
dom.addEventListener(type,fn,false)
}else if(dom.attachEvent){
dom.attachEvent('on'+type,fn)
}else{
dom['on'+type]=fn;
}
}
- 在‘基础套餐’的代码实现基础上实现一个事件监听‘豪华套餐’封装
//获取事件对象
var getEvent=function(event){
return event || window.event;
}
//获取目标元素
var getTarget=function(e){
var _event=getEvent(e)
return e.target || e.srcElement
}
//阻止默认行为
var preventDefault=function(e){
var _event=getEvent(e);
e.preventDefault?e.preventDefault():e.returnValue=false
}
适配器模式
-
将一个类(对象)的接口(方法或者属性)转换成另一个接口
-
案例一:参数适配器
function todo(obj){
var _adapter={
name:'lth',
tool:'hammer',
weapon:'boom'
};
for(var j in _adapter){
_adapter[j]=obj[j] || _adapter[j]
}
console.log(_adapter)
/**
* { name: 'lth1', tool: 'hammer', weapon: 'boom' }
*
* 提示:如果在for-in中把_adapter换成obj有惊喜
*/
}
todo({
name:'lth1',
tool:'hammer',
weapon:'boom',
price:10,
job:'secret'
})
- 案例二:数据适配(目的是返回一个更好操纵的数据)
var arr=['english','1029','sun']
function dataAdapter(arr){
return {
name:arr[0],
date:arr[1],
mood:arr[2]
}
}
var perData=dataAdapter(arr);
console.log(perData) //{ name: 'english', date: '1029', mood: 'sun' }
- 案例三:参数方法适配
var oneMap={
show:function(){
console.log('开始渲染oneMap')
}
}
var secondMap={
// show:function(){
// console.log('开始渲染secondMap')
// }
//接口变了
fade:function(){
console.log('开始渲染secondMap')
}
}
var secondMapAdapter={
show:function(){
return secondMap.fade()
}
}
var renderMap=function(map){
if(map.show instanceof Function){
map.show()
}
}
renderMap(oneMap)
renderMap(secondMapAdapter)
/**
* 开始渲染oneMap
开始渲染secondMap
*/
代理模式
-
由于一个对象不能直接引用另一个对象所以需要代理,在这两个对象之间起到中介的作用
-
代理模式的运用之解决跨域的问题
-
跨域的报错显示
为XMLHttpRequest cannot load http://xx.com No 'Access-Control-Allow-Origin' header is present on the request resource -
统一域名不同端口号、同一域名不同协议、域名和域名对应的ip地址不同、主域和子域、子域和子域都存在跨域限制
-
利用img的src属性向服务器单向发送(不会有响应数据)携带指定参数的信息
var info=(function(){
var _img=new Image();
//返回函数--此函数的目的是利用src属性将信息拼接上并传递出去
//info是对象‘object’格式的
return function(info){
//供拼接的请求字符串
var str='www.baidu.com/b.png?'
//要拼接的字符串
for(var i in info){
str+= i + '=' + info[i]
};
//发送请求
_img.src=str;
console.log(_img.src)
}
})()
info({number:100})
- 代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问
let lth = {
sellHouse(num){
console.log("我要卖"+ num + "元");
}
}
let proxySeller = {
sellHouse(hasSold,num){
if(hasSold){
// 抽成10元
lth.sellHouse(num-10);
}else{
// 没卖没抽成
lth.sellHouse(num);
}
}
}
// lth.sellHouse(100);
proxySeller.sellHouse(false,100);
- 案例一:lth给lxm送药的故事
- 方法一:lth自己给lxm送药
var Drug=function(){}
var lth={
sendDrug:function(target){
var drug=new Drug();
target.receiveFlower(drug)
}
}
var lxm={
receiveDrug:function(drug){
console.log('我收到了' + drug)
}
}
lth.sendDrug(lxm)
- 方法二:引入代理B,lth通过代理B来给lxm送药
- 进阶版本为引入代理B后,代理B通过监视lxm的身体状况来送药(结合定时器)
var lth={
sendDrug:function(target){
var drug=new Drug();
target.receiveDrug(drug)
}
}
var B={
receiveDrug:function(drug){
lxm.watch(function(){
lxm.receiveDrug(drug)
})
}
}
var lxm={
receiveDrug:function(drug){
console.log('我收到了' + drug)
},
watch:function(fn){
setTimeout(function(){fn()},1000)
}
}
lth.sendDrug(B)
- 案例二:通过代理缓存的方式节省向服务器发送的请求数量,此例为如果我一直快速点击单选框,会向服务器一直发送验证的数据,如果我把每隔几秒内的数据打包起来再一起发送会好很多。
- 该html页面的代码为
<body>
<input type="checkbox" id="1">
<input type="checkbox" id="2">
<input type="checkbox" id="3">
<input type="checkbox" id="4">
<input type="checkbox" id="5">
<input type="checkbox" id="6">
<input type="checkbox" id="7">
<input type="checkbox" id="8">
<input type="checkbox" id="9">
</body>
- 该js逻辑为:
var syFile=function(id){
console.log('文件id为'+id)
}
var checkBox=document.getElementsByTagName('input')
console.log(checkBox)
// for(var i=0,c;c=checkBox[i++];){
// c.onclick=function(){
// // console.log(this)
// // 如果点击迅速的话会实时不断的出现打印
// if(this.checked===true){
// syFile(this.id)
// }
// }
// }
//引入一个代理
var prFile=(
function(){
var cache=[], //保存一段时间需要同步的id
timer; //定时器
return function(id){
cache.push(id);
// console.log(cache)
if(timer){
return;
}
timer=setTimeout(function(){
syFile(cache.join())
clearTimeout(timer)
timer=null;
cache.length=0
},2000)
}
}
)()
for(var i=0,c;c=checkBox[i++];){
c.onclick=function(){
// console.log(this)
// 如果点击迅速的话会实时不断的出现打印
if(this.checked===true){
prFile(this.id)
}
}
}
- 案例三:代理缓存
var mult=function(){
var a=1;
for(var i=0,l=arguments.length;i<l;i++){
a=a*arguments[i]
}
return a;
}
mult(3,4)
var proMult=(function(){
var cache={};
return function(){
//var args=Array.prototype.join.call([1,2,3,4],',') --> '1,2,3,4'
var args=Array.prototype.join.call(arguments,',');
if(args in cache){
return cache[args]
}
return cache[args]=mult.apply(this,arguments)
}
})()
proMult(1,2,3)
装修者模式
- 在不改变原对象的基础上,通过对其进行包装拓展(添加属性或者方法),使原有对象可以满足需求,装饰已有的功能对象,对已有的功能原封不动
/**
*
* @param {要装饰的输入框} input
* @param {要新增给指定输入框的新函数} fn
*/
var decorator=function(input,fn){
//获取到要添加装饰的输入框
var input=document.querySelector('input');
//判断此时的输入框之前已经有添加过点击事件
if(typeof input.onclick === 'function'){
//记录缓存下原有的点击回调函数
var oldFn=input.click;
//装饰者模式开始发力为input框重新定义新的事件
input.onclick=function(){
//原有回调函数
oldFn();
//新增的回调函数
fn();
}
}else{
//如果之前没有给这个input框添加过点击事件则直接为其新增上新的回调函数
input.onclick=fn;
}
}
class Lth {
constructor(){
this.name = "lth";
}
release(){
console.log("释放压力");
// console.log("释放了"+num+"压力");
}
}
Function.prototype.Decorator = function(fn,num){
// console.log(this);
this();
fn(num);
}
// 在执行了hurt(num)
function hurt(num){
console.log("释放了"+num+"压力");
}
let lth = new Lth();
// 先执行了lth.release();
lth.release.Decorator(hurt,10);
桥接模式
-
在系统沿着多个维度变化的同时,有不增加其复杂度并已达到解耦,将实现层与抽象层解耦分离,使两部分可以独立变化
-
场景描述:页面上有许多小组件需要绑定事件并在事件发生后做出相应的改变,例如改变颜色和改变背景色,这时候我们可以把这些操作提取共同点.
-
共同点一:我们需要获取节点,共同点二:我们需要获取改变后的颜色,共同点三:我们需要获取改变后的背景色,封装为一个‘桥梁’供事件使用
function change(dom,color,bgColor){
dom.style.color=color;
dom.style.backgroundColor=bgColor;
}
xx.onclick=function(){
change(this,'red','pink')
}
- 多维变量类的应用。
- 移动单元和弹跳单元可以归纳是球类的属性和方法
//移动单元
function Move(a,b){
this.a=a;
this.b=b
}
Move.prototype.motion=function(){
console.log('移动起来')
}
//弹跳单元
function Bounce(c){
this.c=c;
}
Bounce.prototype.dap=function(){
console.log('弹跳起来')
}
//归纳为可以移动和弹跳的球类
function Ball(a,b,c){
//实现移动单元
this.move=new Move(a,b);
//实现弹跳单元
this.bounce=new Bounce(c);
}
Ball.prototype.init=function(){
//实现移动
this.move.motion();
//实现弹跳
this.bounce.dap();
//true
// console.log(this.move.__proto__===Move.prototype)
}
var b=new Ball(1,2,3)
// console.log(b.move) //Move { a: 1, b: 2 }
// console.log(b.move.a) //1
// console.log(b.bounce) //Bounce { c: 3 }
b.init(); //移动起来 弹跳起来
- 如果我们想要在创建一个球类它可以移动、弹跳和消失呢?
function Fade(d){
this.fade=d;
}
Fade.prototype.fadeIn=function(){
console.log('消失不见')
}
//归纳为可以移动、弹跳和消失的新球类
function NewBall(a,b,c,d){
//实现移动单元
this.move=new Move(a,b);
//实现弹跳单元
this.bounce=new Bounce(c);
//实现消失
this.fade=new Fade(d)
}
NewBall.prototype.init=function(){
//实现移动
this.move.motion();
//实现弹跳
this.bounce.dap();
//实现消失
this.fade.fadeIn();
}
var nb=new NewBall(1,2,3,4);
nb.init();
/**移动起来
弹跳起来
消失不见 */
组合模式
- 组合模式又称‘套餐模式’,又称部分-整体模式,将对象组合成树形结构以表示“部分整体”的层次结构,组合模式使得用户对单个对象和组合对象使用具有一致性
//组合对象
var MacroCommand=function(){
return {
commandsList:[],
add:function(command){
this.commandsList.push(command)
return this
},
//并不执行真正的execute操作,而是遍历它所包含的叶对象
//把真正的execute请求委托给这些叶对象,不在于控制叶对象的访问
execute:function(){
for(var i=0,command;command=this.commandsList[i++];){
command.execute()
}
}
}
}
//叶对象
var openFridgeCommand={
execute:function(){
console.log('打开冰箱')
}
}
//叶对象
var openCurtainsCommand={
execute:function(){
console.log('打开窗帘')
}
}
// var macroCommand= MacroCommand()
// macroCommand.add(openFridgeCommand).add(openCurtainsCommand)
// macroCommand.execute() //打开冰箱 打开窗帘
- 为了防止对叶对象进行误操作比试图在叶对象中使用.add方法,可以在客户给叶对象误操作时抛出异常
var openTabletCommand={
execute:function(){
console.log('打开平板')
},
add:function(){
throw new Error('叶对象不能添加子节点')
}
}
// openTabletCommand.add(macroCommand); //Error: 叶对象不能添加子节点
- 组合模式的运用:扫描文件夹和文件---从上往下
//文件夹Folder
var Folder=function(name){
this.name=name;
this.files=[]
}
Folder.prototype.add=function(file){
this.files.push(file)
}
Folder.prototype.scan=function(){
console.log('现在遍历到哪一个文件夹:'+this.name);
for(var i=0,file,files=this.files;file=files[i++];){
file.scan();
}
}
//文件File
var File=function(name){
this.name=name
}
File.prototype.add=function(){
throw new Error('文件下面不能在添加文件,它相当于叶对象')
}
File.prototype.scan=function(){
console.log('现在遍历到哪一个文件并开始扫描文件:'+ this.name)
}
var folder1=new Folder('文件夹一');
var folder2=new Folder('文件夹二');
var folder3=new Folder('文件夹三');
var file1=new File('文件一');
var file2=new File('文件二');
var file3=new File('文件三');
// folder1.scan() // 现在遍历到哪一个文件夹:文件夹一
// folder1.add(file1);
// folder1.scan() //现在遍历到哪一个文件夹:文件夹一 现在遍历到哪一个文件并开始扫描文件:文件一
- 组合模式的运用:扫描文件夹和文件---从下往上--引用父对象--记录父对象通过父对象来删除叶对象
//文件夹Folder
var Folder=function(name){
this.name=name;
this.files=[];
this.parent=null; //增加this.parent属性
}
Folder.prototype.add=function(file){
file.parent=this; //设置父对象
this.files.push(file)
console.log(this.files)
}
Folder.prototype.scan=function(){
console.log('现在遍历到哪一个文件夹:'+this.name);
for(var i=0,file,files=this.files;file=files[i++];){
file.scan();
}
}
Folder.prototype.remove=function(){
if(!this.parent){
return;
}
console.log('folder start',this.parent.files)
/**
* [ File {
name: '文件5',
parent: Folder { name: '文件夹5', files: [Circular], parent: null } },
Folder {
name: '文件夹6',
files: [],
parent: Folder { name: '文件夹5', files: [Circular], parent: null } } ]
*/
for(var files=this.parent.files,l=files.length-1;l>=0;l--){
var file=files[l];
if(file===this){
files.splice(l,1)
console.log('folder over',files)
/**
* folder over [ File {
name: '文件5',
parent: Folder { name: '文件夹5', files: [Circular], parent: null } } ]
*/
}
}
}
//文件File
var File=function(name){
this.name=name;
this.parent=null;
}
File.prototype.add=function(){
throw new Error('文件下面不能在添加文件,它相当于叶对象')
}
File.prototype.scan=function(){
console.log('现在遍历到哪一个文件并开始扫描文件:'+ this.name)
}
File.prototype.remove=function(){
if(!this.parent){
return;
}
for(var files=this.parent.files,l=files.length-1;l>=0;l--){
var file=files[l];
if(file===this){
files.splice(l,1)
console.log('file over')
}
}
}
var folder5=new Folder('文件夹5');
folder5.add(new File('文件5'))
var folder6= new Folder('文件夹6')
folder5.add(folder6)
/**
*
* folder over
现在遍历到哪一个文件夹:文件夹5
现在遍历到哪一个文件并开始扫描文件:文件5
*/
/**
* [ File {
name: '文件5',
parent: Folder { name: '文件夹5', files: [Circular], parent: null } },
Folder {
name: '文件夹6',
files: [],
parent: Folder { name: '文件夹5', files: [Circular], parent: null } } ]
*/
// folder5.scan();//现在遍历到哪一个文件夹:文件夹5 现在遍历到哪一个文件并开始扫描文件:文件5
folder6.remove()
folder5.scan()
亨元模式
- 运用共享技术有效的支持大量的细粒度的对象,主要是对数据、方法共享分离,它将数据和方法分成内部数据、内部方法、外部数据和外部方法
var Human=function(age){
//内部状态
this.age=age;
};
Human.prototype.show=function(){
console.log(this.age+' external state '+this.externalState)
}
//创建出共享对象---只有当共享状态被需要时才被创建出来
var people=new Human('20');
for(var i=1;i<20;i++){
//在这里定义外部状态
people.externalState='externalState'+i;
people.show();
}