前端零碎知识点---有点乱



/**
 * @description 将数组平铺到指定的深度
 */
const flatten = (arr, depth = 1) => 
    depth != 1 ? arr.reduce((a, v) => a.concat( Array.isArray(v) ? flatten(v, depth - 1) : v), [])
    : arr.reduce((a, v) => a.concat(v), [])

var flat = (arr) => {
    return arr.flat(Infinity)
}
 /**
  * 递归 flat
  */
 const flat = (arr) => {
    let result = []
    function each(arr) {
        arr.forEach(item => {
            if (item instanceof Array) {
                each(item)
            } else {
                result.push(item)
            }
        })
    }
    each(arr)
    return result.join(',')
}
 /**
  * 做隐式类型转换 flat
  */

 /**
  * 原理:数组 + 加号是双目运算符,
  * arr是数组,不是基本类型也不是数字,是一个对象,
  * 调对象的时候,会调arr的valueof,
  * 如果valueof返回的还不是基本类型,会调toString方法
  * 数组的vauleOf返回的就是数组本身,所以会调toString方法
  * 如果toString方法返回基本类型,到此为止,否则就会报错
  * 
  */
 const flatten = function (arr) {
    let toString = Array.prototype.toString
    Array.prototype.toString = function() {
        return this.join(',')
    }
    let result = arr + ''
    Array.prototype.toString = toString
    return result
 }
 // 所以 改valueOf 也是可以的
 const flatten = function (arr) {
    let value = Array.prototype.valueOf
    Array.prototype.valueOf = function() {
        return this.join(',')
    }
    let result = arr + ''
    Array.prototype.valueOf = value
    return result
 }
 /**
  * es6中的遍历器iterator是对自定义的复杂数据结构,提供遍历的方法
  * 如果想给这个数据结构增加一个遍历器,必须
  * 1. Symbol.iterator为key 
  * 2. value为 function 
  * 3. function 要求必须返回一个对象
  * 4. 这个对象中必须有next方法
  * 
  */
Array.prototype[Symbol.iterator] = function() {
    let arr = [].concat(this) // [1, [2, [3, [4, 5], 6], 7], 8] 每一次都会调一次next方法
    let getFirst = function(array) {
        let first = array.shift()
        return first
    }
    return {
        next: function() {
            let item = getFirst(arr) // 1, 第二个[2, [3, [4, 5], 6]
            if (item) { // 1 是真,标识下面还有值
                return {
                    value: item, // 遍历时,当前要返回的值, 当第二次是一个数组的时候,这里有一个隐式类型转换,转成toString了 "2,3,4,5,6"
                    done: false // 是不是已经遍历结束
                }
            } else {
                return {
                    done: true
                }
            }
        }
    }
}
var flat = function(arr) {
    let r = []
    for (let i of arr) {
        r.push(i)
    }
    return r.join(',')
}
// flat([1, [2, [3, [4, 5], 6], 7], 8]); 

/**
 * @description 使用reduce取代map和filter 更新数组每一项,并筛选部分
 */
const filterSomeValue = arr => arr.reduce((a, v) => {
    v = v * 2
    if (v > 50) {
        a.push(v)
    }
    return a
}, [])

/**
 * @description 取整 | 0
 */
const getInt = num => num | 0

/**
 * @description 单例模式 IIFE方式加闭包 本质是通过函数作用域的方式来隐藏内部作用域的变量
 */
const Singleton = (function() {
    let _instance = null // 存储单例

    const Singleton = function() {
        if (_instance) return _instance // 判断是否已经有单例
        _instance = this
        this.init() // 初始化
        return _instance
    }

    Singleton.prototype.init = function() {
        this.foo = 'Singeton Pattern'
    }

    Singleton.getInstance = function() {
        if (_instance) return _instance
        _instance = new Singleton()
        return _instance
    }
    
    return Singleton
})()

const vistor1 = new Singleton()
const vistor2 = new Singleton()
const vistor3 = new Singleton.getInstance()
console.log(vistor1 === vistor3) // true

