您的位置:首页 > Web前端 > JavaScript

JavaScript设计模式读书笔记(三)=> 行为型设计模式

2020-04-05 07:14 423 查看

全系列目录

  1. JavaScript设计模式读书笔记(一)=> 创建型设计模式
  2. JavaScript设计模式读书笔记(二)=> 结构型设计模式
  3. JavaScript设计模式读书笔记(三)=> 行为型设计模式
  4. JavaScript设计模式读书笔记(四)=> 技巧型设计模式
  5. JavaScript设计模式读书笔记(五)=>架构型设计模式,MVC,MVP,MVVM

文章目录

  • 3. 状态模式
  • 4. 策略模式
  • 5. 命令模式
  • 6. 备忘录模式
  • 1. 模板方法模式

    父类定义一组操作骨架,而将一些实现步骤推迟到子类中。

    这节自己没有想到很好用到生产中的例子

    // e.g.
    const Alert = function (data) {
    this.content = data.content;
    this.panel = document.createElement('div');
    this.panel.class = 'alert'
    // xxxxx
    }
    
    Alert.prototype.init = function () {
    // xxxx
    }
    
    const RightAlert = function (data) {
    Alert.call(this, data);
    this.panel.class = 'right-alert alert'
    }
    RightAlert.prototype = Object.create(Alert);

    关于提示框的实现看了下antd的Model实现方法,它是如何实现多种类型的提示框之间代码复用的。

    1. 细粒度模块拆分,例如底部按钮,图标,样式等
    2. 通用提示框提前写好组件默认配置项
    3. 特殊提示框通过1步的细粒度模块拆分可以做到自由定制

    2. 观察者模式

    这个在如今貌似用的也挺多的。其又称作发布-订阅者模式或消息机制,定义了一种依赖关系,解决了主题对象与观察者之间的功能的耦合。

    2.1. 创建一个观察者

    它有一个消息容器和三个方法,分别是订阅消息方法,取消订阅的消息方法,发送订阅的消息方法。

    const Observer = (
    function() {
    const _message = {};
    return {
    // 注册信息
    regist: function(type, fn) {
    if (typeof _message[type] === 'undefined') {
    _message[type] = [fn];
    } else {
    _message[type].push(fn);
    }
    },
    // 发布信息
    fire: function(type, args = {}) {
    if (!_message[type]) return
    const events = {
    type,
    args
    }
    for (let item of _message[type]) {
    item.call(this, events);
    }
    },
    // 移除信息
    remove: function(type, fn) {
    if (Array.isArray(_message[type])) {
    _message[type] = _message[type].filter(item => item !== fn)
    }
    }
    }
    }
    )()
    
    // ==========> 测试
    
    Observer.regist('test', (e) => {
    console.log(e.type, e.args)
    })
    
    Observer.fire('test', 'asdasdasd'); // test, asdasdasd

    2.2. 对象之间解耦

    看一个例子

    const student = function(result) {
    this.result = result;
    this.say = () =>{
    console.log(this.result);
    }
    
    }
    
    student.prototype.answer = function(question) {
    Observer.regist(question, this.say);
    }
    
    student.prototype.sleep = function(question) {
    console.log(`${this.result} ${question} 已被注销`);
    Observer.remove(question, this.say);
    }
    
    const teacher = function() {};
    teacher.prototype.ask = function(question) {
    console.log('问题是 ' + question);
    Observer.fire(question);
    }
    
    const student1 = new student('1号回答问题');
    const student2 = new student('2号回答问题');
    
    student1.answer('快毕业了');
    student1.answer('时间太快了');
    student2.answer('快毕业了');
    student2.answer('时间太快了');
    
    student2.sleep('时间太快了');
    
    const teacher1 = new teacher();
    teacher1.ask('快毕业了');
    teacher1.ask('时间太快了');
    
    // ================> 输出
    > "2号回答问题 时间太快了 已被注销"
    > "问题是 快毕业了"
    > "1号回答问题"
    > "2号回答问题"
    > "问题是 时间太快了"
    > "1号回答问题"

    观察者模式最主要的作用是解决类或者对象之间的耦合,解耦两个相互依赖的对象,使其依赖于观察者的消息机制。

    2.3. ES6 Reflect和Proxy实现观察者模式

    此小节来自: http://es6.ruanyifeng.com/#docs/reflect#实例:使用-Proxy-实现观察者模式

    使用 Proxy 写一个观察者模式的最简单实现,即实现observable和observe这两个函数。思路是observable函数返回一个原始对象的 Proxy 代理,拦截赋值操作,触发充当观察者的各个函数。

    const queuedObservers = new Set();
    
    const observe = fn => queuedObservers.add(fn);
    const observable = obj => new Proxy(obj, {set});
    
    function set(target, key, value, receiver) {
    const result = Reflect.set(target, key, value, receiver);
    queuedObservers.forEach(observer => observer());
    return result;
    }

    上面代码中,先定义了一个Set集合,所有观察者函数都放进这个集合。然后,observable函数返回原始对象的代理,拦截赋值操作。拦截函数set之中,会自动执行所有观察者。

    下面来看一个使用例子:

    const person = observable({
    name: '张三',
    age: 20
    });
    
    function print() {
    console.log(`${person.name}, ${person.age}`)
    }
    
    observe(print);
    person.name = '李四';
    // 输出
    // 李四, 20

    3. 状态模式

    状态模式是解决程序中臃肿的分支判断语句问题,最终的目的是简化分支判断流程,将每个分支转化为一种状态独立出来,方便每种状态的管理又不至于每次执行时遍历所有分支。

    思路:创建一个内部状态变量,然后内部封装好每种动作对应的状态,最后状态对象返回一个接口对象,它可以对内部的状态修改或者调用。

    const State = function () {
    const _current = {};
    const states = {
    jump: function() {
    console.log('jump')
    },
    move: function() {
    console.log('move');
    }
    }
    
    const action = {
    changeState: function(...arg) {
    for (let item of arg) {
    _current[item] = true;
    }
    return this;
    },
    goes: function() {
    console.log('触发了动作');
    for (let prop in _current) {
    states[prop] && states[prop]();
    }
    return this;
    }
    }
    
    return {
    ...action
    }
    }
    
    const a = new State();
    a.changeState('jump', 'move').goes().changeState('jump').goes()

    4. 策略模式

    策略模式从代码上来看和状态模式差不多,逻辑只是稍有不同。状态模式中对状态的控制来决定表现行为,所以状态之间通常是不能相互代替的,否则将产生不同的行为结果;策略模式核心是算法。

    // e.g.
    
    const State = function () {
    const states = {
    phone: function(value) {
    return '判断是否是电话'
    },
    number: function(value) {
    return '判断是否是数字'
    }
    }
    
    const action = {
    check: function(type, value) {
    return states[type] ? states[type](value) : '没有该类型的检测方法';
    },
    add: function(type, fn) {
    states[type] = fn;
    }
    }
    
    return {
    ...action
    }
    }

    5. 命令模式

    命令模式是将执行的命令封装,解决命令的发起者与命令的执行者之间的耦合。每一个命令实际上是一个操作。

    const viewCommond = (function() {
    const action = {
    calculate: (value) => {
    console.log('复杂计算,执行一个操作 ' + value.a);
    }
    }
    function execute(msg) {
    action[msg.command].call(action, msg.param);
    }
    return execute
    })()
    
    viewCommond({
    command: 'calculate',
    param: {
    a: 1
    }
    })

    6. 备忘录模式

    它最主要的任务是对现有数据或者状态做缓存,为将来某一刻的使用或者回复做准备。

    此处类似于现在的记忆化函数

    // =================================》 第一种实现方法 来自《JavaScript权威指南》
    function memoize(f) {
    var cache = {};
    return function(){
    var key = arguments.length + Array.prototype.join.call(arguments, ",");
    if (key in cache) {
    return cache[key]
    }
    else return cache[key] = f.apply(this, arguments)
    }
    }
    
    // =================================》 第一种实现方法 来自memoize-one
    // 代码地址 https://github.com/alexreardon/memoize-one/blob/master/src/index.js
    export function memoize (resultFn) {
    let lastArgs = []; // 用来存放上一次调用的参数
    let lastResult; // 用来缓存上一次的结果
    let calledOnce: boolean = false; // 是否调用过,刚开始的时候是false
    
    // 判断两次调用的时候的参数是否相等
    // 这里的 `isEqual` 是一个抽象函数,用来判断两个值是否相等
    const isNewArgEqualToLast = (newArg, index) => isEqual(newArg, lastArgs[index]);
    
    // 如果上一次的参数和这一次一样,直接返回上一次的结果
    const result = function (...newArgs) {
    if (
    calledOnce &&
    newArgs.length === lastArgs.length &&
    newArgs.every(isNewArgEqualToLast)
    ) {
    // 如果和上次的参数一致, 直接返回缓存的值
    return lastResult;
    }
    
    // 如果和上一次的参数不一致,我们需要再次调用原来的函数
    calledOnce = true; // 标记为调用过
    lastArgs = newArgs; // 重新缓存参数
    lastResult = resultFn.apply(this, newArgs); //重新缓存返回值
    
    return lastResult;
    }
    
    // 返回闭包函数
    return result;
    }

    参考资料:

    1. 张容铭著 javascript设计模式 108-182页
    2. JavaScript权威指南
    3. https://github.com/alexreardon/memoize-one/blob/master/src/index.js
    4. https://zhuanlan.zhihu.com/p/37913276
    • 点赞
    • 收藏
    • 分享
    • 文章举报
    刘翾 博客专家 发布了167 篇原创文章 · 获赞 275 · 访问量 59万+ 他的留言板 关注
    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: