前端设计模式——有哪些怎么用

梦里梦外; 2023-03-01 12:49 59阅读 0赞

文章目录

    • 程序是什么
    • 实现需求思考步骤
    • 创建型设计模式-创建对象
      • 工厂模式
      • 单例模式
      • 建造者模式
      • 构造函数模式
      • 混合模式
    • 模块间的沟通
      • 观察者模式(订阅发布模式)
      • 职责链模式
    • 结构型设计模式
      • 代理模式
    • 性能优化(质量调优)
      • 策略模式
      • 享元模式
      • 模块模式

程序是什么

程序 = 模块与模块之间的沟通

实现需求思考步骤

  1. 功能的主体对象如何创建
  2. 脱离代码,抽象思考,实现功能需要哪几步
  3. 回到代码,思考实现这几步,需要什么模块
  4. 组织模块沟通(选择哪种模块间的沟通模式)
  5. 实现模块(创建模块用哪种设计模式)
  6. 性能质量调优(可不可以用到哪种模式或方法,使得代码质量更好,更易维护和扩展)

创建型设计模式-创建对象

工厂模式

大量创建实例,为了不暴露创建对象的具体逻辑,将逻辑封装在一个函数中,这个函数就称为一个工厂。
实例:jquery中的$创建对象,vue
实例代码:

  1. //安全模式创建的工厂方法函数
  2. let UserFactory = function(role) {
  3. if(this instanceof UserFactory) { //确保必须当前类的实例,否则重新创建一个类实例,防止变成window调用
  4. var s = new this[role]();
  5. return s;
  6. } else {
  7. return new UserFactory(role);
  8. }
  9. }
  10. //工厂方法函数的原型中设置所有对象的构造函数
  11. UserFactory.prototype = {
  12. SuperAdmin: function() {
  13. this.name = "超级管理员",
  14. this.viewPage = ['首页', '通讯录', '发现页', '应用数据', '权限管理']
  15. },
  16. Admin: function() {
  17. this.name = "管理员",
  18. this.viewPage = ['首页', '通讯录', '发现页', '应用数据']
  19. },
  20. NormalUser: function() {
  21. this.name = '普通用户',
  22. this.viewPage = ['首页', '通讯录', '发现页']
  23. }
  24. }
  25. //调用
  26. let superAdmin = UserFactory('SuperAdmin');
  27. let admin = UserFactory('Admin')
  28. let normalUser = UserFactory('NormalUser')

单例模式

确保一个类全局只有一个实例化对象,一般用于全局缓存,采用闭包的方式实现。
实例:vue-router和vuex
实例模型:

  1. var Single = (function(){
  2. var instance;
  3. function Construct(){
  4. //创建实例的构造代码块
  5. }
  6. return {
  7. getInstance: function(){
  8. if(!instance){ //确保全局只有一个
  9. instance = new Construct();
  10. }
  11. return instance;
  12. }
  13. }
  14. })()

建造者模式

将一个复杂的对象分解成多个简单的对象来进行构建,将复杂的构建层与表示层分离,使得相同的构建过程可以创建不同的表示的模式。
用于精细化(复杂)的构建一个对象或类
vue2.0,类由很多子类构建而成的
最终暴露出去的是接口、类
适用范围:比较适用与那些有固定生成顺序的对象,或者对象内部有复杂结构的情况
与工厂模式的区别:

工厂模式根据需求的不同,返回不同类的对象,比较灵活
建造者模式返回的是内在逻辑复杂的封装好的对象

设计流程如下:

  1. 客户提出产品需求
  2. 指挥者根据产品需求,安排建造者完成需求的各个部分
  3. 建造者完成相应的部分

修建房子实例:
产品需求:修一个房子,房子里需要有卧室,厨房,客厅
指挥者设计设计图,要求工人施工
工人修建房子

  1. //产品类:产品要素
  2. class House{
  3. constructor(){
  4. this.need = ['卧室', '厨房', '客厅'];
  5. }
  6. }
  7. //设计图绘制
  8. class Diagram {
  9. constructor() {
  10. console.log('拿到图纸')
  11. }
  12. build(partName) {
  13. console.log(`观察${ partName}图纸`);
  14. }
  15. }
  16. //工人实现设计图
  17. class CreatDiagram extends Diagram {
  18. constructor() {
  19. super();
  20. }
  21. build(partName) {
  22. super.build(partName);
  23. this.worker = new worker(partName);
  24. }
  25. getResult() {
  26. console.log('完工');
  27. return this.worker;
  28. }
  29. }
  30. //工人类:内置施工方法
  31. class worker {
  32. constructor(material) {
  33. this.data = material;
  34. this.make();
  35. }
  36. make(){
  37. console.log(`我开始修建${ this.data}`)
  38. }
  39. }
  40. //指挥官类
  41. class Developer {
  42. construct() {
  43. let house= new House();
  44. let workerOk=house.need.map(el=>{
  45. let builder = new CreatDiagram();
  46. builder.build(el);
  47. return builder.getResult();
  48. })
  49. }
  50. }
  51. // 要求产品
  52. let home = new Developer();
  53. // 生成产品
  54. home.construct();

结果:
在这里插入图片描述
优点:

  1. 低耦合性,复杂的流程简单化,各个类各司其职
  2. 可扩展性,以后新增需求,只需要在对应类上处理即可,不需要修改其他代码

缺点:

  • 内在逻辑越复杂的需求,需要创建的类也越多,会显得代码较为臃肿

构造函数模式

通过构造函数的形式定义类,通过new新增实例
实例:

  1. function Person(name,age){
  2. this.name = name;
  3. this.age = age;
  4. }
  5. Person.prototype = {
  6. constructor: Person;
  7. printName: function(){
  8. console.log(this.name);
  9. },
  10. printAge: function(){
  11. console.log(this.age);
  12. }
  13. }
  14. var person = new Person('star', 22);

混合模式

将构造函数的引用属性和方法放到其原型上,子类是父类原型的一个实例。
实例:

  1. function Person(name,age){
  2. this.name = name;
  3. this.age = age;
  4. };
  5. Person.prototype.printName = function(){
  6. console.log(this.name);
  7. }
  8. function Student(name,age){
  9. //继承 Person 的属性
  10. Person.call(this,name,age);
  11. }
  12. function create(prototype){
  13. function F(){ };
  14. F.prototype = prototype;
  15. return new F();
  16. }
  17. //让Student的原型指向一个对象,该对象的原型指向了Person.prototype,通过这种方式继承 Person 的方法
  18. Student.prototype = create(Person.prototype);
  19. Student.prototype.printAge = function(){
  20. console.log(this.age);
  21. }
  22. var student = new Student('star',22);
  23. student.printName();//star

模块间的沟通

观察者模式(订阅发布模式)

一个订阅者订阅发布者,当一个特定的事件发生的时候,发布者会通知(调用)所有的订阅者。对象之间为一对多的依赖关系。
实例:事件监听(onclick)、vue中的数据驱动等
实例模型:

  1. var EventCenter = (function(){
  2. //将所有的"发布-订阅"关系放到events中
  3. var events = { };
  4. //给事件绑定事件处理程序
  5. //event:事件名
  6. //handler:事件处理程序
  7. function on(event, handler){
  8. events[event] = events[event] || [];
  9. events[event].push({
  10. handler: handler
  11. });
  12. }
  13. //发布消息(触发事件),并执行相应的事件处理程序
  14. //event:事件名
  15. //args:给事件处理程序传递的参数
  16. function fire(event, args){
  17. if (!events[event]) { return}
  18. //遍历事件处理程序列表,执行其中每一个事件处理程序
  19. for (var i = 0; i < events[event].length; i++) {
  20. events[event][i].handler(args);
  21. }
  22. }
  23. //移除
  24. function off(event){
  25. delete events[event];
  26. }
  27. //使用模块模式的方式,向外界提供绑定事件处理程序和触发事件的接口
  28. return {
  29. on: on,
  30. fire: fire,
  31. off: off
  32. }
  33. })();
  34. Event.on('change', function(val){
  35. console.log('change... now val is ' + val);
  36. });
  37. Event.on('click', function(val){
  38. console.log('click.... now val is '+ val);
  39. })
  40. Event.fire('change', 'aaa');
  41. Event.fire('click', 'aaa');
  42. Event.off('change');

