结构型设计模式
结构型设计模式关注于如何将类或对象组合成更大,更复杂的结构,以简化设计
Facade
外观模式,为一组复杂的子系统接口提供一个更高级的统一接口,通过这个接口使得对子系统接口的访问更加容易
document.onclick=function (ev) {
ev.preventDefault();
if (ev.target!==document.getElementById('myinpuy')){
hidePageAlert();
}
};
function hidePageAlert() {}
外观模式实现
function addEvent(dom, type, fn) {
if (dom.addEventListener){
dom.addEventListener(type,fn,false);
} else if (dom.attachEvent){
dom.attachEvent('on'+type,fn);
} else {
dom['on'+type]=fn;
}
}
let myInput=document.getElementById('myinput');
addEvent(myInput,'click',function () {
console.log('first event');
});
addEvent(myInput,'click',function () {
console.log('second');
});
addEvent(myInput,'click',function () {
console.log('third');
});
解决兼容性,preventDefault和target
let getEvent=function (event) {
return event||window.event;
};
let getTarget=function (event) {
let ev=getEvent(event);
return ev.target||ev.srcElement;
};
let preventDefault=function (event) {
let ev=getEvent(event);
if (event.preventDefault){
ev.preventDefault();
} else {
ev.returnValue=false;
}
};
function hideInputSug() {}
document.onclick=function (ev) {
preventDefault(ev);
if (getTarget(ev) !==document.getElementById('myinput')){
hideInputSug();
}
};
小型代码库
let A={
g:function (id) {
return document.getElementById(id);
},
css:function (id, key, value) {
document.getElementById(id).style[key]=value;
},
attr:function (id, key, value) {
document.getElementById(id)[key]=value;
},
html:function (id, html) {
document.getElementById(id).innerHTML=html;
},
on:function (id, type, fn) {
document.getElementById(id)['on'+type]=fn;
}
};
A.css('box','background','red');
A.attr('box','className','box');
A.html('box','new container');
A.on('box','click',function () {
A.css('box','width','500px');
});
Adapter
适配器模式.将一个类(对象)的接口(方法或属性)转化成另一个接口,满足用户的需求,使类(对象)之间接口的不兼容问题通过适配器得以解决
let A=A||{};
A.g=function (id) {
return document.getElementById(id);
};
A.on=function (id, type, fn) {
let dom=typeof id==='string'?this.g(id):id;
if (dom.addEventListener){
dom.addEventListener(type,fn,false);
} else if (dom.attachEvent){
dom.attachEvent('on'+type,fn);
} else{
dom['on'+type]=fn;
}
};
A.on(window,'load',function () {
A.on('mybutton','click',function () {});
});
A.g=function (id) {
return $(id).get(0);
};
A.on=function (id, type, fn) {
let dom=typeof id==='string'?$('#'+id):$(id);
dom.on(type,fn);
};
参数适配器
方法需要传递多个参数时,可传递多个参数
function dosomething(name,title,age,color,size,price) {}
let obj={
name:'name',
title:'title',
age:'age',
color:'color',
size:'size',
prize:'prize'
};
function dosomething(obj) {
let _adapter={
name:'name',
title:'title',
age:'age',
color:'color',
size:'size',
prize:'prize'
};
for (let i in _adapter){
_adapter[i]=obj[i]||_adapter[i];
}
}
数据适配
将数组适配成对象
let arr=['javascript','book','html','8.1'];
let obj={
name:'',
title:'',
type:'',
time:''
};
function arrToObj(arr) {
return {
name: arr[0],
type: arr[1],
title: arr[2],
data: arr[3]
};
}
let data=arrToObj(arr);
console.log(data)
服务器端数据适配
适配服务器端数据的变化
function ajaxAdapter(data) {
return [data['key1'],data['key2'],data['key3']];
}
$.ajax({
url:'someAdress.php',
success:function (data, status) {
if (data){
dosometing(ajaxAdapter(data));
}
}
});
proxy
代理模式,由于一个对象不能直接引用另一个对象,所以需要通过代理对象在两个对象之间起到中介作用
let Count=(function () {
let _img=new Image();
return function (param) {
let str='http://www.count.com/a.gif?';
for (let i in param){
str +=i+'='+param[i];
}
_img.src=str;
}
})();
Count({num:10});
小明给女神送花,无代理模式
let Flower=function () {};
let xiaoming={
sendFlower:function (target) {
let flower=new Flower();
target.receiveFlower(flower);
}
};
let A={
receiveFlower:function (flower) {
console.log('receive'+flower);
}
};
xiaoming.sendFlower(A);
引入B代理
let Flower=function () {};
let xiaoming={
sendFlower:function (target) {
let flower=new Flower();
target.receiveFlower(flower);
}
};
let B={
receiveFlower:function (flower) {
A.receiveFlower(flower);
}
};
let A={
receiveFlower:function (flower) {
console.log('receive'+flower);
}
};
xiaoming.sendFlower(B);
加入B的优势
let Flower=function () {};
let xiaoming={
sendFlower:function (target) {
let flower=new Flower();
target.receiveFlower(flower);
}
};
let B={
receiveFlower:function (flower) {
A.listenGoodMood(function () {
A.receiveFlower(flower);
});
}
};
let A={
receiveFlower:function (flower) {
console.log('receive'+flower);
},
listenGoodMood:function (fn) {
setTimeout(function () {
fn();
},3000);
}
};
xiaoming.sendFlower(B);
将开销大的对象延迟到真正需要时才创建
let B={
receiveFlower:function (flower) {
A.listenGoodMood(function () {
let flower=new Flower();
A.receiveFlower(flower);
});
}
};
虚拟代理实现图片预加载
let myImage=(function () {
let imgNode=document.createElement('img');
document.body.appendChild(imgNode);
return {
setSrc:function (src) {
imgNode.src=src;
}
};
})();
myImage.setSrc('http://');
添加代理对象
let myImage=(function () {
let imgNode=document.createElement('img');
document.body.appendChild(imgNode);
return {
setSrc:function (src) {
imgNode.src=src;
}
};
})();
myImage.setSrc('http://');
let proxyImage=(function () {
let img=new Image();
img.onload=function () {
myImage.setSrc(this.src);
};
return {
setSrc:function (src) {
myImage.setSrc('file://');
}
};
})();
proxyImage.setSrc('http://');
不使用代理
let myImage=(function () {
let imgNode=document.createElement('img');
document.body.appendChild(imgNode);
let img=new Image();
img.onload=function () {
imgNode.src=img.src;
};
return {
setSrc:function (src) {
imgNode.src='file://';
img.src=src;
}
};
})();
myImage.setSrc('http://');
虚拟代理合并HTTP请求
let synchronousFile=function (id) {
console.log('start id:'+ id);
};
let proxySynchronousFile=(function () {
let cache=[],
timer;
return function (id) {
cache.push(id);
if (timer){
return null;
}
timer=setTimeout(function () {
synchronousFile(cache.join(','));
clearTimeout(timer);
timer=null;
cache.length=0;
},2000);
};
})();
let checkbox=document.getElementsByTagName('input');
for (let i=0,c;c=checkbox[i++];){
c.onclick=function () {
if (this.checked===true){
proxySynchronousFile(this.id);
}
};
}
虚拟代理在惰性加载中的应用
let miniConsole=(function () {
let cache=[];
let handler=function (ev) {
if (ev.keyCode===113){
let script=document.createElement('script');
script.onload=function () {
for (let i=0,fn;fn=cache[i++];){
fn();
}
};
script.src='miniConsole.js';
document.getElementsByTagName('head')[0].appendChild(script);
document.body.removeEventListener('keydown',handler);
}
};
document.body.addEventListener('keydown',handler,false);
return {
log:function () {
let args=arguments;
cache.push(function () {
return miniConsole.log.apply(miniConsole,args);
});
}
}
})();
miniConsole.log(11);
miniConsole={
log:function () {
console.log(Array.prototype.join.call(arguments));
}
};
缓存代理
let mult=function () {
console.log('start cal');
let a=1;
for (let i=0,l=arguments.length;i<l;i++){
a=a*arguments[i];
}
return a;
};
console.log(mult(2,3),mult(2,3,4));
let proxyMult=(function () {
let cache={};
return function () {
let args=Array.prototype.join.call(arguments,',');
if (args in cache){
return cache[args];
}
return cache[args]=mult.apply(this,arguments);
}
})();
console.log(proxyMult(1,2,3,4),proxyMult(1,2,3,4));
用高阶函数动态创建代理
let mult=function () {
console.log('start cal');
let a=1;
for (let i=0,l=arguments.length;i<l;i++){
a=a*arguments[i];
}
return a;
};
let plus=function(){
let a=0;
for (let i=0,l=arguments.length;i<l;i++){
a=a+arguments[i];
}
return a;
};
let createProxyFactory=function(fn){
let cache={};
return function () {
let args=Array.prototype.join.call(arguments,',');
if (args in cache){
return cache[args];
}
return cache[args]=fn.apply(this,arguments);
}
};
let proxyMult=createProxyFactory(mult),
proxyPlus=createProxyFactory(plus);
console.log(proxyMult(1,2,3,4),proxyMult(1,2,3,4));
console.log(proxyPlus(1,2,3,4),proxyPlus(1,2,3,4));
Decorator
装饰者模式,在不改变原有对象的基础上,通过对其进行包装拓展(添加属性或方法)使原有对象可以满足用户的更复杂的需求
let decorator=function (input, fn) {
let inp=document.getElementById(input);
if (typeof inp.onclick==='function'){
let oldClickFn=inp.onclick;
inp.onclick=function () {
oldClickFn();
fn();
}
} else {
inp.onclick=fn;
}
};
Bridge
桥接模式,在系统沿着多个维度发生变化时,又不增加其复杂度并达到解耦
let spans=document.getElementsByTagName('span');
spans[0].onmouseover=function () {
this.style.color='red';
this.style.background='#ddd';
};
spans[0].onmouseout=function () {
this.style.color='#333';
this.style.background='#f5f5f5';
};
spans[1].onmouseover=function () {
this.getElementsByTagName('string')[0].style.color='red';
this.getElementsByTagName('string')[0].style.background='#ddd';
};
spans[1].onmouseout=function () {
this.getElementsByTagName('string')[0].style.color='#333';
this.getElementsByTagName('string')[0].style.background='#f5f5f5';
};
桥接模式
function changeColor(dom, color, bg) {
dom.style.color=color;
dom.style.background=bg;
}
let spans=document.getElementsByTagName('span');
spans[0].onmouseover=function () {
changeColor(this,'red','#ddd');
};
spans[0].onmouseout=function () {
changeColor(this,'#333','#f5f5f5');
};
spans[1].onmouseover=function () {
changeColor(this.getElementsByTagName('string')[0],'red','#ddd');
};
spans[1].onmouseout=function () {
changeColor(this.getElementsByTagName('string')[0],'#333','#f5f5f5');
};
多元化对象
function Speed(x, y) {
this.x=x;
this.y=y;
}
Speed.prototype.run=function () {
console.log('run run');
};
function Color(cl) {
this.color=cl;
}
Color.prototype.draw=function () {
console.log('draw color');
};
function Shape(sp) {
this.shape=sp;
}
Shape.prototype.change=function () {
console.log('change shape');
};
function Speek(wd) {
this.word=wd;
}
Speek.prototype.say=function () {
console.log('speek word');
};
function Ball(x, y, c) {
this.speed=new Speek(x,y);
this.color=new Color(c);
}
Ball.prototype.init=function () {
this.speed.run();
this.color.draw();
};
function People(x, y, f) {
this.speed=new Speed(x,y);
this.font=new Speek(f);
}
People.prototype.init=function () {
this.speed.run();
this.font.say();
};
function Spirite(x, y, c, s) {
this.speed=new Speed(x,y);
this.color=new Color(c);
this.shape=new Shape(s);
}
Spirite.prototype.init=function () {
this.speed.run();
this.color.draw();
this.shape.change();
};
let p=new People(10,12,16);
p.init();
Composite
组合模式,又称部分-整体模式,将对象组合成树形结构以表示"部分整体"的层次结构,组合模式使得用户对单个对象和组合对象使用具有一致性
let News=function () {
this.children=[];
this.element=null;
};
News.prototype={
init:function () {
throw new Error('rewrite your method');
},
add:function () {
throw new Error('rewrite your method');
},
getElement:function () {
throw new Error('rewrite your method');
},
};
let Container=function (id, parent) {
News.call(this);
this.id=id;
this.parent=parent;
this.init();
};
inheritPrototype(Container,News);
Container.prototype.init=function () {
this.element=document.createElement('ul');
this.element.id=this.id;
this.element.className='new-container';
};
Container.prototype.add=function (child) {
this.children.push(child);
this.element.appendChild(child.getElement());
return this;
};
Container.prototype.getElement=function () {
return this.element;
};
Container.prototype.show=function () {
this.parent.appendChild(this.element);
};
let Item=function (classname) {
News.call(this);
this.classname=classname||'';
this.init();
};
inheritPrototype(Item,News);
Item.prototype.init=function(){
this.element=document.createElement('li');
this.element.className=this.classname;
};
Item.prototype.add=function (child) {
this.children.push(child);
this.element.appendChild(child.getElement());
return this;
};
Item.prototype.getElement=function () {
return this.element;
};
let NewsGroup=function (classname) {
News.call(this);
this.classname=classname||'';
this.init();
};
inheritPrototype(NewsGroup,News);
NewsGroup.prototype.init=function(){
this.element=document.createElement('div');
this.element.className=this.classname;
};
NewsGroup.prototype.add=function (child) {
this.children.push(child);
this.element.appendChild(child.getElement());
return this;
};
NewsGroup.prototype.getElement=function () {
return this.element;
};
let ImageNews=function (url, href, classname) {
News.call(this);
this.url=url||'';
this.href=href||'#';
this.classname=classname||'normal';
this.init();
};
inheritPrototype(ImageNews,News);
ImageNews.prototype.init=function () {
this.element=document.createElement('a');
let img=new Image();
img.src=this.url;
this.element.appendChild(img);
this.element.className='image-news'+this.classname;
this.element.href=this.href;
};
ImageNews.prototype.add=function () {};
ImageNews.prototype.getElement=function () {
return this.element;
};
let IconNews=function (text, href, type) {
News.call(this);
this.text=text||'';
this.href=href||'#';
this.type=type||'video';
this.init();
};
inheritPrototype(IconNews,News);
IconNews.prototype.init=function () {
this.element=document.createElement('a');
this.element.innerHTML=this.text;
this.element.href=this.href;
this.element.className='icon '+this.type;
};
IconNews.prototype.add=function () {};
IconNews.prototype.getElement=function () {
return this.element;
};
let EasyNews=function (text, href) {
News.call(this);
this.text=text||'';
this.href=href||'#';
this.init();
};
inheritPrototype(EasyNews,News);
EasyNews.prototype.init=function () {
this.element=document.createElement('a');
this.element.innerHTML=this.text;
this.element.href=this.href;
this.element.className='text';
};
EasyNews.prototype.add=function () {};
EasyNews.prototype.getElement=function () {
return this.element;
};
let TypeNews=function (text, href, type, pos) {
News.call(this);
this.text=text||'';
this.href=href||'#';
this.type=type||'';
this.pos=pos||'left';
this.init();
};
inheritPrototype(TypeNews,News);
TypeNews.prototype.init=function () {
this.element=document.createElement('a');
if (this.pos==='left'){
this.element.innerHTML='['+this.type+']';
} else {
this.element.innerHTML=this.text+'['+this.type+']';
}
this.element.href=this.href;
this.element.className=this.classname;
};
TypeNews.prototype.add=function () {};
TypeNews.prototype.getElement=function () {
return this.getElement();
};
let new1=new Container('news',document.body);
new1.add(
new Item('normal').add(
new IconNews('succsssss','#','video')
)
).add(
new IconNews('project','#','live')
).add(
new Item('normal').add(
new NewsGroup('has-img').add(
new ImageNews('img/1.jpg','#','small')
).add(
new EasyNews('from 240 to 120','#')
).add(
new EasyNews('five man','#')
)
)
).add(
new Item('normal').add(
new TypeNews('ak47','#','NBA','left')
).add(
new Item('normal').add(
new TypeNews('three','#','CBA','right')
)
)
).show();
Flyweight
享元模式,运用共享技术有效支持大量的细粒度的对象,避免对象间拥有相同内容造成多余的开销
原始需求
let dom=null,
paper=0,
num=5,
i=0,
len=article.length;
for (;i<len;i++){
dom=document.createElement('div');
dom.innerHTML=article[i];
if (i>=num){
dom.style.display='none';
}
document.getElementById('container').appendChild(dom);
}
document.getElementById('next_page').onclick=function () {
let div=document.getElementById('container').getElementsByTagName('div');
let k;
let n;
let j = k = n = 0;
n=++paper % Math.ceil(len/num)*num;
for (;j<len;j++){
div[j].style.display='none';
}
for (;k<5;k++){
if (div[n+k])
div[n+k].style.display='block';
}
};
享元模式
let Flyweight=function () {
let created=[];
function create() {
let dom=document.createElement('div');
document.getElementById('container').appendChild(dom);
created.push(dom);
return dom;
}
return {
getDiv:function () {
if (created.length<5){
return create();
} else {
let div=created.shift();
created.push(div);
return div;
}
}
}
}();
let paper=0,
num=5,
len=article.length;
for (let i=0;i<5;i++){
if (article[i])
Flyweight.getDiv().innerHTML=article[i];
}
document.getElementById('next_page').onclick=function () {
if (article.length<5)
return null;
let n=++paper*num%len,
j=0;
for (;j<5;j++){
if (article[n+j]){
Flyweight.getDiv().innerHTML=article[n+j];
} else if (article[n+j-len]) {
Flyweight.getDiv().innerHTML=article[n+j-len];
}else {
Flyweight.getDiv().innerHTML="";
}
}
};
享元动作
let Flyweight={
moveX:function (x) {
this.x=x;
},
moveY:function (y) {
this.y=y;
}
};
let Player=function (x, y, c) {
this.x=x;
this.y=y;
this.color=c;
};
Player.prototype=Flyweight;
Player.prototype.changeC=function (c) {
this.color=c;
};
let Spirit=function (x,y,r) {
this.x=x;
this.y=y;
this.r=r;
};
Spirit.prototype=Flyweight;
Spirit.prototype.changeR=function (r) {
this.r=r;
};
let player1=new Player(5,6,'red');
console.log(player1);
player1.moveX(6);
player1.moveY(7);
player1.changeC('pink');
console.log(player1);
let spirit1=new Spirit(2,3,4);
console.log(spirit1);
spirit1.moveX(3);
spirit1.moveY(4);
spirit1.changeR(5);
console.log(spirit1);