/**
 * @description 单例模式 ES6的块级作用域 目的是为了 _instance隐藏内部变量
 */
let getInstance

{
    let _instance = null

    const Singleton = function() {
        if (_instance) return _instance // 判断是否已经有单例
        _instance = this
        this.init() // 初始化
        return _instance
    }

    Singleton.prototype.init = function() {
        this.foo = 'Singeton Pattern'
    }

    getInstance = function() {
        if (_instance) return _instance
        _instance = new Singleton()
        return _instance
    }
}

const vistor1 = getInstance()
const vistor2 = getInstance()

/**
 * @description 单例 抽离 创建 和 功能逻辑
 */
class FuncClass {
    constructor(bar) {
        this.bar = bar
        this.init()
    }
    init() {
        this.foo = 'Single Pattern'
    }
}

const Singleton = (function() {
    let _instance = null

    const ProxySingleton = function(bar) {
        if (_instance) return _instance
        _instance = new FuncClass(bar)
        return _instance
    }
    ProxySingleton.getInstance = function(bar) {
        if (_instance) return _instance
        _instance = new Singleton(bar)
        return _instance
    }
    return ProxySingleton
})()

const vistor1 = new Singleton('单例1')
const vistor2 = new Singleton('单例2')
const vistor3 = Singleton.getInstance()

/**
 * @description 单例模式的应用 element-ui 的 loading
 */

import Vue from 'vue'
import loadingVue from './loading.vue // 自己写的Loading组件

const LoadingConstructor = Vue.extend(loadingVue)

let fullscreenLoading

const Loading = (options = {}) => {
    if (options.fullscreen && fullscreenLoading) { // 如果之前创建国,直接返回
        return fullscreenLoading
    }

    let _instance = new LoadingConstructor({
        el: document.createElement('div'),
        data: options
    })

    if (options.fullscreen) {
        fullscreenLoading = _instance
    }
    
    return _instance
}

export default Loading

/**
 * 设计模式-工厂函数 根据不同的输入返回不同类的实例 主要思想是对象的创建和实现分离
 */
class Restaurant {
    static getMenu(menu) {
        switch (menu) {
            case '鱼香肉丝':
                return new YuXiangRouSi()
            case '宫保鸡丁':
                return new GongBaoJiDin()
            default:
                throw new Error('本店没有')
        }
    }
}

class YuXiangRouSi {
    constructor () {
        this.type = '鱼香肉丝'
    }
    eat() {
        console.log(this.type + '好吃~')
    }
}

class GongBaoJiDin {
    constructor () {
        this.type = '宫保鸡丁'
    }
    eat() {
        console.log(this.type + '好吃~')
    }
}

const dish1 = Restaurant.getMenu('鱼香肉丝')
const dish2 = Restaurant.getMenu('宫保鸡丁')

/**
 * @description Vue 源码中的工厂模式 虚拟DOM树机制 createElement生成VNode 用来映射真是DOM节点
 */
createElement('h3', { class: 'main-title' }, [
    createElement('img', { class: 'avatar', attrs: { src: './avatar.png' } }),
    createElement('p', { class: 'user-desc' }, '我要的飞翔,不是谁的肩膀')
])

// createElement:
class Vnode(tag, data. children) {
    // ...
}
function createElement(tag, data, children) {
    return new Vnode(tag, data, children)
}

/**
 * @description 抽象工厂模式
 */
// 饭店方法
 class Restaurant {
    static orderDish(type) {
        switch (type) {
            case '鱼香肉丝':
                return new YuXiangRouSi()
            case '宫保鸡丁':
                return new GongBaoJiDin()
            default:
                throw new Error('本店没有~')
        }
    }
 }
