js常用工具类函数库

1,060 阅读8分钟

内容将从js高级特性,奇技淫巧,高级特性等方面安排内容,包括多字段排序,经典递归,链式调用,class构造类型等等

1. 通用函数

  • 1.1工厂函数
(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory("arg") :
    typeof define === 'function' && define.amd ? define(['arg'], factory) : (global.plunginName = factory("arg"))
})(this, function () {
  'use strict'
  // 主要内容
  return {}
})
  • 1.2 canvas链式调用
  window.Canvas2DContext = function (canvas) { //cavas链式调用
      if (typeof canvas === 'string') {
        canvas = document.getElementById(canvas)
      }
      if (!(this instanceof window.Canvas2DContext)) {
        return new window.Canvas2DContext(canvas)
      }
      this.context = this.ctx = canvas.getContext('2d')
      if (!window.Canvas2DContext.prototype.arc) {
        Canvas2DContext.setup.call(this, this.ctx)
      }
    }
    window.Canvas2DContext.setup = function () {
      let methods = ['arc', 'arcTo', 'beginPath', 'bezierCurveTo', 'clearRect', 'clip',
        'closePath', 'drawImage', 'fill', 'fillRect', 'fillText', 'lineTo', 'moveTo',
        'quadraticCurveTo', 'rect', 'restore', 'rotate', 'save', 'scale', 'setTransform',
        'stroke', 'strokeRect', 'strokeText', 'transform', 'translate'
      ]
      let getterMethods = ['createPattern', 'drawFocusRing', 'isPointInPath', 'measureText', // drawFocusRing not currently supported
        // The following might instead be wrapped to be able to chain their child objects
        'createImageData', 'createLinearGradient',
        'createRadialGradient', 'getImageData', 'putImageData'
      ]
      let props = ['canvas', 'fillStyle', 'font', 'globalAlpha', 'globalCompositeOperation',
        'lineCap', 'lineJoin', 'lineWidth', 'miterLimit', 'shadowOffsetX', 'shadowOffsetY',
        'shadowBlur', 'shadowColor', 'strokeStyle', 'textAlign', 'textBaseline'
      ]
      for (let m of methods) {
        let method = m
        Canvas2DContext.prototype = function () {
          this.ctx.apply(this.ctx, arguments)
          return this
        }
      }
      for (let m of getterMethods) {
        let method = m
        Canvas2DContext.prototype = function () {
          return this.ctx.apply(this.ctx, arguments)
        }
      }
      for (let p of props) {
        let prop = p
        Canvas2DContext.prototype[prop] = function (value) {
          if (value === undefined)
            return this.ctx[prop]
          this.ctx[prop] = value
          return this
        }
      }
    }
    
   调用示例
   let ctx = Canvas2DContext(document.getElementById('canvas')) 
   ctx.strokeStyle('rgb(30, 110, 210)').transform(10, 3, 4, 5, 1, 0).strokeRect(2, 10, 15, 20).context 
   let strokeStyle = Canvas2DContext(document.getElementById('canvas')).strokeStyle('rgb(50, 110, 210)').strokeStyle() 

  • 1.3 Date.format
Date.prototype.format = function (format) {
  let o = {
    "Y+": this.getFullYear() + '',
    "M+": this.getMonth() + 1, //month  MM
    "D+": this.getDate(), //day  DD
    "h+": this.getHours(), //hour  hh
    "m+": this.getMinutes(), //minute mm
    "s+": this.getSeconds(), //second ss
    "Q+": Math.floor((this.getMonth() + 3) / 3), //quarter 季度 q
    "c+": this.getMilliseconds(), //millisecond 毫秒 c
    "W": ['一', '二', '三', '四', '五', '六', '日'][this.getDay() - 1] //week 星期
  }
  for (let k in o) {
    if (new RegExp("(" + k + ")").test(format)) {
      format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr((("" + o[k]).length >= 2 ? 2 : ("" + o[k]).length)))
    }
  }
  return format
}

调用示例:
  //new Date( year, month, date, hrs, min, sec)
  //new Date() 参数可以为整数  也可以为字符串  但格式必须正确  example: new Date(2009,1,1)   //正确  new Date("2009/1/1")     //正确
  //example  new Date().format( "当前日期为:YYYY-MM-DD,星期W,为第Q季度,时间为:hh:mm:ss:c")
  • 1.4 可终止的迭代器
