JS数组的扁平化和设计模式

35 阅读4分钟

数组扁平化

  • 就是将一个 "多维数组" 转化成一个 "一维数组"
  1. 面试版
const arr = [1,2,3,4,[5,6,7,8,[9,10,11,12]]]
function flat(origin) {
    //1.创建一个空数组,用来接收扁平化以后的元素内容
    const newArr = []
    
    //2.利用递归函数,实现数组的扁平化
    function fn(fnOrigin) {
        fnOrigin.forEach((item) => {
            if(Object.property.toString.call(item) === '[object Array]'){
                //说明当前 item 是一个数组,所以此时需要递归调用 fn 函数
                fn(item)
            } else {
                //如果当前分支执行,说明 item 一定不是一个数组
                newArr.push(item)
            }
        })
    }
    fn(origin)
    
    //3.把处理好的数组(newArr) 返回出去
    return newArr
}
const flatArr = flat(arr)
console.log(flatArr) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
  1. 工作版
    console.log(arr) //[1, 2, 3, 4, Array(5)]
    console.log(arr.flat(1)) // [1, 2, 3, 4, 5, 6, 7, 8, Array(4)]
    console.log(arr.flat(2)) //  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
    console.log(arr.flat(Infinity)) //  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

设计模式

  • 为了解决 某一类问题 的一个优化过的代码解决方案

单例模式

  • 一个构造函数,一生只能创建一个实例化对象
  • 准备一个变量默认赋值为 null,然后在第一次实例化的时候,给这个变量赋值为实例化对象
  • 后续在调用实例化的时候,就不再创建实例化对象了,而是拿到提前准备好的变量
    class Dialog {
      constructor () {
        console.log('在页面中创建了一个弹出层')
      }
    }

    // let instance = null
    // function newDialog() {
    //   if (instance === null) {
    //     instance = new Dialog()
    //   }
    //   return instance
    // }
    
    //升级1:原本的 instance 变量为一个全局变量,本次利用自执行函数与闭包将其修改为了 局部变量
    const newDialog = (function () {
        //在自执行函数内创建一个变量(局部变量)
        let instance = null
        
        return function () {
            if(instance === null) {
                instance = new Dialog()
            }
            
            return instance
        }
    })()

    const d1 = newDialog()
    const d2 = newDialog()

单例模式升级

const Dialog = (function () {
    let instance = null
    
    class Dialog {
        constructor(title) {
            this.title = ''
            console.log('在页面中创建了一个弹出层')
        }
        setTitle(newTitle) {
            this.title = newTitle
        }
    }
    
    return function (type) {
        if(instance === null){
            instance = new Dialog()
        }
        
        instance.setTitle(type)
        return instance
    }
})()

    const d1 = new Dialog('警告')
    console.log(d1)

    const d2 = Dialog('通用')
    console.log(d2)

策略模式

  • 为了解决过多的 if... else 的嵌套问题
  • 核心:
    • 创建一个数据结构,这个结构内存储着各种折扣记录,对应的值 是这个折扣的计算方式
    const calcPrice = (function () {
        //当前对象内存储着各种折扣,以及对应的 打折后的商品总价的计算方式
        const calcList = {
          '80%': (total) => {return (total * 0.8).toFixed(1)},
          '70%': (total) => {return (total * 0.7).toFixed(1)},
          // type: (total) => {return (total * type).toFixed(1)}
        }

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

          return calcList[type](total)

        }
        return inner
      })()

      console.log(calcPrice('80%',500));
      console.log(calcPrice('80%',562.2));
  • 策略模式升级
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) {
        calcList[type] = fn
        
        console.log(calcList)
    }
    
    inner.sub = function (type) {
        delete calcList[type]
        
        console.log(calcList)
    }
    
    inner.getList = function() {
        return calcList
    }
    
    return inner
})()

console.log(calcPrice('80%',500));
console.log(calcPrice('80%',562.2));
calcPrice.add('50%',total => (total * 0.5).toFixed(1))
calcPrice.add('88%',total => (total * 0.88).toFixed(1))
calcPrice.sub('88%')

发布订阅模式

  • 一般用来处理 大型项目(框架)的封装
  • 模拟发布订阅模式场景:
    1. 你去到书店,问店员 有没有一本书叫做 <JS 从入门到精通>
    2. 店员: 我们现在没有,然后你留下一个联系方式,将来有了之后我电话通知你
    3. 过了一段时间,有书本了,店员通过电话联系到你,触发了你的技能,然后你买了
class observer {
    constructor (name) {
        this.name = name // 模拟店员的名字,但是不重要
        this.msgList = {} //模拟店员的记录手册
    }
    
    //购买不到书本,留下预约方式的时候执行
    add (type,fn) {
        //通过这个函数可以向 this.msgList 内添加内容,后续方便维护
        if(this.msgList[type] === undefined) {
            //当前分支执行,表明这个 type 是第一次出现
            this.msgList[type] = [] // 在第一次出现的时候,给它赋值一个空数组,后续的内容直接 push 到数组内就可以了
        }
        this.msgList[type].push(fn)
    }
    
    //一段时间后,书本到了,通知对应的预约者,然后触发他们的技能
    tri (type) {
        this.msgList[type].forEach(item => item()) // 通过 传入的 type 找到对应的属性值, 然后遍历调用内部的所有函数
    }
    
    //调用这个方法,取消预约
    delName(type,fn) {
        //到对应的数组,将你的联系方式 删掉
        this.msgList[type] = this.msgList[type].filter(item => item !== fn)
    }
}

const o1 = new observer('小刚')
console.log(o1)

//书名
const bookName1 = 'JS从入门导入图'
const bookName2 = '颈椎病预防'


//预约者的技能(函数)
const fnA = () => console.log('我是锅锅,我想买这本书')
const fnB = () => console.log('我是张三,我想买这本书')
const fnC = () => console.log('我是李四,我想买这本书')

//新增预约者
o1.add(bookName1,fnA)
o1.add(bookName1,fnB)
o1.add(bookName1,fnC)

//中间有一位预约者,取消了预约
o1.delName(bookName1,fnB)

// 过了一段时间, 书本到了, 通知对应的预约者前来购买
o1.tri(bookName1)

console.log('有预约者后的书店', o1)