单例模式 定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。 应用:只存在一个的浮窗(登录浮窗)、只绑定一次的事件、创建唯一实例对象。 实现原理:用变量标志当前是否为某个类创建过对象,如果有,在下一次获取该类的实例时,返回之前创建的对象。 实现 :
把全局变量当做单例来使用,但是要减少全局变量的使用,相对降低全局变量带来的命名污染。
对象字面量
1 2 3 4 5 6 const namespace = { a: 1 , b: () => { alert(2 ); } };
动态创建命名空间
1 2 3 4 5 6 7 8 9 10 11 12 13 let MyApp = {};MyApp.namespace = name => { const parts = name.split('.' ); let current = MyApp; for (let i in parts) { if (!current[parts[i]]) { current[parts[i]] = {}; } current = current[parts[i]]; } }; MyApp.namespace('event' ); MyApp.namespace('dom.style' );
1 2 3 4 5 6 7 8 9 const user = (( ) => { const _name = 'sven' , _age = 29 ; return { getUserInfo: () => { return _name + '_' + _age; } }; })();
惰性单例:在需要的时候才创建对象实例。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 const getSingle = fn => { let result; return () => { return result || (result = fn.apply(this , arguments )); }; }; const createLoginLayer = () => { let div = document .createElement('div' ); div.innerHTML = '登录浮窗' ; div.style.display = 'none' ; document .body.appendChild(div); return div; }; const createSingleLoginLayer = getSingle(createLoginLayer);document .getElementById('loginBtn' ).onclick = () => { let loginLayer = createSingleLoginLayer(); loginLayer.style.display = 'block' ; };
绑定事件只绑定一次。 1 2 3 4 5 6 7 8 9 10 11 12 const bindEvent = getSingle(() => { document .getElementById('div1' ).onclick = () => { alert('click' ); }; return true ; }); const render = () => { bindEvent(); }; render(); render(); render();
策略模式 定义:定义一系列的算法,把他们一个个封装起来,并且使它们可以相互替换,目的是将算法的使用和算法的实现分离开。 实现原理:一个策略模式的程序至少需要两部分组成,一组策略类(封装具体算法,并负责具体的计算过程)和环境类 Context(接受客户发起请求,把请求委托给某一个策略类)。 优点:
利用组合、委托、多态等技术和思想,可以有效地避免多重条件选择语句。
提供了开放-封闭原则的完美支持,将算法封装在独立的 strategy 中,使得它们易于切换,易于理解,易于拓展。
算法可以复用在系统的其他地方,从而避免重复工作。
利用组合和委托让 Context 拥有执行算法的能力,也是继承的一种更轻便的替代方案。
实现:
计算奖金 1 2 3 4 5 6 7 8 9 10 11 12 13 14 const strategies = { S: salary => { return salary * 4 ; }, A: salary => { return salary * 3 ; }, B: salary => { return salary * 2 ; } }; const calculateBonus = (level, salary ) => { return strategies[level](salary); };
策略模式重构表单校验
将校验逻辑封装成策略对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const strategies = { isNonEmpty: (value, errorMsg ) => { if (value === '' ) { return errorMsg; } }, minLength: (value, length, errorMsg ) => { if (value.length < length) { return errorMsg; } }, isMobile: (value, errorMsg ) => { if (!/(^1[3|5|8][0-9]{9}$)/ .test(value)) { return errorMsg; } } };
定义发送请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 const validateFunc = () => { const validator = new Validator(); validator.add([ { dom : registerForm.userName, rules : [{ rule : 'isNonEmpty' , errorMsg : '用户名不能为空' }] }, { dom : registerForm.password, rules : [{ rule : 'isNonEmpty' , errorMsg : '密码不能为空' }, { rule : 'minLength:6' , errorMsg : '密码长度不能少于6位' }] }, { dom : registerForm.phoneNumber, rules : [{ rule : 'isMobile' , errorMsg : '手机号码格式不正确' }] } ]); const errorMsg = validator.start(); return errorMsg; }; registerForm.onsubmit = () => { const errorMsg = validateFunc(); if (errorMsg) { alert(errorMsg); return false ; } };
实现 Vadidator 类(Context),负责接收用户的请求并委托给 strategy 对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class Validator { constructor () { this .cache = []; } add(ruleArr) { ruleArr.forEach(item => { const { dom, rules } = item; rules.forEach(rules => { const { rule, errorMsg } = rules; let array = rule.split(':' ); this .cache.push(() => { const strategy = array.shift(); array.unshift(dom.value); array.push(errorMsg); return strategies[strategy].apply(dom, array); }); }); }); } start() { for (let i = 0 , validatorFunc; (validatorFunc = this .cache[i++]); ) { const msg = validatorFunc(); if (msg) { return msg; } } } }
代理模式 定义:为一个对象提供一个代用品或占位符,以便控制对它的访问。 分类:
虚拟代理(把一些开销很大的对象,延迟到真正需要它的时候才去创建)
保护代理(用于控制不同权限的对象对目标对象的访问)
缓存代理(为一些开销大的运算结果提供暂时的存储,下次运算时如果参数一致,则可以直接返回之前存储的运算结果)
实现:
虚拟代理实现图片预加载 如果不用代理,预加载的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const myImage = (( ) => { const imgNode = document .createElement('img' ); document .body.appendChild(imgNode); let img = new Image(); img.onload = () => { imgNode.src = img.src; }; return { setSrc: src => { imgNode.src = 'loading.gif' ; img.src = src; } }; myImage.setSrc('realImage.jpg' ); })();
使用了虚拟代理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const myImage = (( ) => { const imgNode = document .createElement('img' ); document .body.appendChild(imgNode); return { setSrc: src => { imgNode.src = src; } }; })(); const proxyImage = (( ) => { return { setSrc: src => { myImage.setSrc('loading.gif' ); img.src = src; } }; })(); proxyImage.setSrc('realImage.jpg' );
意义: 符合单一职责原则(就一个类而言,应该仅有一个引起它变化的原因),如果一个对象承担的职责过多,职责的耦合会导致脆弱和低内聚的设计,当发生变化时,设计可能会遭到意外的破坏。 符合开放-封闭原则,如果需求不再需要预加载,可以直接改变调用,而不是改动 myImage 对象,给 img 节点设置 src 和图片预加载两个功能被隔离在两个对象里,它们可以各自变化而不影响对方。
虚拟代理合并 HTTP 请求 通过代理函数收集一段时间内的请求,最后一次性发送给服务器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 const synchronousFile = id => { }; const proxySynchronousFile = (( ) => { let cache = [], timer; return id => { cache.push(id); if (timer) { return ; } timer = setTimeout(() => { synchronousFile(cache.join(',' )); clearTimeout(timer); timer = null ; cache.length = 0 ; }, 2000 ); }; })(); const checkbox = document .getElementsByTagName('input' );for (let i = 0 , c; (c = checkbox[i++]); ) { c.onclick = function ( ) { if (this .checked === true ) { proxySynchronousFile(this .id); } }; }
缓存代理做计算 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 const mult = function ( ) { let a = 1 ; for (let i = 0 , l = arguments .length; i < l; i++) { a *= arguments [i]; } }; const plus = function ( ) { let a = 0 ; for (let i = 0 , l = arguments .length; i < l; i++) { a += arguments [i]; } }; const createProxyFactory = fn => { let cache = {}; return function ( ) { const args = Array .prototype.join.call(arguments , ',' ); if (cache[args]) { return cache[args]; } return (cache[args] = fn.apply(this , arguments )); }; }; const proxyMult = createProxyFactory(mult);const proxyPlus = createProxyFactory(plus);proxyMult(1 , 2 , 3 , 4 ); proxyPlus(1 , 2 , 3 , 4 );