// 菜品抽象类
 class Dish { // 父类
     constructor() {
         if (new.target === Dish) {
             throw new Error('抽象类不能直接实例化')
         }
         this.kind = '菜'
     }
     // 抽象方法 相当于定义一个接口,由子类继承
     eat() {
         throw new Error('抽象方法不能被调用')
     }
 }

 // 菜品 继承菜品抽象类
 class YuXiangRouSi extends Dish {
     constructor() {
         super()
         this.type = '鱼香肉丝'
     }

     eat() {
         console.log(this.kind, this.type, '鱼香肉丝')
     }
 }

 class GongBaoJiDin extends Dish {
    constructor() {
        super()
        this.type = '宫保鸡丁'
    }

    eat() {
        console.log(this.kind, this.type, '宫保鸡丁')
    }
 }

 const dish0 = new Dish() // Error 抽象类不能直接实例化
 const dish1 = Restaurant.orderDish('鱼香肉丝')
 dish1.eat()


 /**
  * 林衣踩在一个板凳上,左手拿着颜料,右手拿着画笔,给自己的家门口写上四个大字,艺术设计
  * 结束之后,满意的看着自己的作品,含笑打开房门,看着自己屋里的格局,左边窗户上挂着一把她从上个世纪
  * 拿回来的剑,
  * 小黄此时在她脚底下转圈,她把目光放到桌上的一个工厂模型,这是她准备去的地方,那里有她这个时代要找的人,
  * 传说中可以拯救末世的人。
  * 
  *
  */
 /**
  * @description 建造者模式 分布构建一个复杂对象,并允许韩兆步骤构造
  * 如: 汽车, 笔记本, 很多部件都由零件制造商制造 指挥长决定如何组装
  */
 
 
/**
 * @description 代理模式,又称为委托模式,他为目标对象创造了一个代理对象,以控制对目标对象的访问
 * Visitor -----------> Proxy --------------> Target
 * Visitor <----------- Proxy <-------------- Target
 * 如 axios 拦截器, 代理模式和装饰者模式很像,但是代理模式是会对目标对象的访问进行控制,axios拦截器可以取消
 * 请求,而装饰者模式是在目标对象上添加新的功能
 * 
 * vue2.x 通过劫持各个属性的 getter 和 setter ,在数据变化时,通过发布订阅模式发布消息给订阅者
 * 触发相应的监听回调,实现数据响应式
 * vu2.x 的缺点: 
 * 1. 无法监听利用索引直接设置数组的项
 * 2. 无法监听数组的长度变化
 * 3. 无法监听ES6的 Set, WeakSet, Map, WeakMap 的变化
 * 4. 无法监听Class 类型的数据
 * 5. 无法监听对象属性的新增或者删除
 */

//  const proxy = new Proxy(target, handler)
const SuperStar = {
    name: '小鲜肉',
    scheduleFlag: false, // 档期标识 默认没空
    playAdvertisement(ad) {
        console.log(ad)
    }
}

const ProxyAssistant = {
    name: '经纪人张某',
    scheduleTime(ad) {
        const schedule = new Proxy(SuperStar, {
            set(obj, prop, val) {
                if (prop !== prop.scheduleFlag) return
                if (!obj.scheduleFlag && val) {
                    obj.scheduleFlag = true
                    obj.playAdvertisement(ad) // 安排
                }
            }
        })

        setTimeout(() => {
            console.log('小鲜鲜有空了')
            schedule.scheduleFlag = true
        }, 2000)
    },

    playAdvertisement(reward, ad) {
        if (reward > 1000000) {
            console.log('没问题,我们小鲜鲜最喜欢拍广告了')
            ProxyAssistant.scheduleTime(ad)
        } else {
            console.log('没空,滚')
        }
    }
}

ProxyAssistant.playAdvertisement(10000, '广告')

/**
 * @description 享元模式 公用某些资源
 */
let examCardNum = 0 // 驾考车总数

class ExamCar {
    constructor(carType) {
        examCardNum++
        this.cardId = examCardNum
        this.carType = carType ? '手动挡' : '自动挡'
        this.usingState = false // 是否正在占用
    }

    // 在本车上考试
    examine(candidateId) {
        return new Promise(resolve => {
            this.usingState = true
            console.log(`考生 ${candidateId} 在 ${this.carType}驾考车-${this.cardId}上考试`)
            setTimeout(() => { // 0 - 2s 考完后
                this.usingState = false
                resolve()
            }, Math.random() * 2000)
        })
    }
}

// 手动挡汽车对象池
ManualExamCarPool = {
    _pool: [], // 驾考车对象池
    _candidateQueue: [], // 考生队列

    // 注册考生 ID 列表
    registCandidates(candidateList) {
        candidateList.forEach(candidateId => this.registCandidate(candidateId))
    },

    // 注册手动挡考生
    registCandidate(candidateId) {
        const examCar = this.getManualExamCar() // 找一个未被占用的手动挡驾考车
        if (examCar) {
            examCar.examine(candidateId) // 开始考试, 考完了让队列中的下一个考生开始考试
                .then(() => {
                    const nextCandidateId = this._candidateQueue.length && this._candidateQueue.shift()
                    nextCandidateId && this.registCandidate(nextCandidateId)
                })
        } else { // 如果没有空车,将考生推入队列
            this._candidateQueue.push(candidateId)
        }
    },

    // 注册手动挡车
    initManualExamCar(manualExamCarNum) {
        for(let i = 0; i <= manualExamCarNum; i++) {
            this._pool.push(new ExamCar(true))
        }
    },

    // 获取状态为未被占用的手动挡车
    getManualExamCar() {
        return this._pool.find(car => !car.usingState)
    }
}

ManualExamCarPool.initManualExamCar(3) // 一共三辆考车
ManualExamCarPool.registCandidates([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) // 10个考生来考试

// 数据库连接池
const mysql = require('mysql')

const connection = mysql.createConnection({
    host: 'localhost',
    user: 'root',
    password: '123456',
    database: 'mydb',
    port: '3306'
})

// 连接回调,在回调中增删改查
connection.connect()

// 关闭连接
connection.end()

// 连接池的创建
const pool = mysql.createPool({
    host: 'localhost',
    user: 'root',
    password: '123456',
    database: 'mydb',
    port: '3306'
})

pool.getConnection((err, connection) => {
    // 数据库操作
    connection.release() // 将连接释放回连接池中
})

// 关闭连接池
pool.end()

/**
 * @description REST 以及 6个限制
 * REST是一种风格,万维网网络架构风格 Representational State Transfer
 * Representational 表示,数据的表现形式 如json xml
 * State 状态 当前的状态 可变的状态
 * Transfer 传输 数据在互联网上传输
 * 
 * 6 个限制
 * 1. 客户端--服务器(Client-Server) CS架构
 * 服务端专注数据存储,提升简单性 ---形容词来自于REST作者的博士论文
 * 前段转出用户界面,提升可移植性
 * 2. 无状态
 * 所有的用户会话信息都保存在客户端
 * 每次请求必须包含所有信息,不能依赖上下文信息
 * 服务端不用保存会话信息,提升了简单性,可靠性,可见性(每次请求要包含很多信息)
 * 3. 缓存(Cache)
 * 所有服务端响应都要被表位可缓存或不可缓存
 * 如 静态资源,至少在一个版本内不会变
 * 减少交互,提升速度
 * 4. 统一接口 (Uniform Interface)
 * 接口与实现解耦,前后端可以独立开发迭代
 * 5. 分层系统(Layered System)
 * 每层只知道相邻的一层,后面隐藏的就不知道了
 * 客户端不知道是和代理还是很真实服务器通信
 * 中间件,不用关心业务逻辑,只要做系统中的分层
 * 其它层:安全层, 负载均衡(分发流量),缓存层
 * 6. 按需代码(code on Demand 可选)
 * 客户端可以下载运行服务端传来的代码(如, js)
 * eval() 可以执行
 * 简化客户端
 * 
 * 
 * 统一接口 的限制:
 * 1. 资源的标识
 * 资源是任何可以命名的事物,如,用户,评论
 * 每个资源可以通过URI被唯一标识 URI 是统一资源标识符,URI最常见的形式是统一资源定位符,URL
 * 2. 通过表述来操作资源
 * 表述 如 JSON XML 传给服务端,服务端来操作
 * 3. 自描述信息
 * 媒体类型(application/json)
 * HTTP方法
 * 是否缓存 Cache-Control
 * 
 * 
 * 控制器的本质是中间件,中间件的本质是函数
 * 
 * koa 断点调试
 * 在vscode中在要调试的文件界面按下F5就启动了
 * 
 * 错误处理
 * 1. 运行时错误,语法没有错误的前提下,返回500 
 * 2. 逻辑错误,找不到404 
 * 先决条件失败402 (请求某个ID不存在)
 * 无法处理的实体 422 (参数格式不对)
 * 4开头的大多是逻辑错误
 * 
 * 防止程序挂掉,try catch
 * 告诉用户错误信息
 * 便于开发调试
 * 
 * koa自带的错误处理
 * koa找不到的情况下默认返回404
 * 
 * 
 */
// 123456789876543212345678987654321
const getNum = n => {
    let num = 0
    let flag = true
    for(let i = 0; i <= n; i++) {
        if (num == 1) flag = true
        if (num == 9) flag = false
        flag ? num++ : num--
    }
    return num
}

// promise 用法
new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('fullfilled')
    }, 1000)
})