function each(object, callback) {
  /*可终止循环each方法*/
  var type = (function (obj) {
    switch (obj.constructor) {
      case Object:
        return 'Object';
        break;
      case Array:
        return 'Array';
        break;
      case NodeList:
        return 'NodeList';
        break;
      default:
        return 'null';
        break;
    }
  })(object);
  // 为数组或类数组时, 返回: index, value
  if (type === 'Array' || type === 'NodeList') {
    // 由于存在类数组NodeList, 所以不能直接调用every方法
    [].every.call(object, function (v, i) {
      return callback.call(v, v, i) === false ? false : true;
    });
  }
  // 为对象格式时,返回:key, value
  else if (type === 'Object') {
    for (var i in object) {
      if (callback.call(object[i], object[i],i) === false) {
        break;
      }
    }
  }else{
  }
}
  • 1.5 多字段排序
function muchfieldarrsort(sarr, keys) {
  // 多字段排序
  /** sarr[]<Object>:原始数组。 keys[]<String>:要排序的多个字段,必须为数组*/
  return sarr.sort(compare);
  function compare(a, b, c = keys[0], i = 0) { //按合并类型递归排序
    //var c = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : groupfieldarr[0];
    //var i = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
    if (a[c] == b[c]) { //等于的话进行判断是否还有后续字段需要排序,没有则返回0;有则递归进行后续字段排序处理。
      if (i == (keys.length - 1)) { // 没有后续字段
        i = 0;
        return 0;
      }
      i += 1;
      return compare(a, b, keys[i], i); //递归排序后续字段
    } else if (a[c] > b[c]) {
      return 1;
    } else {
      return -1;
    }
  }
}
  • 1.6 全屏切换
function tooggleFullScreen(isFullscreen) {
  isFullscreen = isFullscreen === undefined ? !(document.fullScreen || document.mozFullScreen || document.webkitIsFullScreen) : isFullscreen
  var de = document.documentElement
  if (isFullscreen) { // 进入全屏
    if (de.requestFullscreen) {
      de.requestFullscreen()
    } else if (de.mozRequestFullScreen) {
      de.mozRequestFullScreen()
    } else if (de.webkitRequestFullScreen) {
      de.webkitRequestFullScreen()
    }
  } else { // 退出全屏
    de = document
    if (de.exitFullscreen) {
      de.exitFullscreen()
    } else if (de.mozCancelFullScreen) {
      de.mozCancelFullScreen()
    } else if (de.webkitExitFullscreen) {
      de.webkitExitFullscreen()
    }
  }
}
  • 1.7 节流函数
// 思路:在规定时间内只触发一次
function throttle(fn, delay) {
  // 利用闭包保存时间
  let prev = Date.now()
  return function () {
    let context = this
    let arg = arguments
    let now = Date.now()
    if (now - prev >= delay) {
      fn.apply(context, arg)
      prev = Date.now()
    }
  }
}

调用示例
function fn() {
  console.log('节流')
}
addEventListener('scroll', throttle(fn, 1000))
  • 1.8 防抖函数
// 思路:在规定时间内未触发第二次,则执行
function debounce(fn, delay) {
  // 利用闭包保存定时器
  let timer = null
  return function () {
    let context = this
    let arg = arguments
    // 在规定时间内再次触发会先清除定时器后再重设定时器
    clearTimeout(timer)
    timer = setTimeout(function () {
      fn.apply(context, arg)
    }, delay)
  }
}

调用示例
function fn() {
  console.log('防抖')
}
addEventListener('scroll', debounce(fn, 1000))
  • 1.9 柯里化函数
柯里化函数的定义: 将多参数的函数转换成单参数的形式。
柯里化函数实现的原理: 利用闭包原理在执行可以形成一个不销毁的作用域, 然后把需要预先处理的内容都储存在这个不销毁的作用域中, 并且返回一个最少参数函数。
第一种: 固定传入参数, 参数够了才执行
   实现要点:
   柯里化函数接收到足够参数后,就会执行原函数,那么我们如何去确定何时达到足够的参数呢?
   柯里化函数需要记住你已经给过他的参数,如果没给的话,则默认为一个空数组。
   接下来每次调用的时候,需要检查参数是否给够,如果够了,则执行fn,没有的话则返回一个新的 curry 函数,将现有的参数塞给他。
 
