前端设计模式
设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理地运用设计模式可以完美地解决很多问题,每种模式在现实中都有相应的原理来与之对应,每种模式都描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是设计模式能被广泛应用的原因。
单例模式
这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象
- 1、单例类只能有一个实例。
- 2、单例类必须自己创建自己的唯一实例。
- 3、单例类必须给所有其他对象提供这一实例。
就如在单页面中,全局就只有一个Audio对象,在需要音频的地方更换 src ,然后控制其播放,暂停等一系列:
1
2
3 1window.audioContext = new Audio()
2
3
在小程序中,只有一个背景音频,然后切换音频就是更改 src ,然后控制其播放,暂停等一系列 :
1
2
3
4
5
6
7
8
9 1// app.js
2App({
3 onLaunch(){
4
5 },
6 audioContext: wx.getBackgroundAudioManager()
7})
8
9
然后播放页面中使用即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 1// play.js
2const app = getApp()
3Page({
4 onLoad(){
5
6 },
7 onShow(){
8 this.playAudio()
9 },
10 playAudio(){
11 let audioDom = app.audioContext
12 audioDom.title = '起风了'
13 audioDom.src = 'http://audio.22family.com/audio/url/1553691835029489897'
14 audioDom.coverImgUrl = 'http://audio.22family.com/audio/cover/1553691837836859778'
15 audioDom.play()
16 }
17})
18
19
工厂模式
这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象
就如express就是是工厂模式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 1function express(){
2 var express = {}, req = {}, res = {};
3 express.get = function(pathName, cb){
4 // 根据nodejs的path模块获取现在的路由
5 // if(path=== pathName){ cb(req, res) }
6 cb(req, res)
7 }
8 res.send = function(msg){
9 document.write(msg)
10 }
11 return express;
12}
13var app = express()
14app.get('/', function(req, res){
15 res.send(111)
16})
17
18
观察者模式
观察者模式( 又叫发布者-订阅者模式 )应该是最常用的模式之一. 在很多语言里都得到大量应用. 包括我们平时接触的dom事件. 也是js和dom之间实现的一种观察者模式.
-
就像常见的监听
1
2
3
4
5 1window.addEventListener('click', function(){
2 console.log('正在点击')
3})
4
5
- 在vue中的watch和computed:
vue中watch和computed就相当于订阅了data的数据,当数据变化是通知到它们,然后执行相应的操作;vue的虚拟Dom树也订阅了data,computed,watch的数据,当其变化时通知Dom树渲染;低版本的vue可能复杂的数据结构数据变化监听不到,要是用set来设置数据,或者使用$forceUpdate()来强制渲染一次
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 1export default{
2 data(){
3 return{
4 name: 'mySkey'
5 }
6 },
7 computed:{
8 nameLen(){
9 return this.name.length
10 }
11 },
12 watch:{
13 status(){
14 return this.name === 'mySkey'
15 }
16 }
17}
18
19
- 直播和订阅
现在火热的直播,女主播叫点个订阅,点了订阅就能收到飞机票了,你点了订阅就能下次给你发推送了!
- 微信公众号关注
原型模式
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
就像我们经常使用的js对象,Array,String······
1
2
3
4
5
6 1// 这样定义一个数组
2var arr = [1,2,3]
3// 其实是执行了下面这样
4var arr = new Array(1,2,3)
5
6
然后,arr便继承了Array的原型属性和方法(比如length属性,slice,pop,push,shift,unshift,splice等方法)
同样的我们也能自己来扩展js的原生对象的原型链,比如String没有reverse方法:
1
2
3
4
5
6
7
8
9 1if(!String.prototype.reverse){
2 String.prototype.reverse = function(){
3 return Array.prototype.slice.call(this.split('')).reverse().join('')
4 }
5}
6var str = 'hello world'
7console.log(str.reverse())
8
9
js面向对象继承构造函数的属性和方法,就是通过js的_proto_来传递的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 1function Person(name, age){
2 this.name = name
3 this.age = age
4}
5
6// 在prototype中定义的属性和方法,不会在每次实例化的时候都初始化一次,所以把这类必定相同的属性和方法写在prototype中
7Person.prototype = {
8 sing: function(){
9 console.log('sing')
10 }
11}
12var mySkey = new Person('mySkey', 24)
13mySkey.sing()
14console.log(mySkey.name, mySkey.age)
15
16
在es6中使用类class,这些属性和方法还能被继承。但是在js中原理都一个,它们通过一条原型链传递
1
2
3
4
5
6
7
8
9
10
11
12
13
14 1class Person{
2 constructor(name,age){
3 this.name = name
4 this.age = age
5 }
6 sing(){
7 console.log('sing')
8 }
9}
10var mySkey = new Person('mySkey', 24)
11mySkey.sing()
12console.log(mySkey.name, mySkey.age)
13
14
适配器模式
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。就像我们手机只能使用20v的电压,为了在家用电充电,我们需要一个充电头,它就能将220v的电压转成20v的,而且每个厂家的充电头还不一样,所以原装的还贵
在开发中使用到适配器模式的情况也多不胜数:
- 1、数据库设计管理员类型type(甚至其他字段),状态status,一般就不会使用vachar类型,选择int类型更加节省空间,就将status定义为 0 禁用 1 可用
在页面中显示时,我们肯定不是显示数字的
1
2
3 1let statusArr = ['禁用', '可用']
2
3
通过访问数组的键来得到值,这就是适配器模式
-
2、通常我们需要对日期,金钱这些数据进行格式化操作
1
2
3
4
5
6 1let getYear = (t)=>{
2 let date = new Date(t)
3 return date.getFullYear()
4}
5
6
桥接模式
桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。
- 1、举一个简单的例子
我之前不明白为什么公司代码里要将ui框架的toast,confirm,alert这些二次封装,然后来调用。你试想一下,如果有一天,你需要更换个ui框呢?你之前直接使用ui框架的写法,是不是就全部得重写,这是很恐怖的,而你全部写在一个文件中的话,只用更改这个文件的写法,这就是桥接模式。
- 2、在实现api的时候,桥接模式就很好用
将请求接口的工具二次封装,方便下次更换
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 1import axios from 'axios'
2let API_URL = 'https://www.baidu.com'
3export default{
4 get (url, data){
5 return new Promise((resolve,reject)=>{
6 axios({
7 url: API_URL + url,
8 method: 'get',
9 params: data,
10 headers: {
11 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
12 }
13 }).then((res) => {
14 if(res.data.code == 404){
15 alert('系统似乎出了点问题~');
16 return reject(res.data);
17 }else{
18 return resolve(res.data);
19 }
20 });
21 }).catch((err)=>{
22 reject(err)
23 })
24 }
25}
26
27
过滤器模式
过滤器模式(Filter Pattern)或标准模式(Criteria Pattern)是一种设计模式,这种模式允许开发人员使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来。这种类型的设计模式属于结构型模式,它结合多个标准来获得单一标准
- 1、购物车订单
购物车订单提交的时候,我们只需要提交被用户选择的商品的id
1
2
3
4
5
6
7 1sunmit(orders){
2 let idsArr = orders.filter(v=> v.isSelected)
3 let ids = idsArr.join('')
4 ajax.post('/order', { ids })
5}
6
7
- 2、去除关键词
经常遇到一些喷子(性情暴躁,语言粗鲁),系统要将这些骂人的话屏蔽掉
1
2
3
4
5
6
7
8
9
10
11
12
13 1let filterWords = (words)=>{
2 let reg = /(cao|sb|你妹|日你妈|狗日的|傻狗)/gi
3 if(words.match(reg)){
4 let arr = words.match(reg)
5 arr.forEach(e=>{
6 words = words.replace(e, e.split('').map(v=>'*').join(''))
7 })
8 }
9 return words
10}
11console.log(filterWords('你好, 傻狗'))
12
13
装饰器模式
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能
- 就像我们使用形容词一样
对类进行修饰,在实例化的时候加上形容词;知道中间件的人最能将这两者联系起来,其实装饰器就和中间件差不多
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 1function war(status){
2 return function (target){
3 target.prototype.wear = '穿着' + status
4 target.prototype.sing = function(){
5 console.log('sing')
6 }
7 }
8}
9
10@war('朴素的')
11class Father{}
12
13@war('时髦的')
14class Daughter{}
15
16var father = new Father()
17var daughter = new Daughter()
18console.log(father.wear + '父亲, ' + daughter.wear + '女儿')
19
20
- 理解成中间件
可将一些日志操作及其他放置在装饰器中,在类的声明时就能记录相应的日志
1
2
3
4
5
6
7
8 1function method(){
2 console.log('使用了函数方法')
3}
4
5@method
6class Method{} // 使用了函数方法
7
8
外观模式
外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性
就像你需要获取一个用户的姓名和他的年龄,有两个函数分别执行这两个功能
1
2
3
4 1let getName = ()=>'mySkey'
2let getAge = ()=>23
3
4
那么你需要分别调用两个函数才能一次获取姓名和年龄,为何不提供一个高级的接口呢?
1
2
3
4
5 1let getUserinfo = ()=>{
2 return getNum() + getAge()
3}
4
5
享元模式
享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式
- 时间显示控件
演示一下未使用享元模式的程序,这个代码就会多次实例化Date对象,虽然很小,但是随着时间推移,会使浏览器的内存越来越少,最终肯定会挂掉
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 1let getCurrentTime = ()=>{
2 let date = new Date()
3 let year = date.getFullYear()
4 let month = date.getMonth() + 1
5 let day = date.getDate()
6 let hour = date.getHours()
7 let minute = date.getMinutes()
8 let second = date.getSeconds()
9 let dateArr = [year, month, day, hour, minute, second]
10 dateArr = dateArr.map((v,k)=>{
11 if(k!=0) v=v.toString().padStart(2, '0')
12 return v
13 })
14 return dateArr.join('-')
15}
16
17setInterval(() => {
18 console.log(getCurrentTime())
19}, 1000)
20
21
所以使用享元模式来重改这段代码,只实例化一次Date对象,就不会造成多次实例化,占用大量内存空间了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 1let date = new Date()
2let timestamp = Date.parse(date)
3let getCurrentTime = ()=>{
4 timestamp += 1000
5 date.setTime(timestamp)
6 let year = date.getFullYear()
7 let month = date.getMonth() + 1
8 let day = date.getDate()
9 let hour = date.getHours()
10 let minute = date.getMinutes()
11 let second = date.getSeconds()
12 let dateArr = [year, month, day, hour, minute, second]
13 dateArr = dateArr.map((v,k)=>{
14 if(k!=0) v=v.toString().padStart(2, '0')
15 return v
16 })
17 return dateArr.join('-')
18}
19
20setInterval(() => {
21 console.log(getCurrentTime())
22}, 1000)
23
24
代理模式
在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式
就像我们搬家,以前只能自己搬,掌握了代理模式之后就能交给搬家公司来搬,这样就不用每家每户都是自己来搬家了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 1class Home{
2 constructor(name){
3 this.name = name
4 }
5 move(){
6 console.log('自己搬家')
7 }
8}
9class moveCompany{
10 superMove(user){
11 console.log('专业搬家三十年,接单客户:' + user)
12 }
13}
14
15let myHome = new Home('mySkey')
16let company = new moveCompany()
17myHome.move()
18company.superMove(myHome.name)
19
20
- ajax请求
调用ajax请求的时候,无论是各种开源库,还是自己写的Ajax类, 都会给xhr对象设置一个代理. 我们不可能频繁的去操作xhr对象发请求, 而应该是这样
1
2
3
4
5
6
7 1var request = Ajax.get( 'cgi.xx.com/xxx' );
2request.send();
3request.done(function(){
4
5});
6
7
职责链模式
责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式
- 产品的流程
就举个例子,互联网产品到底经历了什么?首先是一个需求,有需求才是市场嘛!客户用一个功能需求,他便联系了一个互联网公司,提了需求,公司这边报了价,客户同意了报价。由(售前)工作人员和客户签订合同,然后把需求报给(产品经理),产品经理设计出基本交互原型,然后把需求传递给(UI)设计师,然后调整多次开UI评审会,通过后交给(程序员)开发人员开发
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 1let makeProduction = ()=>{
2 salerBefore()
3 productManager()
4 uiDesign()
5 if('ui评审会通过'){
6 programmer()
7 }
8}
9let salerBefore = ()=>console.log('接单签合同')
10let productManager = ()=>console.log('确定需求绘制原型')
11let uiDesign = ()=>console.log('设计优化')
12let programmer = ()=>console.log('写BUG')
13makeProduction('商城')
14
15
命令模式
命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令
- redux的reducer
使用过react和redux的人一定写过reducer,外部不需要了解到底需要操作什么,只根据action的type来执行响应操作
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 1
2let defaultData = [
3 { audios: [], page: {} }, // 推荐
4 { audios: [], page: {} }, // 最热
5 { audios: [], page: {} }, // 原创
6 { audios: [], page: {} }, // 飙升
7 { audios: [], page: {} }, // 最新
8]
9let musics = (state, action) => {
10 if (typeof state === 'undefined') {
11 return defaultData
12 }
13 switch (action.type) {
14 case 'setMusics':
15 return action.musics
16
17 case 'saveMusics':
18 let saveMusics = [].concat(state)
19 saveMusics[action.musics.type] = {
20 audios: saveMusics[action.musics.type].audios.concat(action.musics.audios),
21 page: action.musics.page
22 }
23 return saveMusics
24
25 default:
26 return state
27 }
28}
29
30export { musics }
31
32
- npm和yarn这种包管理工具
包管理工具也是通过命令来区分操作
解释器模式
解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等
每门语言都有解释器的,就像宪法规定什么行为合法,什么不合法一样!
迭代器模式
迭代器模式(Iterator Pattern),这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示
-
1、数组迭代器
1
2
3
4
5
6
7
8 1let forEach = (arr, cb)=>{
2 for(let i=0;i<arr.length;i++){
3 cb(arr[i], i)
4 }
5}
6forEach(['hi','he','she'],(v,i)=>console.log(v,i))
7
8
-
2、对象迭代器
1
2
3
4
5
6
7
8 1let forEach = (obj, cb)=>{
2 for(let i in obj){
3 cb(obj[i], i)
4 }
5}
6forEach({ name: 'mySkey', age: '23' },(v,i)=>console.log(v,i))
7
8
中介者模式
中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。中介者模式属于行为型模式
就像房屋中介,房主将自己房子挂在他们那里,然后你去找中介的时候,他们就把带你去看他们手上的房源;就像淘宝网,淘宝商家发布了商品,你发起订单,付款到淘宝,商家发货,你确认收货后,淘宝将钱打入商家账户
备忘录模式
备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。
-
1、vue中的keep-alive就能缓存数据
-
2、小程序能缓存数据
-
3、无限下拉刷新
1
2
3
4
5
6
7
8
9
10 1let cache = {}
2onReachBottom(){
3 page += 1 // 页码加 1
4 ajax.get('/list', { page }).then(res=>{
5 cache[page] = res.data.list // 将每页数据缓存起来,减少服务器开销
6 this.list = this.list.concat(res.data.list)
7 })
8}
9
10
状态模式
在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式
还是拿分页来举个例子,如果要更换下查询条件,但是现在分页的页码已经大于1了,但是我们明显需要传入的页码是1,那么就可以改变一下获取数据的函数
1
2
3
4
5
6
7
8
9
10 1let getList = (clear = false)=>{
2 if(clear){ // 需要重置页码的地方调用时传入个 true
3 page = 1
4 }
5 ajax.get('/list', { page }).then(res=>{
6 this.list = res.data.list
7 })
8}
9
10
策略模式
在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法
其实就是定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换
-
就像验证提交数据的正确性,可以将校验规则单独封装起来,集中管理,之后判断返回的是true还是false
1
2
3
4
5
6
7
8
9
10
11
12 1export default {
2 email(email){
3 let reg = /^\w+@[a-zA-Z0-9]{2,10}(?:\.[a-z]{2,4}){1,3}$/
4 return reg.test(email)
5 },
6 phone(phone){
7 let reg = /^((0\d{2,3}-\d{7,8})|(1[3584]\d{9}))$/
8 return reg.test(phone)
9 }
10}
11
12
然后使用时就能需要的地方引入来校验
1
2
3
4 1import valid from '/valid.js'
2console.log(valid.phone('12345678912'))
3
4
模板模式
在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式
就像我们的组件一样,哪里需要哪里搬,我们就是搬砖的,其实就是相当于把代码copy了一份过来
-
各种游戏都有初始化,开始,结束流程
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
33 1class Game{
2 //初始化游戏
3 initialize(){}
4
5 //开始游戏
6 startPlay(){}
7
8 //结束游戏
9 endPlay(){}
10
11 play(){
12 this.initialize();
13 this.startPlay();
14 this.endPlay();
15 }
16}
17
18class Chess extends Game{
19 initialize(){
20 console.log('初始化棋牌游戏')
21 }
22 startPlay(){
23 console.log('开始棋牌游戏')
24 }
25 endPlay(){
26 console.log('结束棋牌游戏')
27 }
28}
29
30var game = new Chess()
31game.play()
32
33
访问者模式
在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作
-
就像一个函数是相同的,但是调用的对象不一样导致结果不同
1
2
3
4
5
6
7
8
9
10
11 1function showName(){
2 console.log(this.name)
3}
4window.name = 'hello world'
5let mySkey = { name: 'mySkey' }
6let xiaoming = { name: '小明' }
7showName()
8showName.call(mySkey)
9showName.call(xiaoming)
10
11
组合模式
组合模式又叫部分-整体模式,它将所有对象组合成树形结构。使得用户只需要操作最上层的接口,就可以对所有成员做相同的操作
-
经常我们需要隐藏整个板块,那么隐藏父级容器就能达到效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <title>combination</title>
6</head>
7<body>
8 <ul id="container">
9 <li></li>
10 <li></li>
11 <li></li>
12 </ul>
13<script type="text/javascript">
14var dom = document.getElementById('container')
15dom.style.display = 'none'
16</script>
17</body>
18</html>
19
20
- 文件夹,我们删除文件夹也会删除下面的文件夹及文件,给文件夹添加权限也会影响到下面的
mixin模式
mixin模式就是一些提供能够被一个或者一组子类简单继承功能的类,意在重用其功能。在面向对象的语言中,我们会通过接口继承的方式来实现功能的复用。但是在javascript中,我们没办法通过接口继承的方式,但是我们可以通过javascript特有的原型链属性,将功能引用复制到原型链上,达到功能的注入。
其实就是一个包括了混入对象所有的属性方法的超集,最简单的一个例子:
1
2
3
4
5
6 1let a = { a: 'A' }
2let b = { b: 'B' }
3let c = { ...a, ...b }
4console.log(c)
5
6
一个比较健壮的mixin模式
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
33 1function extend(destClass) {
2 var classes = Array.prototype.slice.call(arguments, 1);
3 for (var i=0; i<classes.length; i++) {
4 var srcClass = classes[i];
5 var srcProto = srcClass.prototype;
6 var destProto = destClass.prototype;
7 for (var method in srcProto) {
8 if (!destProto[method]) {
9 destProto[method] = srcProto[method];
10 }
11 }
12 }
13}
14function Book() {}
15Book.prototype.getName = function() {
16 console.log('Book getName')
17}
18Book.prototype.setName = function() {
19 console.log('Book setName')
20}
21
22function Tech(){}
23Tech.prototype.showTech = function(){
24 console.log('Tech showTech')
25}
26
27function JS() {}
28
29extend(JS, Book, Tech);
30var js = new JS();
31js.getName()
32
33
MVC模式
MVC 是一种使用 MVC(Model View Controller 模型-视图-控制器)设计创建 Web 应用程序的模式:
- Model(模型)
表示应用程序核心(比如数据库记录列表),是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据
- View(视图)
显示数据(数据库记录),是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的
- Controller(控制器)
处理输入(写入数据库记录),是应用程序中处理用户交互的部分。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据
- MVC优点:
1、耦合性低,视图层和业务层分离,这样就允许更改视图层代码而不用重新编译模型和控制器代码。
2、重用性高
3、生命周期成本低
4、MVC使开发和维护用户接口的技术含量降低
5、可维护性高,分离视图层和业务逻辑层也使得WEB应用更易于维护和修改
6、部署快
- MVC缺点:
1、不适合小型,中等规模的应用程序,花费大量时间将MVC应用到规模并不是很大的应用程序通常会得不偿失。
2、视图与控制器间过于紧密连接,视图与控制器是相互分离,但却是联系紧密的部件,视图没有控制器的存在,其应用是很有限的,反之亦然,这样就妨碍了他们的独立重用。
3、视图对模型数据的低效率访问,依据模型操作接口的不同,视图可能需要多次调用才能获得足够的显示数据。对未变化数据的不必要的频繁访问,也将损害操作性能
- 应用:
在web app 流行之初, MVC 就应用在了java(struts2)和C#(ASP.NET)服务端应用中,后来在客户端应用程序中,基于MVC模式,AngularJS应运而生
MVP模式
- Model(模型)
表示应用程序核心(比如数据库记录列表),是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据
- View(视图)
显示数据(数据库记录),是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的
- Presenter (发布者)
在View和Model之间起到桥梁的作用,又是一个独立的大模块,封装了业务的复杂度,将UI和业务逻辑拆分开来,使UI和业务都可以独立的进行变化。从整体的数据流向上看,Presenter从Model层获取数据,并通过接口发送给View层展示;View层将用户交互传递给Presenter,由Presenter完成相应的业务逻辑,这其中可能会有Model层的参与,比如Presenter调用Model层的接口来保存数据。
- MVP优点:
1、模型与视图完全分离,我们可以修改视图而不影响模型
2、可以更高效地使用模型,因为所有的交互都发生在一个地方——Presenter内部
3、我们可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑。这个特性非常的有用,因为视图的变化总是比模型的变化频繁
4、如果我们把逻辑放在Presenter中,那么我们就可以脱离用户接口来测试这些逻辑(单元测试)
- MVP缺点:
视图和Presenter的交互会过于频繁,使得他们的联系过于紧密。也就是说,一旦视图变更了,presenter也要变更
- MVP应用:
可应用与Android开发
MVVM模式
WPF的数据绑定与Presentation Model相结合是非常好的做法,使得开发人员可以将View和逻辑分离出来,但这种数据绑定技术非常简单实用,也是WPF所特有的,所以我们又称之为Model-View-ViewModel(MVVM)
- Model(模型)
表示应用程序核心(比如数据库记录列表),是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据
- View(视图)
显示数据(数据库记录),是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的
- viewModel(视图控制)
View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理
- MVVM优点:
1、低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
2、可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
3、独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成xaml代码。
4、可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写