/**
 * 构造函数,必须接受一个函数作为参数,函数又接收 resolve, reject 两个参数,也是函数
 * 1. 必须是函数
 * 2. 状态值
 * promise 对象存在三种状态
 * - Pending 进行中
 * - Fullfilled 已成功
 * - Rejected 已失败
 * 状态只能由 Pending 变为Fullfilled 或者 Pending 变为 Rejected
 * 且状态改变之后不会发生变化,会一直保存这个状态
 * Promise 的值是指状态改变时,传给回调函数的值
 */

 // 判断变量是否为函数
 const isFunction = val => typeof(val) === 'function'

// 定义三个常量 标记Promise对象的三种状态和值
const PENDING = 'PENDING'
const FULLFILLED = 'FULLFILLED'
const REJECTED = 'REJECTED'

// 定义我们自己的 promise
class MyPromise {
    constructor(exector) {
        if (!isFunction(exector)) {
            throw new Error('MyPromise must accept a function as a parameter')
        }
        
        // 添加状态
        this._status = PENDING
        this._value = undefined
        // 添加成功回调函数队列
        this._fullfilledQueues = []
        // 添加失败回调函数队列
        this._rejectedQueues = []

        try {
            exector(this._resolve.bind(this), this._reject.bind(this))
        } catch (err) {
            this._reject(err)
        }
    }
    // 添加resolve时执行的函数
    _resolve(val) {
        if (this._status !== PENDING) return
        // 依次执行成功队列中的函数
        const run = () => {
            this._status = FULLFILLED
            this._value = val
            this._fullfilledQueues.map(cb => cb())
        }
        // 为了支持同步的Promise 这里采用异步调用
        setTimeout(() => run(), 0)
    }
    // 添加reject时执行的函数
    _reject(err) {
        if (this._status !== PENDING) return
        const run = () => {
            this._status = REJECTED
            this._value = err
            let cb
            while (cb = this._rejectedQueues.shift()) {
                cb(err)
            }
        }
        setTimeout(run, 0)
    }

    // 添加 then 方法
    then(onFullfilled, onRejected) {
        const { _value, _status } = this
        // 返回一个新的 Promise
        return MyPromise((onFullfilledNext, onRejectedNext) => {
            // 封装一个成功时执行的函数
            let fullfilled = value => {
                try {
                    if (!isFunction(onFullfilled)) {
                        onFullfilledNext(value)
                    } else {
                        let res = onFullfilled(value)
                        if (res instanceof MyPromise) {
                            // 如果当前回调函数返回MyPromise对象,必须等待其状态改变后再执行
                            res.then(onFullfilledNext, onRejectedNext)
                        } else {
                            // 否则将返回的结果作为参数,传入下一个then回调,并立即执行下一个then
                            onFullfilledNext(res)
                        }
                    }
                } catch (err) {
                    // 如果函数执行出错,新的promise对象状态为失败
                    onRejectedNext(err)
                }
            }

            // 封装一个失败时执行的函数
            let rejected = error => {
                try {
                    if (!isFunction(onRejected)) {
                        onRejectedNext(error)
                    } else {
                        let res = onRejected(error)
                        if (res instanceof MyPromise) {
                            res.then(onFullfilledNext, onRejectedNext)
                        } else {
                            onFullfilledNext(res)
                        }
                    }
                } catch (err) {
                    onRejectedNext(err)
                }
            }
            switch (_status) {
                // 当状态为 pending 时,将then 方法回调函数计入执行队列等待
                case PENDING:
                    this._fullfilledQueues.push(onFullfilled)
                    this._rejectedQueues.push(onRejected)
                    break
                // 当状态已经改变时,立即执行对象的回调
                case FULLFILLED:
                    fullfilled(_value)
                    break
                case REJECTED:
                    rejected(_value)
                    break
            }

        })
    }
}