初步封装
var currying = function (fn) {
  // args 获取第一个方法内的全部参数
  var args = Array.prototype.slice.call(arguments, 1)
  return function () {
    // 将后面方法里的全部参数和args进行合并
    var newArgs = args.concat(Array.prototype.slice.call(arguments))
    // 把合并后的参数通过apply作为fn的参数并执行
    return fn.apply(this, newArgs)
  }
}

支持多参数传递封装
function progressCurrying(fn, args) {
  var _this = this
  var len = fn.length;
  var args = args || [];
  return function () {
    var _args = Array.prototype.slice.call(arguments);
    Array.prototype.push.apply(args, _args);
    // 如果参数个数小于最初的fn.length,则递归调用,继续收集参数
    if (_args.length < len) {
      return progressCurrying.call(_this, fn, _args);
    }
    // 参数收集完毕,则执行fn
    return fn.apply(this, _args);
  }
}
  • 1.10简单双向数据绑定
let obj = {}
let input = document.getElementById('input')
let span = document.getElementById('span')
// 数据劫持
Object.defineProperty(obj, 'text', {
  configurable: true,
  enumerable: true,
  get() {
    console.log('获取数据了')
  },
  set(newVal) {
    console.log('数据更新了')
    input.value = newVal
    span.innerHTML = newVal
  }
})
// 输入监听
input.addEventListener('keyup', function (e) {
  obj.text = e.target.value
})
  • 1.11 ajax
function ajax(options) {
  // 请求地址
  const url = options.url
  // 请求方法
  const method = options.method.toLocaleLowerCase() || 'get'
  // 默认为异步true
  const async = options.async
  // 请求参数
  const data = options.data
  // 实例化
  const xhr = new XMLHttpRequest()
  // 请求超时
  if (options.timeout && options.timeout > 0) {
    xhr.timeout = options.timeout
  }
  // 返回一个Promise实例
  return new Promise((resolve, reject) => {
    xhr.ontimeout = () => reject && reject('请求超时')
    // 监听状态变化回调
    xhr.onreadystatechange = () => {
      if (xhr.readyState == 4) {
        // 200-300 之间表示请求成功,304资源未变,取缓存
        if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
          resolve && resolve(xhr.responseText)
        } else {
          reject && reject()
        }
      }
    }
    // 错误回调
    xhr.onerror = err => reject && reject(err)
    let paramArr = []
    let encodeData
    // 处理请求参数
    if (data instanceof Object) {
      for (let key in data) {
        // 参数拼接需要通过 encodeURIComponent 进行编码
        paramArr.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]))
      }
      encodeData = paramArr.join('&')
    }
    // get请求拼接参数
    if (method === 'get') {
      // 检测url中是否已存在 ? 及其位置
      const index = url.indexOf('?')
      if (index === -1) url += '?'
      else if (index !== url.length - 1) url += '&'
      // 拼接url
      url += encodeData
    }
    // 初始化
    xhr.open(method, url, async)
    // 发送请求
    if (method === 'get') xhr.send(null)
    else {
      // post 方式需要设置请求头
      xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=UTF-8')
      xhr.send(encodeData)
    }
  })
}
  • 1.12 树形数据和平行数据互相转换
平行数据转嵌套树
function listToTree(tempData) {
  // 删除所有的children,以防止多次调用
  let map = {}; // 构建map
  tempData.forEach(function (item) {
    delete item.children;
    map[item.id] = item; // 构建以id为键 当前数据为值
    map["children"]=[]
  });
  let treeData = [];
  tempData.forEach(child => {
    const mapItem = map[child.pid]; // 判断当前数据的pid是否存在map中
    if (mapItem) {
      // 存在则表示当前数据不是最顶层的数据
      // 注意: 这里的map中的数据是引用了tempData的它的指向还是arr,当mapItem改变时arr也会改变,踩坑点
      (mapItem.children || (mapItem.children = [])).push(child); // 这里判断mapItem中是否存在child
    } else {
      // 不存在则是顶层数据
      treeData.push(child);
    }
  });
  return treeData;
}

