Javascript几种常见的 设计模式

95 阅读2分钟

1. 单例模式

     生成唯一一个对象,但可以通过改变实参(可有可无,0个或多个)修改,对象的某属性的值
     特点:
      1) 一个构造函数, 一生只能创建一个实例化对象
      2)准备一个变量默认赋值为 null, 然后在第一次实例化的时候 给这变量赋值为实例化对象
      3)后续在调用实例化的时候, 就不再创建实例化对象了, 而是拿到提前准备好的变量    

1)单例模式 基础版

    <script>
        let instance = null
        class Dialog {
            constructor() {
            console.log('在页面中 创建了 一个 弹出层')
            }
        }
        function newDialog() {
            if (instance === null) {
                instance = new Dialog()
            }
            return instance
        }
        // 第一次调用, 创建一个实例化对象然后给到 instance 中, 并返回 instance
        let d1 = newDialog()
        // 第二次以及后续的所有调用, 都是直接返回 instance
        let d2 = newDialog()//在页面中 创建了 一个 弹出层  (只输出一次)
        newDialog()//无输出  单例模式一生只能创建一个实例化对象
        newDialog()//无输出  单例模式一生只能创建一个实例化对象
    </script>

2)单例模式 升级过渡版(不用看)

    <script>
        class Dialog {
            constructor() {console.log('在页面中 创建了 一个 弹出层')}
        }
        //升级1: 原本的 instance 变量为一个全局变量, 本次利用自执行函数与闭包将其修改为了 局部变量
        const newDialog = (function () {
            // 自执行函数内 创建一个变量(局部变量)
            let instance = null
            return function () {
                if (instance === null) {
                    instance = new Dialog()
                }
                return instance
            }
        })()
        const d1 = newDialog()//在页面中 创建了 一个 弹出层
        const d2 = newDialog()//无输出  单例模式一生只能创建一个实例化对象
    </script>

3)单例模式 必会版

    <script>
        const newDialog = (function () {
            let instance = null
            class Dialog {
                constructor() {
                    this.title = ''
                    console.log('在页面中 创建了 一个 弹出层')
                }
                setTitle(newTitle) {
                    this.title = newTitle
                }
            }
            return function (type) {
                if (instance === null) {
                    // 当前分支语句的代码 只会在第一次的时候 执行
                    instance = new Dialog()
                }

                // 此时的位置, 每一次调用 newDialog('警告') 都会执行一次
                instance.setTitle(type)
                return instance
            }
        })()

        const d1 = newDialog('第1次')
        console.log(d1)  //Dialog {title: '第1次'}
        const d2 = newDialog('第2次')// 过了很久之后, 第二次调用instance.setTitle(type)
        console.log(d2) //Dialog {title: '第2次'}
        console.log(d1===d2)//true 同一个对象,但可以通过变量type修改,对象的某属性的值
    </script>
等价于:newDialog相当于一个函数名
        const newDialog=function (type) {
        if (instance === null) {
            instance = new Dialog()
        }
        instance.setTitle(type)
        return instance

2. 策略模式

 为了解决过多的 if...else 的嵌套问题

 策略模式的核心(当前案例)
         创建一个数据结构, 这个结构内存储着各种折扣记录, 对应的值 是这个折扣的计算方式
     {
         '8折': 商品总价 * 80%,
         '7折': 商品总价 * 70%,
         '5折': 商品总价 * 50%,
         '9折': 商品总价 * 90%,
     }
 

1) 基础版

        if ('8折') {
            console.log('商品总价的 * 80%')
        } else if ('85折') {
            console.log('商品总价的 * 85%')
        } else if ('95折') {
            console.log('商品总价的 * 95%')
        } else if ('9折') {
            console.log('商品总价的 * 90%')
        }

2) 策略模式 过渡版(不用看)

        const calcPrice = (function () {
        // 当前对象内存储着各种折扣, 以及对应的 打折后商品总价的计算方式
        const calcList = {
            '80%': (total) => { return (total * 0.8).toFixed(1) },
            '70%': (total) => { return (total * 0.7).toFixed(1) },
        }

        function inner(type, total) {
            /**
             *  type: 表明当前几折
             *  total: 打折前的商品总价
             *  调用当前函数是想得到 打折后的商品总价, 所以我们需要书写一个 return
            */
            return calcList[type](total)
        }

        return inner
    })()

    console.log(calcPrice('80%', 500))
    console.log(calcPrice('80%', 1000))
    console.log(calcPrice('80%', 876.3))
    

策略模式 必会版

    <script>
        const calcPrice = (function () {
            const calcList = {
                '80%': (total) => { return (total * 0.8).toFixed(1) },
                '70%': (total) => { return (total * 0.7).toFixed(1) },
                '60%': total => (total * 0.6).toFixed(1),
            }

            /**
             *  所有的引用数据类型, 都可以当成一个对象来使用
            */
            function inner(type, total) {
                return calcList[type](total)
            }

            inner.add = function (type, fn) {//给inner增加add方法
                calcList[type] = fn
            }

            inner.sub = function (type) {//给inner增加sub方法
                delete calcList[type]
            }

            inner.getList = function () {
                return calcList
            }
            return inner
        })()

        console.log(calcPrice('80%', 500))
        console.log(calcPrice('80%', 1000))
        console.log(calcPrice('80%', 876.3))

        calcPrice.add('50%', total => (total * 0.5).toFixed(1))//增加50%折扣
        calcPrice.add('88%', total => (total * 0.88).toFixed(1))//增加88%折扣

        calcPrice.sub('60%')//删除60%折扣

        console.log(calcPrice.getList())
    </script>