职责链模式

让功能的完成以消息按链条传递来处理(线性处理,同步模块)
基本架子

  1. var arr = [同步模块]
  2. var result;
  3. async function run(){
  4. while(arr.length>0){
  5. result = await arr.shift()(result)
  6. }
  7. }
  8. run.then(res=>{
  9. })

结构型设计模式

代理模式

为其他对象提供一种代理,也就是当其他对象直接访问该对象时,如果开销较大,就可以通过这个代理层控制对该对象的访问。
使用场景:懒加载,合并http请求和缓存等
实例1:图片懒加载

  1. // 创建一个本体对象
  2. var myImage = (function(){
  3. // 创建标签
  4. var imgNode = document.createElement( 'img' );
  5. // 添加到页面
  6. document.body.appendChild( imgNode );
  7. return {
  8. // 设置图片的src
  9. setSrc: function( src ){
  10. // 更改src
  11. imgNode.src = src;
  12. }
  13. }
  14. })();
  15. // 创建代理对象
  16. var proxyImage = (function(){
  17. // 创建一个新的img标签
  18. var img = new Image;
  19. // img 加载完成事件
  20. img.onload = function(){
  21. // 调用 myImage 替换src方法
  22. myImage.setSrc( this.src );
  23. }
  24. return {
  25. // 代理设置地址
  26. setSrc: function( src ){
  27. // 预加载 loading
  28. myImage.setSrc( 'file:// /C:/Users/svenzeng/Desktop/loading.gif' );
  29. // 赋值正常图片地址
  30. img.src = src;
  31. }
  32. }
  33. })();
  34. proxyImage.setSrc( 'http:// image.qq.com/music/photo/k/000GGDys0yA0Nk.jpg' );

实例2:请求缓存

  1. var proxyAjax = (
  2. function () {
  3. var caches = { };
  4. return function (param, callback) {
  5. if (caches[param]) { //缓存中有直接返回,不再发送请求
  6. callback(caches[param]);
  7. return;
  8. }
  9. // 模拟异步请求
  10. sendData(param, function (data) {
  11. caches[param] = data;
  12. callback(data);
  13. });
  14. }
  15. }
  16. )();
  17. function sendData(param, callback) {
  18. //模拟请求返回
  19. setTimeout(function () {
  20. callback(param + Math.random());
  21. }, 1000);
  22. }
  23. //发起请求
  24. proxyAjax(param,function(){
  25. })

性能优化(质量调优)

策略模式

定义一系列算法,将他们封装起来,让算法的使用和算法的实现分离开来,避免多重判断调用哪些算法。
使用场景:适用于有多个判断分支的场景,如较多的if、else,解决表单验证的问题
一个基于策略模式的程序至少由两部分组成:一组策略和一个调用环境
实例1:发放奖金,绩效得A的员工,将拿到原本工资3倍的钱;绩效得B的员工,将拿到原本工资2倍的钱;绩效得C的员工,将拿到原本工资*1.5倍的钱

  1. //一组策略
  2. var strategies = {
  3. "A": function(original) {
  4. return original*3
  5. },
  6. "B": function(original) {
  7. return original*2
  8. },
  9. "C": function(original) {
  10. return original*1.5
  11. }
  12. }
  13. //调用环境
  14. function calcSalary(original, grade) {
  15. return strategies[grade](original)
  16. }
  17. calcSalary(2000,'A') // 6000
  18. calcSalary(2000,'B') // 4000
  19. calcSalary(2000,'C') // 3000