树形数据转平行数据,运用参数缓存结果
function treeToList(data, pid = '',list = []) {
  // 树形数据转换成平行数据
  data.forEach((item, index) => {
    list.push(Object.assign(item, { pid: }))
    if (Array.isArray(item.children) && item.children.length) {
      this.treeToList(item.children,item.id, list)
    }
  })
  return list
}

  • 1.13 精确计算
  • 1.14 金额大写
function digitUppercase(money) {
    // 汉字的数字
    let cnNums = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']
    // 基本单位
    let cnIntRadice = ['', '拾', '佰', '仟']
    // 对应整数部分扩展单位
    let cnIntUnits = ['', '万', '亿', '兆']
    // 对应小数部分单位
    let cnDecUnits = ['角', '分', '毫', '厘']
    // 整数金额时后面跟的字符
    let cnInteger = '整'
    // 整型完以后的单位
    let cnIntLast = '元'
    // 最大处理的数字
    let maxNum = 999999999999999.9999
    // 金额整数部分
    let integerNum
    // 金额小数部分
    let decimalNum
    // 输出的中文金额字符串
    // 分离金额后用的数组,预定义
    let parts
    let chineseStr = money < 0 ? '欠' : ''
    money = (money + '').split('').filter((item, index) => {
      return new RegExp('^[0-9.]$').test(item)
    }).join('')
    if (money === '') { return '' }
    money = Math.abs(money) + ''
    if (money >= maxNum) {
      // 超出最大处理数字
      return ''
    }
    if (money === 0) {
      chineseStr = chineseStr + (cnNums[0] + cnIntLast + cnInteger)
      return chineseStr
    }
    // 转换为字符串
    if (money.indexOf('.') === -1) {
      integerNum = money
      decimalNum = ''
    } else {
      parts = money.split('.')
      integerNum = parts[0]
      decimalNum = parts[1].substr(0, 4)
    }
    // 获取整型部分转换
    if (parseInt(integerNum, 10) > 0) {
      let zeroCount = 0
      let IntLen = integerNum.length
      for (let i = 0; i < IntLen; i++) {
        let n = integerNum.substr(i, 1)
        let p = IntLen - i - 1
        let q = p / 4
        let m = p % 4
        if (n === '0') {
          zeroCount++
        } else {
          if (zeroCount > 0) {
            chineseStr += cnNums[0]
          }
          // 归零
          zeroCount = 0
          chineseStr += cnNums[parseInt(n)] + cnIntRadice[m]
        }
        if (m === 0 && zeroCount < 4) {
          chineseStr += cnIntUnits[q]
        }
      }
      chineseStr += cnIntLast
    }
    // 小数部分
    if (decimalNum !== '') {
      let decLen = decimalNum.length
      for (let i = 0; i < decLen; i++) {
        let n = decimalNum.substr(i, 1)
        if (n !== '0') {
          chineseStr += cnNums[Number(n)] + cnDecUnits[i]
        }
      }
    }
    if (chineseStr === '') {
      chineseStr += cnNums[0] + cnIntLast + cnInteger
    } else if (decimalNum === '') {
      chineseStr += cnInteger
    }
    return chineseStr
}

  • 1.15 分级高效递归汇总,支持数组,单条数据的汇总
