JS中的几种设计模式举例说明
什么是设计模式?
设计模式是解决实际问题的一种固定思路,这些思路通常是从实际经验中总结而来。大部分问题的解决之道是有相同模式的,这些模式总结出来就是我们通常所讲的设计模式。
工厂模式
js中创建对象的一种模式,可以理解为批量生产具有类似属性和行为的对象。
// again factory pattern function factory(name){ var o = new object(); o.name = name; o.type = 'factory'; o.can = function(){ console.log('my name is ' + this.name); } } var o1 = factory('o1'); var o2 = factory('o2'); o1.can(); // my name is o1 o2.can(); // my name is o2
工厂模式具有问题就是不知道对象是那个工厂函数创建的。
看一下下面的代码,复杂的工厂模式
function factory(){ this.name = 'factory'; this.can = function(){ console.log('i can produce the product'); } } factory.prototype.produce = function(){ // 下面实现子类中重复性的业务 console.log('i start work at 8 clock'); // throw new error('fn: produce has to be define by sub'); } function extend(sub, sup){ sub.prototype = new sup(); sub.prototype.constructor = sub; sub.prototype.sup = sup.prototype; } // another extend func (just inherit the sup's prototype) function extend2(sub, sup){ var f = function(){}; f.prototype = sup.prototype; sub.prototype = new f(); // 为什么要借助一个中间函数f sub.prototype.constructor = sub; sub.sup = sup.prototype; } function carfactory(){ this.type = 'car'; factory.call(this); } carfactory.prototype.produce=function(){ phonefactory.sup.produce.call(this); console.log('i produce the car'); } function phonefactory(){ this.type = 'phone'; factory.call(this); } extend2(phonefactory, factory); phonefactory.prototype.produce=function(){ phonefactory.sup.produce.call(this); console.log('i produce the phone'); } var phone = new phonefactory(); var sub = new carfactory();
单体模式
单体模式的特点使实例只会被实例化一次。虽然,对象字面量也可以看做是实现单体模式的一种方式,但它不能实例化。
function singlepattern(name){ if(singlepattern.instance){ return singlepattern.instance; } this.name = name; this.say = function(){ console.log(this.name); } singlepattern.instance = this; } var a = new singlepattern('aaaaa'); var b = new singlepattern('bbbbb'); a.say(); // aaaaa b.say(); // aaaaa a === b; // true 因为单体模式下,只能被实例化一次,当第二次new的时候,返回的是第一次生成的实例
???那么问题来了,页面弹窗是否可以考虑使用单体模式?
单体模式的高级写法:
var getsingleinstance = function(fn){ var o = null; return function(){ return o || (o = fn.apply(this, arguments)); } } // 创建一个弹窗实例的方法 function createdlg(html){ console.log(arguments); var p = document.createelement('p'); p.innerhtml = html; p.style.display = 'none'; return p; } // 创建iframe实例的方法 function createiframe(html){ var iframe = document.createelement('iframe'); iframe.innerhtml = html; iframe.style.display = 'none'; return iframe; } var createsingedlg = getsingleinstance(createdlg); var createsingeiframe = getsingleinstance(createiframe); var dlg1 = createsingedlg( 'dlg1'); var dlg2 = createsingedlg( 'dlg2'); var iframe1 = createsingeiframe('iframe1'); var iframe2 = createsingeiframe('iframe2'); console.log(dlg1.innerhtml); // dlg1 console.log(dlg2.innerhtml); // dlg1 console.log(iframe1.innerhtml); // iframe1 console.log(iframe2.innerhtml); // iframe1
代码示例中最终输出结果证明了只有一个实例被生成
模块模式
还没弄懂这种模式的使用场景,留个坑先
代理模式
保护机制: 过滤掉一些不合理的行为;下面这个例子中,from想送礼物给to, 但是to对于价格低于200的礼物不会接受,这时候它就找了个proxy,帮它甄别这些礼物。
var to = function(){ this.received = function(gift){ console.log("i have received the " + gift); } } var from = function(price, name){ this.gift={ price: price, name: name } this.sendgift = function(proxy){ console.log('i send a gift, and its price is:' + this.gift.price); proxy.sendgift(this.gift); } } var proxy=function(){ this.sendgift = function(gift){ if(gift.price < 200){ console.log('the gift\'s price is too low~~'); } else { new to().received(gift.name); } } } var proxy = new proxy(); new from(100, 'book').sendgift(proxy); new from(300, 'car').sendgift(proxy);
单一原则:对一个类而言,只做一件事;应用实例(图片预加载)
ps: 面向对象设计的原则之一:单一原则
var loadingimg = 'https://loading.io/assets/img/ajax.gif'; var targetimg = 'https://bpic.588ku.com/original_origin_min_pic/18/05/31/f7c539d4b0e349d0869838920f9fc05c.jpg'; // 传统方式的加载 var loadimage=function(src){ var imgnode = document.createelement('img'); imgnode.src = loadingimg; document.body.appendchild(imgnode); var img = new image(); img.onload=function(){ imgnode.src = src; } img.src = src; } loadimage(targetimg); // 使用代理模式 var loadimg = (function(){ var imgnode = document.createelement('img'); document.body.appendchild(imgnode); return function(src){ imgnode.src = src; } })(); var proxy = (function(){ var img = new image(); img.onload=function(){ loadimg(this.src); } return function(src){ loadimg(loadingimg); img.src = src; } })(); proxy(targetimg);
缓存代理:对一些开销大且可能重复性的任务进行缓存处理。
下面实现有个求和函数,如果所有的参数都相同,则直接从缓存中取上一次的计算结果。
function sum(arr){ var sum = 0; sum = arr.reduce(function(pre, cur){ return pre + cur; }); return sum; } var proxy = function(arr){ if(!proxy.cache){ proxy.cache = {}; } arr.sort(); if(proxy.cache[''+arr.join('')] !== undefined){ console.log('the result is from cache'); return proxy.cache[arr.join('')] } else { proxy.cache[arr.join('')] = sum(arr); return proxy.cache[arr.join('')] } }
虚拟代理:把一些开销很大 的操作延迟到真正需要的时候再进行;
文件同步的例子,比如客户端c要从服务端s下载文件。在客户端,每个文件都有个checkbox。
勾选checkbox,则下载文件;不勾选,则不需要下载服务端的文件
var download = function(file){ console.log('download file: ' + file);} var proxy = function(){ var that = this; if(proxy.o){ return proxy.o; } this.files = []; setinterval(function(){ that.files.foreach(function(v){ download(v); that.files.remove(v); }) }, 5000); proxy.o = this; } var p = new proxy(); var ul = document.createelement('ul'); document.body.appendchild(ul); for(let i = 0; i < 20; i++){ let li = document.createelement('li'); let file = (+new date())+(math.random(0,1)* 10000).tofixed(0); li.innerhtml = '' + file; li.onclick=function(){ var input = li.firstelementchild; if(input.checked){ p.files.add(input.value); } else { p.files.remove(input.value); } } ul.appendchild(li); } array.prototype.add=function(item){ if(this.indexof(item) == -1){ this.push(item); } } array.prototype.remove=function(item){ let i = this.indexof(item); this.splice(i, 1); }
观察者模式
下面是个开胃菜,一个很简单的例子
// 创建对象 var o = { }; function observer(oldval, newval) { // 其他处理逻辑... console.info('name属性的值从 '+ ( oldval || '' ) +' 改变为 ' + newval); } object.defineproperty(o, 'name', { configurable: true, enumerable: true, get: function(){ return this.val; }, set: function(n){ observer(this.val, n); this.val = n; } }); o.name = 1; o.name = 2;
发布订阅模式
这种模式跟观察者模式很类似,他们都实现了不同间的解耦。
array.prototype.add=function(item){ if(this.indexof(item) == -1){ this.push(item); } } array.prototype.remove=function(item){ let i = this.indexof(item); this.splice(i, 1); } function com(name){ this.name = name; } com.prototype = (function () { var events = {}; return { on: function (e, fn) { events[e] = events[e] || []; events[e].add(fn); }, off: function (e, fn) { events[e] = events[e] || []; if (fn) { events[e].remove(fn); } else { events[e] = null; } }, fire:function(e, data){ if(events[e]){ events[e].foreach(function(fn){ fn(data) }); } } } })(); com.prototype.constructor = com; var a = new com('a'); a.on('e.a', function(d){console.log(d)}); var b = new com('b'); b.fire('e.a', 'b') // 多写了一个深度复制的方法 object.defineproperty(object.prototype, 'extend', { confiugrable: true, enumerable: false, writable: false, value: function(){ var source = null; while(source = array.prototype.shift.call(arguments)){ for(key in source){ if(typeof source[key] === 'object'){ this[key] = this[key] || array.isarray(source[key]) [] : {} ; this[key].extend(source[key]); } else { this[key] = source[key]; } } } } });
迭代器模式
var iterator = function() { this.index = 0; this.items = [1,2,,3,4,5]; } iterator.prototype={ next: function(){ if(this.index >= this.items.length){ this.index = this.index - this.items.length; } return this.items[this.index++]; }, hasnext: function(){ return this.index < this.items.length; }, rewind: function(){ this.index = 0; }, current: function(){ return this.items[this.index] } } iterator.prototype.constructor = iterator; var a = new iterator(); var b = new iterator(); console.log(a.next()); console.log(a.next()); console.log(b.next());
利用迭代器模式,重写了array.prototype.reduce方法
(function(){ function array1(a){ var values = new array(); values.push.apply(values, arguments); values.next = 1; values.cur = 0; return values; } array.prototype.reduce1=function(fn){ if(this.length==0){ return null; } if(this.length == 1){ return fn(this[0], '', 0, this); } if(this.length == 2){ return fn(this[0], this[1], 1, this); } var that = this, pre = that[0]; while(this[this.next]){ pre = fn(pre, that[that.next], that.next, that); this.cur++; this.next++; } return pre; } var a = new array1(1,2,3,4); var r = a.reduce1(function(pre, cur, index, arr){ return pre * cur; }); console.log(r); })()
装饰者模式
在不改变原有对象基础上,丰富原对象的功能。
function a(){ this.name = 'a'; } a.prototype.say=function(){console.log(this.name)} var say1 = a.prototype.say; a.prototype.say=function(){ say1.call(this); console.log("i am new say fn "); } var a = new a(); a.say();
装饰者模式的一个应用场景有哪些?
面向切面 aspect oriented programming(aop)
下面的例子中一个前置装饰,一个后置装饰。
function.prototype.before=function(beforefn){ var that = this; return function(){ beforefn.apply(this, arguments); return that.apply(this, arguments); } } function.prototype.after=function(afterfn){ var that = this; return function(){ var result = that.apply(this, arguments); afterfn.apply(this, arguments); return result; } } function foobar(x, y){ console.log(x, y) } function foo(x, y){ console.log(x/10, y/10); } function bar(x, y){ console.log(x*10, y*10); } foobar = foobar.before(foo).after(bar); foobar(2,3); // 0.2 0.3 // 2 3 // 20 30
aop的应用场景相对而言,还是比较广泛的。日志、安全控制/访问控制、性能监测以及缓存等,这些都是aop的典型应用场景。
上一篇: 微信公众号H5支付接口调用方法
下一篇: 冰淇淋制作方法,美味的奥利奥冰淇淋