实例2:表单验证,用户名不能为空,密码长度要大于6位,当提交表单的时候对表单做验证

  1. //一组策略
  2. var strategies = {
  3. isNotEmpty: function(value, msg) {
  4. if(value === '') {
  5. return msg
  6. }
  7. },
  8. minLength: function(value, length, msg) {
  9. if(value.length < length) {
  10. return msg
  11. }
  12. }
  13. }
  14. //调用环境封装
  15. var Validator = function(){
  16. this.cache = []
  17. };
  18. Validator.prototype.add = function(dom, rule, msg) {
  19. var ruleKey = [];
  20. var ruleVal = ''
  21. if(rule.indexOf(':')!=-1) {
  22. ruleKey = rule.split(':')[0];
  23. ruleVal = rule.split(':')[1];
  24. } else {
  25. ruleKey = rule;
  26. }
  27. this.cache.push(function(){
  28. var temp=[]
  29. temp.push(dom.value)
  30. if(ruleVal) {
  31. temp.push(ruleVal)
  32. }
  33. temp.push(msg)
  34. return strategies[ruleKey].apply(dom, temp);
  35. })
  36. }
  37. //不用区分具体的表单类型,总是会返回同样的结果——一个没有通过验证的列表和错误信息
  38. Validator.prototype.validate = function() {
  39. var count = 0
  40. this.cache.forEach(function(item){
  41. var msg = item()
  42. if(msg) { //存在错误
  43. count++
  44. alert(msg)
  45. }
  46. })
  47. if(count > 0) {
  48. return false
  49. } else {
  50. return true
  51. }
  52. };
  53. //创建一个Validator实例,在表单提交事件中调用validate方法
  54. var test = new Validator()
  55. test.add(registerForm.username, 'isNotEmpty', '用户名不能为空');
  56. test.add(registerForm.password, 'minLength: 6','密码至少6位');
  57. registerForm.onsubmit = function() {
  58. if(!test.validate()) {
  59. return false;
  60. }
  61. }

享元模式

对数据、方法共享分离,减少重复对象创建,降低内存消耗。
享元模式要求将对象的属性划分为内部状态与外部状态(状态在这里通常指属性)
内部与外部状态的划分

  1. 内部状态存储于对象内部
  2. 内部状态可以被一些对象共享
  3. 内部状态独立于具体的场景,通常不会改变
  4. 外部状态取决于具体的场景,并根据场景而变化,外部状态不能被共享

有多少种内部状态的组合,系统中便最多存在多少个共享对象,外部状态存储于共享对象外部。在需要用到的时候,外部状态传入与共享对象组成一个完整的对象。
适用场景:

  1. 一个程序中使用了大量的相似对象
  2. 由于使用了大量对象,造成很大的内存开销
  3. 对象的大多数状态都可以变为外部状态
  4. 剥离出对象的外部状态之后,可以用相对较少的共享对象取代大量对象