let utils = {
  performGradedSummarySingData(obj, gradedCalcFields) { // 分级汇总单条数据计算
    let self = this
    if (Array.isArray(obj.children) && obj.children.length) {
      gradedCalcFields.forEach((key) => {
        obj[key] = obj.children.reduce((preResult, next) => {
          let curItemGradedSummaryValue = 0
          if (Array.isArray(next.children) && next.children.length) {
            curItemGradedSummaryValue = parseFloat(self.performGradedSummarySingData(next, gradedCalcFields)[key])
          } else {
            curItemGradedSummaryValue = parseFloat(next[key])
          }
          return preResult + (isNaN(curItemGradedSummaryValue) ? 0 : curItemGradedSummaryValue)
        }, 0)
      })
    }
    return obj
  },
  performGradedSummaryData(data, gradedCalcFields) { // 数组分级汇总计算
    let self = this
    data.forEach((item) => {
      self.performGradedSummarySingData(item, gradedCalcFields)
    })
    return data
  },
  listToTreeByNestCodeLength(data, nestCodeKey, ) { // 平行数据 根据nestCodeKey 长度 生成嵌套数据
    let map = {} // 构建map
    data.forEach(function (item) {
      map[item[nestCodeKey]] = item
      item.children = []
    })
    let treeData = []
    data.forEach(item => {
      let pnestCodeKey = item[nestCodeKey]
      pnestCodeKey = pnestCodeKey.slice(0, pnestCodeKey.length - 1)
      let mapItem = map[item[pnestCodeKey]]
      while (!mapItem && pnestCodeKey !== '') {
        pnestCodeKey = pnestCodeKey.slice(0, pnestCodeKey.length - 1)
        mapItem = map[pnestCodeKey]
      }
      if (mapItem) {
        (mapItem.children || (mapItem.children = [])).push(item)
      } else {
        treeData.push(item)
      }
    })
    return treeData
  }
}
设计一个方法将
let list = [
  {
    a: 0,
    b: 0,
    itemCode: 10
  },
  {
    a: 0,
    b: 0,
    itemCode: 101001
  },
  {
    a: 1,
    b: 3,
    itemCode: 1010010001
  },
  {
    a: 2,
    b: 3,
    itemCode: 1010010002
  },
  { 
    a: 3,
    b: 7,
    itemCode: 101002
  },
  {
    a: 2,
    b: 2,
    itemCode: 11
  }
]
转换得到:
let list2 = [
  {
    a: 6,
    b: 13,
    itemCode: 10,
    children: [
      {
        a: 3,
        b: 6,
        itemCode: 101001,
        children: [
          {
            a: 1,
            b: 3,
            itemCode: 1010010001
          },
          {
            a: 2,
            b: 3,
            itemCode: 1010010002
          }
        ]
      },
      {
        a: 3,
        b: 7,
        itemCode: 101002
      }
    ]
  },
  {
    a: 2,
    b: 2,
    itemCode: 11
  }
]
let gradedCalcFields = ['a', 'b']
utils.performGradedSummaryData(utils.listToTreeByNestCodeLength(list,'itemcode'), gradedCalcFields)
  • 1.16 Excel 处理相关函数
 let excelUtils = {
  columnLabelToIndex(label) { // 获取列字母索引转化为数字索引
    if (!/[A-Z]+/.test(label.toLocaleUpperCase())) {
      throw new Error('Invalid parameter,Expect a String<Alphabetic>!')
    }
    let index = 0
    let chars = label.toLocaleUpperCase().split('')
    for (let i = 0; i < chars.length; i++) {
      index += (chars[i].charCodeAt() - 'A'.charCodeAt() + 1) * Math.pow(26, chars.length - i - 1)
    }
    return index - 1
  }
  getExcelColumnLabel(num) { // 获取索引获取列字母
    // 方法一
    if (!/[0-9]+/.test(num)) {
      throw new Error('Invalid parameter,Expect a Number!')
    }
    let ordA = 'A'.charCodeAt(0)
    let ordZ = 'Z'.charCodeAt(0)
    let len = ordZ - ordA + 1
    let result = ''
    while (num >= 0) {
      result = String.fromCharCode(num % len + ordA) + result
      num = Math.floor(num / len) - 1
    }
    return result
    // 方法二
    /*
        let temp = ''
        let i = Math.floor(Math.log(25.0 * (num) / 26.0 + 1) / Math.log(26)) + 1
        if (i > 1) {
          let sub = num - 26 * (Math.pow(26, i - 1) - 1) / 25
          for (let j = i; j > 0; j--) {
            temp = temp + String.fromCharCode(sub / Math.pow(26, j - 1) + 65)
            sub = sub % Math.pow(26, j - 1)
          }
        } else {
          temp = temp + String.fromCharCode(num + 65)
        }
        return temp
    */
  }
}

2. 处理技巧

  • 2.1运用存储加速递归
var fibonacci = function (n) {
  return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
}

var fibonacci = (function () {
  var cache = [0, 1]
  return function (n) {
    if (cache[n] === undefined) {
      for (var i = cache.length; i <= n; ++i) {
        cache[i] = cache[i - 1] + cache[i - 2]
      }
    }
    return cache[n];
  }
})()
  • 2.2高阶函数
// 高阶函数, 它接收一个方法作为参数, 返回一个该方法运用存储后的新方法。
var memoize = function (func) {
  var cache = {};
  return function () {
    var key = Array.prototype.slice.call(arguments).toString();
    return key in cache ? cache[key] : (cache[key] = func.apply(this, arguments));
  }
}
// ES6版本的memoize函数如下:
var memoizeES6 = function (func) {
  const cache = {};
  return (...args) => {
    const key = [...args].toString()
    return key in cache ? cache[key] : (cache[key] = func(...args));
  }
}
fibonacci = memoizeES6(fibonacci);
// GCD(最大公约数)
var gcd = memoize(
  function (a, b) {
    var t;
    if (a < b)
      t = b, b = a, a = t;
    while (b != 0)
      t = b, b = a % b, a = t;
    return a;
  })
gcd(27, 183)

var factorial = memoize(
  function (n) {
    return (n <= 1) ? 1 : n * factorial(n - 1);
  })
factorial(5); //=> 120
  • 2.3Math.pow实现
var myPow = function (x, n) {
  // !本题核心: 当n为奇数和偶数时两种情况的讨论
  // 情况1: (2,4) = (2,2) * (2,2)
  // 情况2:(2,5) = (2,2) * (2,2) * 2
  // 首先n是有可能为0,正,负数的
  if (n === 0) return 1;
  // 无论正负数我们都先将其转换为正数计算
  const res = dfs(x, Math.abs(n));

  if (n > 0) {
    return res;
  } else {
    return 1 / res;
  }

  function dfs(x, n) {
    // 递归的结束条件
    if (n === 1) {
      return x;
    }
    let temp = dfs(x, Math.floor(n / 2));
    return n % 2 ? (x * temp * temp) : (temp * temp)
  }
}
  • 2.4判断两个json数据单纯的数据数值相等
递归,精确数据类型获取比较,闭环等
let CompareUtil = {
  getType(obj) {
    return Object.prototype.toString.call(obj).slice(8, -1)
  },
  compareObj(obj1, obj2) {
    if (typeof (obj1) === 'object' && typeof (obj2) === 'object') {
      return this.compareJson(obj1, obj2)
    }
    else {
      console.log("传入数据类型不是JSON")
    }
  },
  compareJson(obj1, obj2) {
    try {
      if (this.getType(obj1) !== this.getType(obj2)) {
        return false
      }
      else if (this.getType(obj1) === 'Array') {
        return this.comArrayJson(obj1, obj2)
      }
      else if (this.getType(obj1) === 'Object') {
        return this.comObjectJson(obj1, obj2)
      }
      else if (this.getType(obj1) === "String") {
        return obj1 === obj2
      }
      else if (this.getType(obj1) === "Number") {
        return obj1 === obj2
      }
      else if (this.getType(obj1) === "Null") {
        return null === obj2
      }
      else if (this.getType(obj1) === "Function") {
        return obj1 + '' === obj2 + ''
      }
      else {
        return false
      }
    } catch (e) {
      return false
    }
  },
  comArrayJson(arr1, arr2) {
    let self = this
    if (arr1.length === arr2.length) {
      return arr1.every((item, index) => {
        return self.compareJson(item, arr2[index])
      })
    }
    else {
      return false
    }
  },
  comObjectJson(obj1, obj2) {
    let self = this
    if (Object.keys(obj1).join("") === Object.keys(obj2).join("")) {
      return Object.keys(obj1).every((item, index) => {
        return self.compareJson(obj1[item], obj2[item])
      })
    } else {
      return false
    }
  }
}

调用示例,引入闭环
console.log(CompareUtil.compareObj({}, {}))
let b = { a: 1 }
b.a = b
let a = { b: 1 }
a.b = a
console.log(CompareUtil.compareObj(a, b))
  • 2.5同步异步
请用程序表达王老板与[媳妇,小三,秘书]的旅途[[吃饭,睡觉,打豆豆],[吃饭,买电影票,看电影],[吃饭,散步,玩游戏]]生涯。注意:王老板同一时刻只能选一个人干一件事,过程需要花费一定的时间。

const sleep = (time) => new Promise((resolve) => void setTimeout(resolve, time)); // 模拟延迟

class HappyEvent {
  constructor(name, steps) {
    this.name = name;
    this.steps = steps;
  }
  async do(peoples) {
    const { steps } = this;
    const { length } = peoples;
    const peoplesStr = `${peoples.slice(0, length - 1).join("、")}${peoples[length - 1]
      }`;
    HappyEvent.log(`${peoplesStr}准备去${this.name}`);

    for (let i = 0; i < steps.length; i++) {
      const step = steps[i];
      HappyEvent.log(`${peoplesStr}${step.name}...`);
      await sleep(step.time * 1000); // 模拟延迟
      // await new Promise((reject) => {
      //   setTimeout(() => {
      //     reject();
      //   }, step.time * 1000);
      // });
    }
  }

  static log(info) {
    const date = new Date();
    console.log(`${date.getSeconds()}${info}`);
  }
}
class Happy {
  constructor(man, girls) {
    Object.assign(this, { man, girls });
  }
  async play(events) {
    HappyEvent.log("旅途开始!");
    const { man, girls } = this;
    for (let i = 0; i < girls.length; i++) {
      const girl = girls[i];
      const event = events[Math.floor(Math.random() * events.length)];
      // console.log(event);
      await event.do([man, girl]);
    }
    HappyEvent.log("旅途结束!");
  }
}
new Happy("张经理", ["张夫人", "李秘书", "小三"]).play([
  new HappyEvent("散步", [
    {
      name: "找饭店",
      time: 1,
    },
    {
      name: "吃饭",
      time: 5,
    },
    {
      name: "散步",
      time: 10,
    }]),
  new HappyEvent("打豆豆", [
    {
      name: "吃饭",
      time: 3,
    },
    {
      name: "睡觉",
      time: 1,
    },
    {
      name: "打豆豆",
      time: 8,
    }]),
  new HappyEvent("看电影", [
    {
      name: "找电影院",
      time: 3,
    },
    {
      name: "买电影票",
      time: 1,
    },
    {
      name: "看电影",
      time: 8,
    }]),
  new HappyEvent("玩游戏", [
    {
      name: "找酒店",
      time: 1,
    },
    {
      name: "开房",
      time: 1,
    },
    {
      name: "打豆豆",
      time: 10,
    }])
]);
  • 2.6洋葱模型
  class onionModel {
        // 洋葱模型
        middleware = [];
        constructor(obj) {
                this.ctx = obj || {};
        }
        use(fn) {
                this.middleware.push(fn);
        }
        start() {
                let fn = this.compose(this.middleware);
                return fn(this.ctx).then(this.respond);
        }
        respond() {
                console.log(`响应数据`);
        }
        dispatch(index, ctx) {
                let fn = this.middleware[index];
                if (!fn) return Promise.resolve();
                return fn(ctx, () => this.dispatch(index + 1, ctx));
        }
        compose(middleware) {
                let self = this;
                return function (ctx) {
                        return self.dispatch(0, ctx);
                };
        }
        // compose(middleware) {
        //   return function (ctx) {
        //     return dispatch(0);
        //     function dispatch(index) {
        //       let fn = middleware[index];
        //       if (!fn) return Promise.resolve();
        //       return fn(ctx, () => dispatch(index + 1))
        //     }
        //   }
        // }
      }
    /*
      const sleep=time=>new Promise((resolve,reject)=>{setTimeout(()=>{resolve()},time)})
      let onionModelCall = new onionModel()
      onionModelCall.use(async (ctx, next) => {
        console.log(ctx)
        console.log(`第一层 start`)
        await sleep(1000)
        await next()
        await sleep(1000)
        console.log(`第一层 end`)
      })
        
      onionModelCall.use(async (ctx, next) => {
        console.log(`第二层 start`)
        await sleep(2000)
        await next()
        await sleep(2000)
        console.log(`第二层 end`)
      })
        
      onionModelCall.use(async (ctx, next) => {
        console.log(`第三层 start`)
        await sleep(3000)
        await next()
        await sleep(3000)
        console.log(`第三层 end`)
      })
      //执行监听回调,模拟接受请求后的处理
      onionModelCall.start()
     */