JavaScript设计模式浅析

  • JavaScript设计模式浅析
    • 一、工厂模式
      • 1、简单的工程模式
      • 2、工程模式实例
    • 二、建造者模式
      • 1、建造者模式例子
    • 三、单体模式
      • 1、普通创建对象方式
      • 2、单体模式创建对象
    • 四、装饰器模式
      • 1、装饰器模式例子
    • 五、组合模式
      • 1、组合模式例子
    • 六、观察者模式
      • 1、观察者模式例子
    • 七、策略模式
      • 1、策略模式例子
    • 八、链模式
      • 1、链模式例子
      • 2、Zepto链模式简析
    • 九、委托模式
      • 1、委托模式例子
    • 十、数据访问对象模式
      • 1、数据访问对象模式例子
    • 十一、等待者模式
      • 1、等待者模式例子
    • 十二、MVC模式
      • 1、MVC模式例子
    • 十三、MVVM模式
      • 1、Vue响应式数据原理解析

JavaScript设计模式浅析

一、工厂模式

工程模式是一种比较简单的设计模式,主要用来创建对象用的

1、简单的工程模式

//1.简单的工程模式.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script>function createPerson(name) {var o = {}o.name = name;o.getName = function() {console.log(this.name)}return o}var person1 = createPerson('zhangsan');person1.getName()console.log(person1.name)</script></head>
<body></body>
</html>

2、工程模式实例

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script>function Person(name) {this.name = name}Person.prototype.getName = function () {console.log(this.name)}function Car(model) {this.model = model}Car.prototype.getModel = function() {console.log(this.model)}function create(type, params) {return new this[type](params)}create.prototype = {person: Person,car: Car,}var person1 = new create('person', 'zhangsan')person1.getName()/*new create('person, 'zhangsan') -> {__proto__: create.prototype}new this[type](params) = new Person('zhang san'); -> {__proto__: Person.prototype,name: 'zhangsan'}*/// 如果不想要使用new进行工程创建对象function create(type, param) {// instanceof 判断一个构造函数的prototype属性所指向的对象是否在另一个被检测对象的原型链上// obj instanceof F// 描述:obj.proto.proto… => F.prototype,沿着对象obj的原型链查找是否存在对象F.prototype,// 若存在则返回true,若查找到原型链的终点Object.prototype仍未找到,则返回false。if (this instanceof create) {return new this[type](params)} else {return new create(type, param)}}var  car1 = create('car', 'benz');car1.getModel()</script>
</head>
<body></body>
</html>

二、建造者模式

建造者模式注重创建对象的细节,使用这种模式创建出的复杂对象或者复合对象结构会非常清晰

1、建造者模式例子

比如有一家招聘中介公司,现在有一批纸质简历或者简历数据信息,为了做成统一的电子版简历,我们需要将这些信息进行整理,我们如何去做呢

// 未使用建造者模式前
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script>var data = [{name: 'zhang san',age: 24,work: 'engineer'},{name: 'li si',age: 25,work: 'teacher'},{name: 'wang wu',age: 26,work: 'sb'},]function Candidate(param) {var _candidate = {};_candidate.name = param.name_candidate.age = param.age;_candidate.firstName = _candidate.name.split(' ')[0];_candidate.secondName = _candidate.name.split(' ')[1];_candidate.work = {};switch(param.work) {case 'engineer': _candidate.work.name = '工程师'_candidate.work.description = '热爱编程'break;case 'teacher': _candidate.work.name = '老师'_candidate.work.description = '热爱分享'break;default:_candidate.work.name = param.work_candidate.work.description = '无'break}_candidate.work.changeWork = function(work) {this.name = work}_candidate.work.changeDes = function(des) {this.description = des;}return _candidate}var candidateArr = []for (let i = 0; i < data.length; i++) {candidateArr[i] = Candidate(data[i])}console.log(candidateArr[0])candidateArr[1].work.changeWork('xxx')console.log(candidateArr[1].work)</script>
</head>
<body></body>
</html>

// 建造者模式
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script>var data = [{name: 'zhang san',age: 24,work: 'engineer'},{name: 'li si',age: 25,work: 'teacher'},{name: 'wang wu',age: 26,work: 'sb'},]function Candidate(param) {var _candidate = new Person(param);_candidate.name = new CreateName(param.name)_candidate.work = new CreateWork(param.work)return _candidate}function Person(param) {this.name = param.namethis.age = param.age}function CreateName(name) {this.wholeName = namethis.firstName = name.split(' ')[0]this.secondName = name.split(' ')[1]}function CreateWork(work) {switch(work) {case 'engineer': this.name = '工程师'this.description = '热爱编程'break;case 'teacher': this.name = '老师'this.description = '热爱分享'break;default:this.name = workthis.description = '无'break}}CreateWork.prototype.changeWork = function(work) {this.name = work}CreateWork.prototype.changeDes = function(des) {this.description = des}var candidateArr = []for (let i = 0; i < data.length; i++) {candidateArr[i] = Candidate(data[i])}console.log(candidateArr[0])candidateArr[1].work.changeWork('xxx')console.log(candidateArr[1].work)</script>
</head>
<body></body>
</html>

