JavaScript面向对象和模块化基础(二)

295 阅读4分钟

主要知识点

  • 单例模式
  • 工厂模式
  • 装饰者模式

设计模式

设计模式是软件开发人员在软件开发过程中面临的一些具有代表性问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

单例模式

单例模式 (Singleton Pattern)又称为单体模式,保证一个类只有一个实例,并提供一个访问它的全局访问点。例如vuex、一个游戏的主程序一个客户端对应一个实例。也就是说,**第二次使用同一个类创建新对象的时候,应该得到与第一次创建的对象完全相同的对象。**只有一个实例,可以节约开销,提高性能。

  • 无论实例化多少次都希望只得到一个相同的实例,而不是多个
  • 写一个对象,这个对象也是一个单例
  • 类层面
    • 只允许实例化一次
    • 类里面判断是否实例化,如果没有就进行实例化,如果有就返回this。这样会防止外部误操作进行多次实力化
import Player from './player.js';

// 主程序游戏管理类;
//不对外暴露 Game类
class Game{
    constructor(){
        this.player = null;
    }
    login(name){
        this.player = new Player(name);
    }
}
//声明局部变量 不对外暴露 Game类防止多次实例化
//局部变量外面访问不到。
let instance = null ;
//传参
export default function (...arg) {
    if (!instance){
        instance = new Game(...arg);
    }
    return instance;
}

  • 优点
    • 只有一个实例,可以节约开销,提高性能
    • 外部误操作进行多次实力化
  • 缺点
    • 不易扩展

工厂模式

工厂模式 (Factory Pattern),封装具体实例创建逻辑和过程,外部只需要根据不同条件返回不同的实例。

不对外暴露构造函数,通过不同类型,返回不同的实例

class Cat{
    constructor() {
        this.name = 'miaomiao';
          }
}
      class Dog{
          constructor() {
              this.name = 'dogdog';
          }
      }

      function Factory(type) {
   switch (type) {
       case 'cat':
           return new Cat();
       case 'dog':
           return new Dog();
      default:
          break;
          }
      }
      let cat = Factory("cat");
      console.log(cat);
      let dog = Factory("dog");
      console.log(dog);

特性:

  • 优点:实现代码复用性,封装良好,抽象逻辑

  • 缺点:增加了代码复杂程度;

装饰者模式

在不改变原有对象的基础之上,将功能附加到对象上。提供了比继承更有弹性的替代方案(扩展原有对象功能)

适用场景
  • 添加需求,不要改动原本复杂的代码
  • 扩展一个类的功能或者给一个类添加附加职责
  • 给一个对象动态的添加功能,或动态撤销功能。

方法一 继承

通过对继承方法的重写,可以扩展新的功能

例如:

		class Cat{
		    constructor() {
		        this.name = 'miaomiao';
            }
            jump(){
                console.log('jump jump...');
			}
		}

		class Mycat extends Cat{
			jump() {
                super.jump();
                console.log('I can jump 2 meters high')
            }
        }

        let mimi = new Mycat();
		mimi.jump();

方法二 装饰者

  • 利用高阶函数

    利用高阶函数,高阶函数以函数作为参数传入,返回一个可执行函数

            class Cat{
                constructor() {
                    this.name = 'miaomiao';
                }
                jump(){
                    console.log('jump jump...');
            	}
            }
    
            function Jump() {
                console.log("i can jump 2 meters");
            }
            //利用高阶函数,高阶函数以函数作为参数传入,返回一个可执行函数
            const Decorator = function Mycat(fn1,fn2) {
    			return function () {
                    fn1();
                    fn2()
                }
            }
    
            let myCat = new Cat();
            let catJump = Decorator(myCat.jump,Jump);
            console.log(catJump);
            catJump();
    
  • 原型链

    利用Function基类的原型池,添加装饰函数。再调用时候,调取原型链的装饰函数。

    这个方法写法会稍微简洁一点

            class Cat{
                constructor() {
                    this.name = 'miaomiao';
                }
                jump(){
                    console.log('jump jump...');
                }
            }
    
            function Jump() {
                console.log("i can jump 2 meters");
            }
    		//利用原型链
            Function.prototype.DecoratorFn = function (fn1) {
                //自身调用执行
                this();
                fn1();
            }
    
            let myCat = new Cat();
    
            myCat.jump.DecoratorFn(Jump);
    

观察者模式(自定义事件)

观察者模式 (Observer Pattern) 定义一个对象与其他对象之间的一种依赖关系,当对象发生某种变化的时候,依赖它的其它对象都会得到更新

  • 延迟执行函数。简单说例如一个英雄基类里面有个初始化函数,但是定义时候你不想让他马上执行,然后就自定义事件,将它保存再一个数组中。等例如用户登录了之后再执行初始化函数,那么就在用户登录时候触发这个执行函数
  • 模块之间的解耦。按照上面的解析。英雄初始化函数可以放在英雄基类里面,而不需要放在用户登录函数中。
  • 结构:
    • 自定义事件绑定addEvent
    • 自定义事件触发triggerEvent
    • 自定义事件移除removeEvent
    function fn1() {
        console.log("fn1...");
    }
    function fn2() {
        console.log("fn2...");
    }

	//事件管理类
    const handle = {};

    //添加事件
	function addEvent(eventName,fn) {
	    //首次没有定义 eventName
		if(typeof handle[eventName] === "undefined"){
            handle[eventName] = [];
		}
        handle[eventName].push(fn);
    }
    //触发对象
	function triggerEvent(eventName,...arg) {
	    //如果该事件名称没有定义直接返回不执行
		if(typeof handle[eventName] === "undefined"){
		    return  console.log("没有该事件");
		}
		handle[eventName].forEach(fn =>{
		    fn(...arg);
		})
    }
    //移除事件
	function removeEvent(eventName,fn) {
        if(typeof handle[eventName] === "undefined"){
            return  console.log("没有该事件");
        }
        handle[eventName].forEach((fnItem,index) =>{
            if(fn == fnItem){
                handle[eventName].splice(index,1)
			}
		})
    }


    addEvent("myEvent",fn1);
    addEvent("myEvent",fn2);
    console.log('>>1',handle);

     triggerEvent("myEvent");
    removeEvent("myEvent",fn2);

    console.log('>>2',handle)

王者案例优化

github_code