// promise 的 then 方法接收两个参数 参数可选
// promise.then(onFullfilled, onRejected)
/**
 * 多次调用then 
 * then 可以被同一个Promise对象调用多次
 * 当promise成功状态时,所有onFullFilled需要按注册顺序依次回调
 * 失败同上
 * then方法必须返回一个新的promise对象
 * promise支持链式调用
 */

let promise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve()
    }, 1000)
})

promise2 = promise1.then(res => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('return promise')
        }, 2000)
    })
})
promise2.then(res => {
    console.log(res)
})


/**
 * @description 适配器 
 */
var chinaPlug = {
    type: '中国插头',
    chinaInPlug() {
        console.log('开始充电')
    }
}
chinaPlug.chinaInPlug() // 开始供电

// 当我们到日本了,要用日本的适配
var japanPlug = {
    type: '日本插头',
    japanInPlug() {
        console.log('开始供电')
    }
}

// 日本插头电源适配器
function japanPlugAdapter(plug) {
    return {
        chinaInPlug() {
            return plug.japanInPlug()
        }
    }
}

japanPlugAdapter(japanPlug).chinaInPlug() // 开始供电

/**
 * @description 装饰者模式
 */
var originHouse = {
    getDesc() {
        console.log('毛坯房')
    }
}

function furniture() {
    console.log('搬入家具')
}
function painting() {
    console.log('刷漆')
}

originHouse.getDesc = function() {
    var getDesc = originHouse.getDesc
    return function() {
        getDesc()
        furniture()
    }
}

originHouse.getDesc = function() {
    var getDesc = originHouse.getDesc
    return function() {
        getDesc()
        painting()
    }
}

originHouse.getDesc()

/**
 * @description 外观模式 --- 一个统一的外观为复杂的子系统提供一个简单的高层功能接口
 */
var uav = {
    // 电子调速器
    diantiao1: {
        up() {
            console.log('电调1发送指令:电机1增大转速')
            uav.dianji1.up()
        },
        down() {
            console.log('电调1发送指令:电机1减少转速')
            uav.dianji1.up()
        }
    },
    diantiao2: {
        up() {
            console.log('电调2发送指令:电机1增大转速')
            uav.dianji1.up()
        },
        down() {
            console.log('电调2发送指令:电机1减少转速')
            uav.dianji1.up()
        }
    }
}
// 遥控器
controller: {
    up() {
        uav.diantiao1.up()
        uav.diantiao2.up()
    }
    down() {
        uav.diantiao1.down()
        uav.diantiao2.down()
    }
    // ...
}

// 函数参数重载
function domBinaEvent(nodes, type, selector, fn) {
    if (fn === undefined) {
        fn = selector
        selector = null
    }
    // ...
}
domBinaEvent(node, 'click', '#div1', fn)
domBinaEvent(node, 'click', '#div1')

// vue 中的函数参数重载
export function createElement(
    context,
    tag,
    data,
    children,
    normalizationType,
    alwaysNormalize
) {
    if (Array.isArray(data) || isPrimitive(data)) {
        normalizationType = children
        children = data
        data = undefined
    }
}