三、单体模式

单体模式是设计模式中最常用的一种。单体模式就是希望可以在使用方法创建对象的时候,我们无论创建多少次对象都指向同一个

1、普通创建对象方式

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script>function notSingle() {return {a: 1}}var a = notSingle()var b = notSingle()console.log(a === b) // 打印 falsefunction notSingle2() {this.a = 123}var a1 = new notSingle2()var a2 = new notSingle2()console.log(a1 === b1); // 打印 false// 无论是使用函数,还是用构造函数的情况下,我们创建出来的对象都是相互独立的// 在绝大多数情况下,这也是我们所期望的结果,这是没问题的// 当在某些情况下,我们需要他们创建出来的返回的数据是已经被创建出来的对象数据,而不是再去创建出来的新的对象</script>
</head>
<body></body>
</html>

2、单体模式创建对象

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script>var _unique = null;function createSingle() {var obj = {a: 1}if (_unique === null) {_unique = obj}return _unique}var a = createSingle()var b = createSingle()console.log(a === b) // 打印truevar a1 = createSingle()_unique = null // 在中途修改了_unique都会导致返回的对象不一致var b1 = createSingle()console.log(a1 === b1) // falsevar createSingle1 = (function() {var _unique = null;function single() {return {a: 1}}return function() {if (_unique === null) {// _unique = {//     a: 1// }_unique = single()} else {return _unique}}})()var a2 = createSingle1()var b2 = createSingle1()console.log(a2 === b2)</script>
</head>
<body></body>
</html>

四、装饰器模式

装饰器模式,希望在不改变原对象的基础上,通过对其拓展功能和属性来实现更复杂的逻辑

1、装饰器模式例子

假设有一家店在卖一种车,价格为10万元,如果用户需要在此基础上加装一些配置则需要加钱。比如加热座椅配置需要两万元,电动后视镜需要0.8万元等。

// 一般写法
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script>function Car() {this.price = 10;}Car.prototype = {addHeatSeat: () => {this.hasHeatSeat = true;this.price += 2},addAutoMirror: () => {this.hasAutoMirror = truethis.price += 0.8}}var car1 = new Car()console.log(this.price) // 10car1.addHeatSeat()car1.addAutoMirror()console.log(car1.price) // 12.8</script>
</head>
<body></body>
</html>
// 装饰器模式
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script>function Car() {this.price = 10;}// 装饰器模式减少了对构造函数的操作function carWithHeatSeat(carClass) {carClass.hasHeatSeat = truecarClass.price += 2}function carWithAutoMirror(carClass) {carClass.hasAutoMirror = truecarClass.price += 0.8}var car2 = new Car()console.log(this.price) // 10carWithHeatSeat(car2)carWithAutoMirror(car2)console.log(car2.price) // 12.8</script>
</head>
<body></body>
</html>

五、组合模式

组合模式作用于将多个部分通过组合变成一个整体。就如同我们去麦当劳点了一个汉堡、两个薯条和一杯可乐。我们可以将这些东西看成一个个部分或者说组件,通过组合就可以将整个套餐产出给顾客。这个就是组合模式

1、组合模式例子

比如我们在工作中经常会制作一些表单,比如登录、注册,或者一些信息填写等等,这些表单其实都是类似的,如果你今天制作一个注册的表单,明天做个调查问卷的表单,是不是你会觉得麻烦,有些重复劳动的感觉。学会组合模式就会好很多了

