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.name
this.age = param.age
}

function CreateName(name) {
this.wholeName = name
this.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 = work
this.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>

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) // 打印true

var a1 = createSingle()
_unique = null // 在中途修改了_unique都会导致返回的对象不一致
var b1 = createSingle()

console.log(a1 === b1) // false

var 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 = true
this.price += 0.8
}
}

var car1 = new Car()
console.log(this.price) // 10
car1.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 = true
carClass.price += 2
}

function carWithAutoMirror(carClass) {
carClass.hasAutoMirror = true
carClass.price += 0.8
}

var car2 = new Car()
console.log(this.price) // 10
carWithHeatSeat(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.prototype
subClass.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.id
this.element.method = this.method
this.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' + className
this.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
}

// input
function 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.type
this.element.id = this.id
this.element.name = this.name
this.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
// 上下两个语句一样的效果,但最好是返回this
return 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 : 0
for (i = 0; i < len; i++) {
this[i] = dom[i]
}
this.length = len
this.selector = selector || ''
}

$.fn = {
// 去看源码
a: function () {
console.log('aaaaa');
return this
},
n: function () {
console.log('nnnnn');
return this
}
}

zepto.Z.prototype = Z.prototype = $.fn
$.zepto = zepto

return $
})()

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.SUCCESS
var 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.SUCCESS

value = 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.model
var v = MVC.view
var 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/set
2.需要知道修改的这个数据跟哪个视图有关联,观察者模式
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 = 2
data.b = 3
</script>
</head>
<body>

</body>
</html>

posted @ 2021-10-30 20:08  aixuexi666888  阅读(143)  评论(0编辑  收藏  举报