实例:
多个文件上传(转载:https://www.cnblogs.com/xiaohuochai/p/8039957.html)
区分内外部状态
内部状态:上传方式
外部状态:文件名称、文件大小

  1. var Upload = function( uploadType){
  2. this.uploadType = uploadType;
  3. };
  4. //文件删除处理
  5. Upload.prototype.delFile = function( id ){
  6. uploadManager.setExternalState( id, this );
  7. if ( this.fileSize < 3000 ){
  8. return this.dom.parentNode.removeChild( this.dom );
  9. }
  10. if ( window.confirm( '确定要删除该文件吗? ' + this.fileName ) ){
  11. return this.dom.parentNode.removeChild( this.dom );
  12. }
  13. }
  14. //创建upload对象
  15. var UploadFactory = (function(){
  16. var createdFlyWeightObjs = { };
  17. return {
  18. create: function( uploadType){
  19. //如果某种内部状态对应的共享对象已经被创建过,那么直接返回这个对象,否则创建一个新的对象
  20. if ( createdFlyWeightObjs [ uploadType] ){
  21. return createdFlyWeightObjs [ uploadType];
  22. }
  23. return createdFlyWeightObjs [ uploadType] = new Upload( uploadType);
  24. }
  25. }
  26. })();
  27. //创建uploadManager对象,它负责向UploadFactory提交创建对象的请求,并用一个uploadDatabase对象保存所有upload对象的外部状态,以便在程序运行过程中给upload共享对象设置外部状态
  28. var uploadManager = (function(){
  29. var uploadDatabase = { };
  30. return {
  31. add: function( id, uploadType, fileName, fileSize ){
  32. //仅创建了2个对象,即两个内部状态共享对象
  33. var flyWeightObj = UploadFactory.create( uploadType );
  34. var dom = document.createElement( 'div' );
  35. dom.innerHTML =
  36. '<span>文件名称:'+ fileName +', 文件大小: '+ fileSize +'</span>' +
  37. '<button class="delFile">删除</button>';
  38. dom.querySelector( '.delFile' ).onclick = function(){
  39. flyWeightObj.delFile( id );
  40. }
  41. document.body.appendChild( dom );
  42. uploadDatabase[ id ] = {
  43. fileName: fileName,
  44. fileSize: fileSize,
  45. dom: dom
  46. };
  47. return flyWeightObj ;
  48. },
  49. setExternalState: function( id, flyWeightObj ){
  50. var uploadData = uploadDatabase[ id ];
  51. for ( var i in uploadData ){
  52. flyWeightObj[ i ] = uploadData[ i ];
  53. }
  54. }
  55. }
  56. })();
  57. //触发上传动作的startUpload函数,即内部状态
  58. var id = 0;
  59. window.startUpload = function( uploadType, files ){
  60. for ( var i = 0, file; file = files[ i++ ]; ){
  61. var uploadObj = uploadManager.add( ++id, uploadType, file.fileName, file.fileSize );
  62. }
  63. };
  64. //上传文件,plugin和flash为uploadType
  65. startUpload( 'plugin', [
  66. {
  67. fileName: '1.txt',
  68. fileSize: 1000
  69. },
  70. {
  71. fileName: '2.html',
  72. fileSize: 3000
  73. },
  74. {
  75. fileName: '3.txt',
  76. fileSize: 5000
  77. }
  78. ]);
  79. startUpload( 'flash', [
  80. {
  81. fileName: '4.txt',
  82. fileSize: 1000
  83. },
  84. {
  85. fileName: '5.html',
  86. fileSize: 3000
  87. },
  88. {
  89. fileName: '6.txt',
  90. fileSize: 5000
  91. }
  92. ]);

结果显示:
在这里插入图片描述

模块模式

模块模式可以指定类想暴露的属性和方法,不会污染全局,采用闭包的形式。
实例:

  1. var Person = (function() {
  2. var name = 'xxx'
  3. function sayName() {
  4. console.log(name)
  5. }
  6. return{
  7. name: name,
  8. sayName: sayName
  9. }
  10. })()

发表评论

表情:
评论列表 (有 0 条评论,59人围观)

还没有评论,来说两句吧...

相关阅读

    相关 设计模式哪些?

    设计模式是一种在软件设计中常用的方法,旨在解决常见的设计问题和提高软件质量的一种通用的方法。设计模式主要分为三类:创建型模式、结构型模式和行为型模式。 创建型模式:

    相关 前端技术哪些

    前端(Front-end)指的是用户可见的界面。 网站前端页面也就是网页的页面开发,比如网页上的特效、布局、图片、视频,音频等内容。 前端的工作内容: 将美工设计的效果图