/**
 * @description 发布 订阅 又称观察者模式
 * 比如:群聊,所有群聊的人都订阅了这个群,
 * 当有人发消息时,群会主动通知给所有订阅这个群的人
 * 1. Publisher: 发布者 当消息发生时,负责通知订阅者
 * 2. Subscriber: 订阅者 当消息发生时被通知的对象
 * 3. SubscriberMap: 储存所有订阅者的数组
 * 4. type: 消息类型,订阅者可以订阅不同消息类型
 * 5. subscribe: 将订阅者添加到SubscriberMap中
 * 6. unsubscribe: 将SubscriberMap中删除订阅者
 * 7. notidy: 遍历通知 SubscriberMap 中对象的每个订阅者
 */
const Publisher = (function() {
    const _subsMap = {} // 储存订阅者
    return {
        // 消息订阅
        subscribe(type, cb) {
            if (_subsMap[type]) {
                if (!_subsMap[type].includes(cb)) {
                    _subsMap[type].push(cb)
                }
            } else _subsMap[type] = [cb]
        },
        // 消息退订
        unsubscribe(type, cb) {
            if (!_subsMap[type] || !_subsMap[type].includes(cb)) return
            const idx = _subsMap[type].indexOf(cb)
            _subsMap[type].splice(idx, 1)
        },
        // 消息发布
        notify(type, ...payload) {
            if (!_subsMap[type]) return
            _subsMap[type].forEach(cb => cb(...payload))
        }
    }
})()

Publisher.subscribe('运动鞋', message => console.log('33码 红色'))

Publisher.notify('运动鞋', '运动鞋到货啦...')

class Publisher {
    constructor() {
        this._subsMap = {}
    }

    // 消息订阅
    subscribe(type, cb) {
        if (_subsMap[type]) {
            if (!_subsMap[type].includes(cb)) {
                _subsMap[type].push(cb)
            }
        } else _subsMap[type] = [cb]
    }
    // 消息退订
    unsubscribe(type, cb) {
        if (!_subsMap[type] || !_subsMap[type].includes(cb)) return
        const idx = _subsMap[type].indexOf(cb)
        _subsMap[type].splice(idx, 1)
    }
    // 消息发布
    notify(type, ...payload) {
        if (!_subsMap[type]) return
        _subsMap[type].forEach(cb => cb(...payload))
    }
}

const adadis = new Publisher()
adadis.subscribe('运动鞋', message => console.log('33码 红色'))
Publadadisisher.notify('运动鞋', '运动鞋到货啦...')

/*
 * 
 */

/**
 * @description 考察点,变量提升
 */
var a = 10
function foo () {
    console.log(a)
    var a = 20 // undefined
    // let a = 20 // ReferenceError: a is not defined
}
foo() // undefined

/**
 * @description 考察点
 */
var array = []
for (var i = 0; i < 3; i++) {
    array.push(() => i)
}
var newArray = array.map(el => el())
console.log(array, newArray)

/**
 * @description 考察点,setTimeout异步,让出当前线程
 */
function foo() {
    foo(); //执行1000次左右会发生堆栈溢出的错误, 
    //setTimeout(foo, 0); //永远不会堆栈溢出
}
foo()
/**
 * 执行settimeout函数这个时候虽然它又继续调用自己,
 * 但是这里可以理解多线程操作了,
 * 只是开启另一个线程来启动foo,
 * 而当前线程仍然继续执行,
 * 当当前线程的foo执行完成后,
 * 自然就出栈了,每一次的FOO执行都是这个过程,
 * 所以栈里不会容量超标的
*/

function foo(){ // 会造成页面卡死
    return Promise.resolve().then(foo) 
}
/**
 * @description 如何让一个对象可迭代
 */
var obj = { x: 1, y: 2, z: 3 }
obj[Symbol.iterator] = function() {
    return {
        next: function() {
            if (this._count === 3) {
                return {
                    value: this._count,
                    done: true
                }
            }
            this._count += 1
            return {
                value: this._count,
                done: false
            }
        },
        _count: 0
    }
};
[...obj]

你可能感兴趣的:(前端零碎知识点---有点乱)