注:组合模式属于那种,代码量越大,重复需求越大(就是那种两个页面之间只有小部分代码不一致,但是又不能用组件化的代码的那种需求),效率就越高。如果只是小容量的系统,基本不需要使用组合模式来创建表单。

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><script>window.onload = function() {// 组合寄生继承function inheritPrototype(subClass, superClass) {function F() {};F.prototype = superClass.prototypesubClass.prototype = new F()subClass.prototype.constructor = subClass}// 容器基类function Container() {this.children = []this.element = null}Container.prototype = {init: () => {throw new Error('请重写init方法')},add: function(child){this.children.push(child);this.element.appendChild(child.element)return this // 方便链式调用}}// 基于容器基类创建表单容器function CreateForm(id, method, action, parent) {Container.call(this)this.id = id || ''this.method = method || ''this.action = action || ''this.parent = parent || ''this.init()}inheritPrototype(CreateForm, Container);CreateForm.prototype.init = function() {this.element = document.createElement('form')this.element.id = this.idthis.element.method = this.methodthis.element.action = this.action}CreateForm.prototype.show = function() {this.parent.appendChild(this.element)}// 行容器组件function CreateLine(className) {Container.call(this)this.className = className === undefined ? 'form-line' : 'form-line' + classNamethis.init()}inheritPrototype(CreateLine, Container);CreateLine.prototype.init = function() {this.element = document.createElement('div')this.element.className = this.className}// label // 最下层叶子节点不需要继承容器基类的方法function CreateLabel(text, forName) { this.text = text || ''this.forName = forName || ''this.init()}CreateLabel.prototype.init = function() {this.element = document.createElement('label')this.element.setAttribute('for', this.forName)this.element.innerHTML = this.text}// inputfunction CreateInput(type, id, name, defaultValue) {this.type = type || ''this.id = id || '';this.name = name || '';this.defaultValue = defaultValue || '';this.init()}CreateInput.prototype.init = function() {this.element = document.createElement('input')this.element.type = this.typethis.element.id = this.idthis.element.name = this.namethis.element.value = this.defaultValue}var form = new CreateForm('owner-form', 'GET', '/aaa.html', document.body);var userLine = new CreateLine().add( new CreateLabel('用户名', 'user') ).add( new CreateInput('text', 'user', 'user')) var passwordLine = new CreateLine().add( new CreateLabel('密码', 'pwd') ).add( new CreateInput('password', 'pwd', 'pwd')) var submitLine = new CreateLine().add( new CreateLabel('submit', '', '', '登录') )form.add(userLine).add(passwordLine).add(submitLine).show()}// var aa = new Container()</script></head>
<body><!-- 普通方式实现表单 --><!-- <form action="xxx" method="GET"><div class = 'form-line'><label for="user">用户名</label><input type="text" id = "user" name ="user"/></div><div class = 'form-line'><label for="pwd">用户名</label><input type="password" id = "pwd" name ="pwd"/></div><div class = 'form-line'><input type="submit" value="登录"/></div></form>-->
</body>
</html>

六、观察者模式

观察者模式又叫发布订阅模式或者消息模式。是设计模式中非常著名也是非常重要的一种模式,这种模式一般会定义一个主体和众多的个体,这里的主体可以想象为一个消息中心,里面有各种各样的消息,众多的个体可以订阅不同的消息,当未来消息中心发布某条信息的时候,订阅过他的个体就会等到通知

1、观察者模式例子

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><script>// 消息中心var msgCenter = (function () { var _msg = {}; // 存储消息/*var _msg = {'carInfo': [person1.alreadyRegister.carInfo, person2.alreadyRegister.carInfo, person3.alreadyRegister.carInfo, , person5.alreadyRegister.carInfo]'newInfo': [person1.alreadyRegister.newInfo, person2.alreadyRegister.newInfo, person3.alreadyRegister.newInfo, , person4.alreadyRegister.newInfo]}*/return {// 用于订阅一个消息register: function(type, fn) { // fn 回调函数if (_msg[type]) {_msg[type].push(fn)} else {_msg[type] = [fn]}},// 用于发布消息fire: function(type, args) { // args 发布消息所附带的信息if (!_msg[type]) {return} var event = {type: type,args: args || {}}for(var i = 0; i < _msg[type].length; i++) {_msg[type][i](event)}},// 用于取消订阅的消息cancel: function(type, fn) {if (!_msg[type]) {return}for(var i = 0; i < _msg[type].length; i++) {if (_msg[type][i] === fn) {_msg[type].splice(i, 1)return}}}}})()function Person() {this.alreadyRegister = {};}Person.prototype.register = function(type, fn) {if (this.alreadyRegister[type]) {console.log('您已经订阅过这个消息了,请不要重复订阅')} else {msgCenter.register(type, fn);this.alreadyRegister[type] = fn}}Person.prototype.cancel = function (type) { msgCenter.cancel(type, this.alreadyRegister[type])delete this.alreadyRegister[type]}var person1 = new Person()var person2 = new Person()var person3 = new Person()var person4 = new Person()var person5 = new Person()person1.register('carInfo', function (e) { console.log('person1得到了关于' + e.type + '的消息,消息内容是:' + e.args.info);})person1.register('newInfo', function (e) { console.log('person1得到了关于' + e.type + '的消息,消息内容是:' + e.args.info);})person2.register('carInfo', function (e) { console.log('person2得到了关于' + e.type + '的消息,消息内容是:' + e.args.info);})person2.register('newInfo', function (e) { console.log('person2得到了关于' + e.type + '的消息,消息内容是:' + e.args.info);})person3.register('carInfo', function (e) { console.log('person3得到了关于' + e.type + '的消息,消息内容是:' + e.args.info);})person3.register('newInfo', function (e) { console.log('person3得到了关于' + e.type + '的消息,消息内容是:' + e.args.info);})person4.register('carInfo', function (e) { console.log('person4得到了关于' + e.type + '的消息,消息内容是:' + e.args.info);})person5.register('newInfo', function (e) { console.log('person5得到了关于' + e.type + '的消息,消息内容是:' + e.args.info);})msgCenter.fire('carInfo', {info: '新汽车上市!'})msgCenter.fire('newInfo', {info: '某某某来了!'})person1.register('carInfo', function(e) {console.log('test')})console.log('------------------')person1.cancel('carInfo')msgCenter.fire('carInfo', { info: '再发一条' })</script>
</head>
<body></body>
</html>

七、策略模式

策略模式是一种相对比较简单的设计模式,主要用于有多种状态或者策略需要进行选择的时候,将所有选择封装在一起,只给外部暴露出必要的接口

1、策略模式例子

比如我们看这样一个例子,我们有一个表单,需要验证表单项,如果验证不通过就返回一个提示文案。这里我们就可以使用策略模式的思路进行封装

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script>var formStrategy = (function() {var strategy = {notEmpty: function(value) {return value.length ? '' : '请填写内容'},isNumber: function(value) {var reg = /^[0-9]+(\.[0-9]+)?$/return reg.test(value) ? '': '请填写一个数字'},isPhone: function(value) {var reg = /^\d{3}-\d{8}$|^\d{4}-\d{7}$/return reg.test(value) ? '': '请输入一个正确的电话号码'}}return {validate: function(type, value) {value = value.replace(/^\s+|\s+$/, '');return strategy[type] ? strategy[type](value) : '没有这个检测方法, 请手动添加'},addStrategy: function(type, fn) {if (strategy[type]) {return '这个方法已经存在'} else {strategy[type] = fn}}}})()window.onload = function() {formStrategy.addStrategy('isEmail', function(value) {var reg = /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-a-zA-Z0-9_-]+)+$/return reg.test(value) ? '' : '请输入一个正确的邮箱地址'})var oInput = document.querySelector('input')oInput.onchange = function() {var result;result = formStrategy.validate('notEmpty', this.value) ||formStrategy.validate('isEmail', this.value) || //  formStrategy.validate('isNumber', this.value) || '通过检测'console.log(result)}}</script>
</head>
<body><input type="text">
</body>
</html>

八、链模式

链模式是实现链式调用的主要方法,通过在自身方法中返回自身的方式,在一个对象连续多次调用自身方法是可以简化写法的。

这种链式调用在很多库和框架中,如jQuery、zepto中频繁的使用

1、链模式例子

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script>var obj = {a: function() {console.log('aaaa')// return obj// 上下两个语句一样的效果,但最好是返回thisreturn this},b: function() {console.log('bbbb')return obj}}obj.a().b().a() // 打印aaaa bbbb aaaa</script>
</head>
<body></body>
</html>

2、Zepto链模式简析

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><!--使用npm引入zepto--><script src='./node_modules/zepto/dist/zepto.js'></script><script>// Uncaught SyntaxError: Unexpected token var// var a = 1 var b = 1 // 正常情况下,JavaScript能够识别这样定义的代码为两行代码// var a = 1// var b = 2// TypeError: 2 is not a function// 自执行函数前面 加上一个; 否则自执行函数前面所定义变量的地方,// 会认为是获取要执行2这个自执行函数所得到的返回值 也即是执行 2(function(){})()// 但是2并不是一个自执行函数, 所以报错// (function(){})()// (function(){})()// Uncaught TypeError: (intermediate value)(...) is not a function// 自执行函数 就算是分成两行来写,js也不能很好的分清楚// 这两行代码的具体执行的方式。所以会报错// 如果写Javascript代码时,有不打分号;的习惯的话,就要记得在// 自执行函数前加上分号(但是最好要有打分号的习惯)// 模拟实现zepto(function (global, factory) { if (typeof define === 'function' && define.amd) {// amd 模块化方法引入// xxx 由于这里我们用npm下载之后引入,所以不会走这步// 想走这步的,可以自己试试} else {factory(global)}})(this, function(window) {var Zepto = (function() {var emptyArray = []var zepto = {}var $ = function (selector, context) { return zepto.init(selector, context)}zepto.init = function (selector, context) { var dom = zepto.qsa(document, selector)return zepto.Z(dom, selector)}zepto.qsa = function (element, selector) { //.... 详细去看源码// 简单情况下的操作return [document.getElementById(selector.slice(1))]}zepto.Z = function (dom, selector) { return zepto.Z(dom, selector)/*Z的内容:$ = zepto.init = zepto.Z = new Z {0:domA,lenght: 1,selector: '#a',__proto__: $.fn}*/}function Z(dom, selector) { var i, len = dom ? dom.length : 0for (i = 0; i < len; i++) {this[i] = dom[i]} this.length = lenthis.selector = selector || ''}$.fn = {// 去看源码a: function () { console.log('aaaaa');return this},n: function () { console.log('nnnnn');return this}}zepto.Z.prototype = Z.prototype = $.fn$.zepto = zeptoreturn $})()window.Zepto2 = Zepto// 由于已经引入了zepto, 所以$就不定义了})/*Z的内容:$ = zepto.init = zepto.Z = new Z {0:domA,lenght: 1,selector: '#a',__proto__: $.fn}*/window.onload = function () { console.log($('#a'));console.log(Zepto2('#a'));}</script>
</head>
<body><div id='a'>aaa</div>
</body>
</html>

九、委托模式

当多个对象需要处理同一请求时,可以将这些请求交由另一个对象同一处理

1、委托模式例子

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><script>window.onload = function () { // var aLis = document.getElementsByTagName('li')// for( var i = 0; i < aLis.length; i++) {//     aLis[i].onclick = function () { //         console.log(i); // 每次都会打印4//      }// }// 两种解决变量提升的方式// ES6 let// let aLis = document.getElementsByTagName('li')// for(let i = 0; i < aLis.length; i++) {//     aLis[i].onclick = function () { //         console.log(i);//      }// }// 闭包// var aLis = document.getElementsByTagName('li')// for(var i = 0; i < aLis.length; i++) {//     (function (i) { //         aLis[i].onclick = function () { //             console.log(i);//          }//     })(i)// }// // 该方式存在问题// // 1。需要每个li上绑定不同的事件,会造成性能的损失和内存占用// // 2. 新增的li还需要重新绑定事件// var oUl = document.querySelector('ul')// var oLi = document.createElement('li') // oLi.innerHTML = 'eeeee'// oUl.appendChild(oLi)// oLi.onclick = function () { //     // xxx// }var oUl = document.querySelector('ul')oUl.onclick = function (e) { var e = e || window.event,target = e.target || e.srcElement; // IE的兼容处理if (target.nodeName.toLowerCase() === 'li') {console.log(target.innerHTML);}   }var oLi = document.createElement('li') oLi.innerHTML = 'eeeee'oUl.appendChild(oLi)}</script>
</head>
<body><ul id="ul1"><li>aaaa</li><li>bbbb</li><li>cccc</li><li>dddd</li></ul>
</body>
</html>

十、数据访问对象模式

数据访问对象模式主要是用来抽象和封装一个对象来对数据源进行访问和存储,这样可以方便对数据的管理,以及避免数据间的重复。覆盖等问题出现。

1、数据访问对象模式例子

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><script>/*{nameSpace|key: expire|value}    */var DataVisitor = function (nameSpace, splitSign) { this.nameSpace = nameSpace;this.splitSign = splitSign || '|';}DataVisitor.prototype = {status: {SUCCESS: 0,FAILURE: 1,OVERFLOWER: 2, // 内存溢出TIMEOUT: 3,},getKey: function (key) { return this.nameSpace + this.splitSign + key},set: function (key, value, cbFn, expireTime) { var status = this.status.SUCCESS// this.key = this.getKey(key)// this.expireTime =  typeof expireTime === 'number' ? expireTime + new Date().getTime() : -1;key = this.getKey(key)expireTime =  typeof expireTime === 'number' ? expireTime + new Date().getTime() : -1;try{window.localStorage.setItem(key, expireTime + this.splitSign + value)} catch(e) {status = this.status.OVERFLOWER}cbFn && cbFn.call(this, status, key, value)return value},get: function (key, cbFn) { key = this.getKey(key);var status = this.status.SUCCESSvar value = window.localStorage.getItem(key)if (value) {var index = value.indexOf(this.splitSign),time = value.slice(0, index);if (time > new Date().getTime() || time == -1) {value = value.slice(index + this.splitSign.length);} else {value = null;status = this.status.TIMEOUT;window.localStorage.removeItem(key)}} else {status = this.status.FAILURE}cbFn && cbFn.call(this, status, key, value)return value},remove: function (key, cbFn) { key = this.getKey(key);var status = this.status.SUCCESSvalue = window.localStorage.getItem(key)if (value) {value = value.slice(value.indexOf(this.splitSign) + this.splitSign.length);window.localStorage.removeItem(key)var status = this.status.SUCCESS} cbFn && cbFn.call(this, status, key, value)}}var learnInPro = new DataVisitor('learnInPro')learnInPro.set('aaa', '123', function (status, key, value) { console.log(status, key, value);})learnInPro.get('aaa', function (status, key, value) { console.log(status, key, value);})learnInPro.remove('aaa', function (status, key, value) { console.log(status, key, value);})learnInPro.set('bbb', '123', function (status, key, value) { console.log(status, key, value);}, 1000 * 2)learnInPro.get('bbb', function (status, key, value) { console.log(status, key, value);})setTimeout(function () { learnInPro.get('bbb', function (status, key, value) { console.log(status, key, value);})}, 1000 * 3)</script>
</head>
<body></body>
</html>

十一、等待者模式

通过对多个异步进程的监听,对未来事件进行统一管理

1、等待者模式例子

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><script>/*waiter.when(异步事件1, 异步事件2, ...).done(fn).fail(fn)    *///等待者模式// 成熟的等待者模式 有 async/await 模式 和 promise模式function Waiter() { var dfd = []; // 用来存放异步事件var doneArray = []; // 用来存放done注册成功之后的回调var failArray = []; // 存放失败的回调this.when = function () { // 获取arguments -> 一个类数组对象dfd = Array.prototype.slice.call(arguments); // 将类数组转换成数组for (var i = dfd.length -1; i >= 0; i--) {var d = dfd[i];if (!d || d.rejected || d.resolved || !(d instanceof Defer)) {dfd.splice(i, 1);}}return this}this.done = function () { var args = Array.prototype.slice.call(arguments);doneArray = doneArray.concat(args)return this}this.fail = function () {  var args = Array.prototype.slice.call(arguments);failArray = failArray.concat(args)return this}this.Defered = function () {  return new Defer()}var Defer = function () { this.resolved = false;this.rejected = false;}Defer.prototype = {resolve: function () { this.resolved = true;for (var i = 0; i < dfd.length; i++) {if (!dfd[i].resolved) {return}}// 如果全部完成,就不会被return出去,则可执行下面的语句// 执行成功的回调函数_exec(doneArray)},reject: function () { this.rejected = true;// 执行失败的回调_exec(failArray)}}function _exec(arr) { for( var i = 0; i < arr.length; i++) {arr[i] && arr[i]()}}}var waiter = new Waiter()var async1 = function () { var dfd = waiter.Defered()setTimeout(function () { console.log('async1 done');dfd.resolve()}, 1000)return dfd}var async2 = function () { var dfd = waiter.Defered()setTimeout(function () { console.log('async1 done');dfd.resolve()}, 2000)return dfd}waiter.when(async1(), async2()).done(function () { console.log('success');}).fail(function () { console.log('fail');})</script>
</head>
<body></body>
</html>

十二、MVC模式

MVC 模式代表 Model-View-Controller(模型-视图-控制器) 模式。这种模式用于应用程序的分层开发。

  • Model(模型) - 模型代表一个存取数据的对象或 JAVA POJO。它也可以带有逻辑,在数据变化时更新控制器。
  • View(视图) - 视图代表模型包含的数据的可视化。
  • Controller(控制器) - 控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开。

他是一种使用业务逻辑、数据和视图进行分类的方式来进行组织架构代码的一种模式

1、MVC模式例子

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><script>// 一个简单的mvc实例var MVC = {}MVC.model = (function () { var data = {sidebar: [{title: 'sidebar1',href: './a.html'},{title: 'sidebar2',href: './aa.html'},{title: 'sidebar3',href: './aaaa.html'}]};return {getData: function (key) { return data[key];},setData: function (key, value) { data[key] = value;MVC.view('createSidebar')}}})()MVC.view = (function () { var m = MVC.model;var view = {createSidebar: function () { var data = m.getData('sidebar')var html = '';html += '<div id="#sidebar">';for(var i = 0; i < data.length; i++) {html += '<div class="sidebar-item"><a href="' + data[i].href + '">' + data[i].title + '</a></div>';}html += '</div>'document.body.innerHTML = html}}return function (v) { view[v]()}})()MVC.ctrl = (function () { var m = MVC.modelvar v = MVC.viewvar c = {initSideBar: function () { v('createSidebar')},updateSideBar: function () { m.setData('sidebar', [{title: 'new sidebar', href:'./aaa.html'}])}}return c})()window.onload = function () { MVC.ctrl.initSideBar()setTimeout(function () { MVC.ctrl.updateSideBar()}, 3000)}</script>
</head>
<body></body>
</html>

十三、MVVM模式

MVVM是Model-View-ViewModel的缩写

Model:代表数据模型也可以在model中定义数据修改和操作的业务逻辑,也可以称为数据层,因为它仅仅只关心数据,不关心任何行为

View:用户操作界面,当ViewModel对Mdodel进行更新的时候,会通过数据绑定更新到View

ViewModel:业务逻辑层,View需要什么数据,ViewModel要提供这个数据;View有某些操作ViewModel就要响应这些操作,所以可以说他是Model for View

mvvm即Model-View-ViewModel,mvvm的设计原理是基于mvc的,所以说mvvm不算是一种创新,充其量是一种改造,这其中的ViewModel便是一个小小的创新

1、Vue响应式数据原理解析

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><script>/*如何去实现数据与视图绑定1.需要知道哪个数据改变了。一般我们可以使用数据访问对象的方法,在vue中我们使用的是es5的对象访问属性get/set2.需要知道修改的这个数据跟哪个视图有关联,观察者模式3.修改视图*/// var test = {//     _a: undefined,//     get a() {//         return this._a//     },//     set a(newVal) {//         this._a = newVal//     }// }// console.log(test.a); // undefined// test.a = 'abc';// console.log(test.a); // abc// console.log(test); // {_a: abc}// // 如果不想打印出_a// var test2 = (function () { //     var _a = undefined//     var test = {//         get a () {//             return _a;//         },//         set a (newVal) {//             _a = newVal;//         }//     }//     return test// })// console.log(test2); // {a: undefined}// var test3 = {_a: 111};// Object.defineProperty(test3, 'a', {//     get: function () { //         return this._a;//     },//     set: function (newVal) { //         this._a = newVal//     }// })// console.log(test3.a); // 111// test3.a = 'abc'// console.log(test3.a); // abc// ----------------// var data = {//     a: 1// };// var dep = []; // 依赖收集, 消息中心// var target = null; // 订阅者或者叫观察者// Object.defineProperty(data, 'a', {//     get: function () { //         dep.push(target)//     },//     set: function (newVal) {  //         for(var i = 0; i < dep.length; i++) {//             dep[i]()//         }//     }// })// function watch(exp, fn) { //     target = fn;//     data[exp] // 调用 data[exp].get()// }// watch('a', function () {  //     console.log('我是第一个监听a改变的函数');// })// watch('a', function () {  //     console.log('我是第二个监听a改变的函数');// })// data.a = 2/*上面代码存在的几个问题:1.我们现在访问不到a, 并且a的值需要另一个变量区暂存。2.目前只做了一个数据的收集工作3.如果再次访问属性的时候,就会重复收集依赖*/var data = {a: 1,b: 2};for(var key in data) {(function (key) { var dep = [];var value = data[key];Object.defineProperty(data, key, {get: function () { for(var i = 0; i < dep.length; i++) {if (dep[i] === target) {return value}}dep.push(target)return value},set: function (newVal) {  if (newVal !== value) {value = newVal;for (var i = 0; i < dep.length; i++) {dep[i]()}}}})})(key)}// var dep = []; // 依赖收集, 消息中心var target = null; // 订阅者或者叫观察者// Object.defineProperty(data, 'a', {//     get: function () { //         dep.push(target)//     },//     set: function (newVal) {  //         for(var i = 0; i < dep.length; i++) {//             dep[i]()//         }//     }// })function render() { document.write('<div>文案test: <p>a的值: ' + data.a + ', b的值:' + data.b + '</p></div>')}function watch(exp, fn) { target = fn;if (typeof exp === 'function') {exp();return;}data[exp] // 调用 data[exp].get()}// watch('a', function () {  //     console.log('我是第一个监听a改变的函数');// })// watch('a', function () {  //     console.log('我是第二个监听a改变的函数');// })// watch('b', function () {  //     console.log('我是第一个监听b改变的函数');// })watch(render, render)data.a = 2data.b = 3</script>
</head>
<body></body>
</html>

JavaScript设计模式浅析相关推荐

  1. 听飞狐聊JavaScript设计模式系列05

    本回内容介绍 上一回聊到JS的类的模拟,继承,分析了nodejs,extjs,jquery,underscore的继承源码. 介一回,偶们来聊一下在JS中模拟接口,掺元类,装饰者模式,有些盆友可能用过 ...

  2. 《JavaScript设计模式》——11.2 一切只因跨域

    本节书摘来自异步社区<JavaScript设计模式>一书中的第11章,第11.2节,作者:张容铭著,更多章节内容可以访问云栖社区"异步社区"公众号查看 11.2 一切只 ...

  3. 《JavaScript设计模式与开发实践》模式篇(12)—— 装饰者模式

    在传统的面向对象语言中,给对象添加功能常常使用继承的方式,但是继承的方式并不灵活, 还会带来许多问题:一方面会导致超类和子类之间存在强耦合性,当超类改变时,子类也会随之 改变;另一方面,继承这种功能复 ...

  4. JavaScript设计模式与开发实践——JavaScript的多态

    "多态"一词源于希腊文polymorphism,拆开来看是poly(复数)+ morph(形态)+ ism,从字面上我们可以理解为复数形态. 多态的实际含义是:同一操作作用于不同的 ...

  5. 16种JavaScript设计模式(中)

    简介 上文中介绍了学习设计模式前需要了解的一些基础概念和js的基础模式-原型模式,没看过的同学可以点这里,本章将介绍以下几种模式 单例模式 策略模式 代理模式 迭代器模式 发布订阅模式 命令模式 组合 ...

  6. javascript 设计模式(一)

    1.为什么要深入学习Javascript? 用户对页面的美观性,易用性要求越来越高 Javascript+HTMl5+CSS3将是未来客户端技术的潮流 学好JS有助于更好学习JS库.JS框架 开发自己 ...

  7. JavaScript设计模式--简单工厂模式例子---XHR工厂

    JavaScript设计模式--简单工厂模式例子---XHR工厂 第一步,Ajax操作接口(目的是起一个接口检测作用) (1)引入接口文件 //定义一个静态方法来实现接口与实现类的直接检验 //静态方 ...

  8. JavaScript 设计模式基础(二)

    JavaScript 设计模式基础(一) 原型模式 在以类为中心的面向对象编程语言中,类和对象的关系就像铸模和铸件的关系,对象总是从类中创建.而原型编程中,类不是必须的,对象未必从类中创建而来,可以拷 ...

  9. 《JavaScript设计模式与开发实践》阅读摘要

    <JavaScript设计模式与开发实践>作者:曾探 系统的介绍了各种模式,以及js中的实现.应用,以及超大量高质量代码,绝对值得一读 面向对象的js 静态类型:编译时便已确定变量的类型 ...

最新文章

  1. 6名学生返校后被判定为密接,高校紧急通知!
  2. 4.Git基础-查看提交历史
  3. selenium+Headless Chrome实现不弹出浏览器自动化登录
  4. 提莫隐身+机器人能钩_航空工业官宣全新歼20正式亮相,可以隐身的变形金刚
  5. 互斥信号量的等待与通知
  6. Oracle不使用索引的几种情况列举
  7. 微信公众平台前端开发技巧分享
  8. Nginx内存管理详解
  9. javafx中的tree_JavaFX中的塔防
  10. 团队项目第二次冲刺(4)
  11. mysql join 循环_关于mysql联表的内嵌循环操作nested loop join中on和where执行顺序问题...
  12. html特殊字符p如何屏蔽 asp,利用asp去除html标记的四种方法
  13. 谷歌地球(Google Earth)7.3.1.4507官方多语言专业版
  14. 计算机控制液压同步顶升系统,变频控制同步顶升液压系统
  15. Vue路由守卫(通俗易懂)
  16. 洛谷P2440 木材加工 —二分答案
  17. ACO 蚁群算法(算法流程,TSP例子解析)
  18. 【深度学习框架体系的学习】pytorch
  19. 《锋迷商城》——首页:轮播图
  20. 在linux中使用getch()函数

热门文章

  1. 修身养性,为人处事100条
  2. Android美化之全局透明背景
  3. LC振荡电路L和C 参数越小 频率越高
  4. Python之pandas库(万年一更版)
  5. 【PAT乙级】1103 缘分数 (20 分)
  6. 2022年Unity客户端面试题总结
  7. 外汇交易提醒:美元三连阴创逾一周新低,欧洲央行或准备更大幅度加息
  8. 中考准用计算机,2021中考倒计时,考试需要带哪些物品?考生和家长需要提前了解...
  9. ORB_原理与源码解析
  10. Kerbose 原理