JS方法手写系列

写这篇主要是想回顾一下之前手写的一些JS方法,巩固下JS,也做个记录;有些方法在我之前博客也有单独写过,比如手写Promise、基于发布订阅模式的简单JS事件、深拷贝、Vue2响应式原理等等,在这里也算是总结一下吧

1. 深拷贝 

之前的博客:js深拷贝 

function deepClone(source) {
  if(typeof source !== 'object') return source
  const target = source.constructor === Array ? [] : {}
  for(let key in source) {
    if(source.hasOwnProperty(key)) {
      if(source[key] && typeof source[key] === 'object') {
        target[key] = deepClone(source[key])
      } else {
        target[key] = source[key]
      }
    }
  }
  return target
}

2. 基于发布订阅的简单JS事件

之前博客:手写一个基于发布订阅模式的简单js事件 

class EventBus {
  constructor() {
    this.arrays = {}
  }
  // 订阅
  $on(type, fn) {
    this.arrays[type] = this.arrays[type] || []
    this.arrays[type].push(fn)
  }
  // 发布
  $emit(type, ...args) {
    if(!this.arrays[type]) return
    this.arrays[type].forEach(callback => {
      callback(...args)
    })
  }
  // 解绑
  $off(type, fn) {
    const args = Array.from(arguments)
    if(args.length === 0) {
      this.arrays = {}
    } else if(args.length === 1) {
      if(!this.arrays[type]) return
      this.arrays[type] = []
    } else if(args.length === 2) {
      if(!this.arrays[type]) return
      for(let i = this.arrays[type].length - 1; i >= 0; i--) {
        if(this.arrays[type][i] === fn) {
          this.arrays[type].splice(i, 1)
        }
      }
    }
  }
}

3. 观察者模式

class Observerd {
  constructor() {
    this.observerList = []
  }
  addObserver(observer) {
    this.observerList.push(observer)
  }
  notify() {
    this.observerList.forEach(observer => {
      observer.update()
    })
  }
}

class Observer {
  constructor(doSome) {
    this.doSome = doSome
  }
  update() {
    console.log(this.doSome)
  }
}

const obj1 = new Observer('观察者1')
const obj2 = new Observer('观察者2')

const objd = new Observerd()
objd.addObserver(obj1)
objd.addObserver(obj2)
objd.notify()

4. 手写Promise 

之前博客:手写Promise 

class MyPromise {
  static PENDING = 'pending'
  static FULFILLED = 'fulfilled'
  static REJECTED = 'rejected'
  constructor(fn) {
    this.status = MyPromise.PENDING
    this.result = null
    this.resolveCallbacks = []
    this.rejectCallbacks = []
    try {
      fn(this.resolve.bind(this), this.reject.bind(this))
    } catch(e) {
      this.reject(e)
    }
  }

  resolve(data) {
    setTimeout(() => {
      if(this.status === MyPromise.PENDING) {
        this.status = MyPromise.FULFILLED
        this.result = data
        if(this.resolveCallbacks.length) {
          this.resolveCallbacks.forEach(callback => {
            callback(data)
          })
        }
      }
    })
  }

  reject(err) {
    setTimeout(() => {
      if(this.status === MyPromise.PENDING) {
        this.status = MyPromise.REJECTED
        this.result = err
        if(this.rejectCallbacks.length) {
          this.rejectCallbacks.forEach(callback => {
            callback(err)
          })
        }
      }
    })
  }

  then(ONFULFILLED, ONREJECTED) {
    return new MyPromise((resolve, reject) => {
      ONFULFILLED = typeof ONFULFILLED === 'function' ? ONFULFILLED : () => {}
      ONREJECTED = typeof ONREJECTED === 'function' ? ONREJECTED : () => {}
      if(this.status === MyPromise.PENDING) {
        this.resolveCallbacks.push(ONFULFILLED)
        this.rejectCallbacks.push(ONREJECTED)
      }
      if(this.status === MyPromise.FULFILLED) {
        setTimeout(() => {
          ONFULFILLED(this.result)
        })
      }
      if(this.status === MyPromise.REJECTED) {
        setTimeout(() => {
          ONREJECTED(this.result)
        })
      }
    })
  }
}

5. Vue2的响应式核心 

之前博客: Vue2响应式原理 

const data = {
  name: 'wft',
  info: {
    age: 12
  },
  list: [1,2,3]
}

// 重写数组方法
const oldArrProto = Array.prototype
const newArrProto = Object.create(oldArrProto)
const methods = ['push', 'pop', 'shift', 'unshift', 'splice', 'reverse']
methods.forEach(method => {
  newArrProto[method] = function() {
    oldArrProto[method].call(this, ...arguments)
    console.log('更新视图操作----->>>')
  }
})

function observer(target) {
  if(typeof target !== 'object' || target === null) {
    return target
  }

  if(target.constructor === Array) {
    target.__proto__ = newArrProto
  }

  for(let key in target) {
    if(target.hasOwnProperty(key)) {
      defineReactive(target, key, target[key])
    }
  }
}

function defineReactive(target, key, value) {
  if(typeof value === 'object') {
    observer(value)
  }
  Object.defineProperty(target, key, {
    get() {
      return value
    },
    set(newVal) {
      if(typeof newVal === 'object') {
        observer(newVal)
      }
      if(newVal !== value) {
        value = newVal
        console.log('更新视图操作----->>>')
      }
    }
  })
}

6. 树形结构,根据ID找出它所属的父级集合 

之前博客: js树形结构,根据里层id找出它所属的每层父级集合 

class FindArrsById {
  constructor(id, tree) {
    this.id = id
    this.flatArr = this.flatTreeAndSetLevel.call(this, tree)
    this.parentAreas = this.getParentAreas.call(this, this.id, this.flatArr)
    this.getParentValuesByKey.bind(this)
  }

  flatTreeAndSetLevel(tree, level = 0) {
    const list = []
    tree.forEach(item => {
      const o = JSON.parse(JSON.stringify(item))
      if(o.children) delete o.children
      o.level = level
      list.push(o)
      if(item.children && item.children.length) {
        list.push(...this.flatTreeAndSetLevel(item.children, level + 1))
      }
    })
    return list
  }

  getParentAreas(pid, list) {
    const target = []
    let o = list.find(item => item.id == pid) || {}
    if(JSON.stringify(o) != '{}') target.push(o)
    if(o.parentId) target.push(...this.getParentAreas(o.parentId, list))
    return target
  }

  getParentValuesByKey(key) {
    return this.parentAreas.map(item => item[key]).reverse()
  }
}

7. 千分位数值转换 

function numberToCurrencyNo(value) {
  if (!value) return '0'
  if (value === '--') return '--'
  value = value - 0
  // 将数值截取,保留0位小数
  value = value.toFixed(0)
  // 获取整数部分
  const intPart = Math.trunc(value)
  // 整数部分处理,增加,
  const intPartFormat = intPart.toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,')
  // 预定义小数部分
  let floatPart = ''
  // 将数值截取为小数部分和整数部分
  const valueArray = value.toString().split('.')
  if (valueArray.length === 2) { // 有小数部分
    floatPart = valueArray[1].toString() // 取得小数部分
    return intPartFormat + '.' + floatPart
  }
  return intPartFormat + floatPart
}

console.log(numberToCurrencyNo(23423456))  //23,423,456

8. 数组去重 

之前博客:js数组去重 

export function noRepeatArr(list) {
  if (list.every(item => typeof item !== 'object')) {
    return [...new Set(list)]
  }
  return [...new Set(list.map(item => JSON.stringify(item)))].map(item => JSON.parse(item))
}

9. 手写一个字符串的trim()方法 

function myTrim(str) {
  if(typeof str !== 'string') return ''
  if(str.split('').every(str => str === ' ')) return ''
  let start = 0
  let end = str.length - 1
  for(let i = 0; i < str.length; i++) {
    if(str[i] !== ' ') {
      start = i
      break
    }
  }
  for(let i = str.length - 1; i >= 0; i--) {
    if(str[i] !== ' ') {
      end = i
      break
    }
  }
  return str.slice(start, end + 1)
}

10. uuid

uuid应该后端来生成 

function uuid() {
  const temp_url = URL.createObjectURL(new Blob())
  const uuid = temp_url.toString()
  URL.revokeObjectURL(temp_url) //释放这个url
  return uuid.substring(uuid.lastIndexOf('/') + 1)
}

11. 判断手机是Andoird还是IOS

// 1. ios
// 2. android
// 3. 其他
function getOSType() {
  let u = navigator.userAgent, app = navigator.appVersion;
 let isAndroid = u.indexOf('Android') > -1 || u.indexOf('Linux') > -1;
 let isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
 if (isIOS) {
   return 1;
 }
 if (isAndroid) {
   return 2;
 }
 return 3;
}

12. 手机号脱敏 

// 手机号脱敏
function hideMobile(mobile) {
  return mobile.replace(/^(\d{3})\d{4}(\d{4})$/, "$1****$2")
}

console.log(hideMobile('17812345678')) //178****5678

13. 防抖 

<input id="input" type="text">

<script>
  const $ = name => document.querySelector(name)

  // 防抖函数
  const antiShake = (() => {
    let timer = null
    return (callback, wait) => {
      if(timer) clearTimeout(timer)
      timer = setTimeout(callback, wait)
    }
  })()

  $('#input').addEventListener('keydown', antiShake.bind(this, getData, 1000))

  // 发送请求模拟函数
  function getData() {
    console.log('发送请求----->>>')
  }
</script>

14. 节流 

<input id="input" type="text">

<script>
  const $ = name => document.querySelector(name)

  // 节流 
  const throttle = (() => {
    let timer = null
    return (callback, wait) => {
      if(!timer) {
        callback()
        timer = setTimeout(() => {
          timer = null
        }, wait)
      }
    }
  })()

  $('#input').addEventListener('keydown', throttle.bind(this, getData, 1000))

  // 发送请求模拟函数
  function getData() {
    console.log('发送请求----->>>')
  }
</script>

补充一下 

有参数的情况下 

<script>
  // 防抖函数
  const debounce = (() => {
    let timer = null
    return function(callback, wait, ...args) {
      if(timer) clearTimeout(timer)
      timer = setTimeout(callback.bind(this, ...args), wait)
    }
  })()

  // 节流
  const throttle = (() => {
    let timer = null
    return function(callback, wait, ...args) {
      console.log(this, 'this----->>>')
      if(!timer) {
        callback.apply(this, args)
        timer = setTimeout(() => {
          timer = null
        }, wait)
      }
    }
  })()

  // 测试
  window.addEventListener('resize', throttle.bind(this, getData, 1000, '哈哈哈哈', '呵呵呵呵'))

  function getData(val1, val2) {
    console.log(val1, val2, '防抖/节流 处理 发送请求----->>>')
  }
</script>

15. 检验数据类型 

function getObjType(obj) {
  var toString = Object.prototype.toString
  var map = {
    '[object Boolean]': 'boolean',
    '[object Number]': 'number',
    '[object String]': 'string',
    '[object Function]': 'function',
    '[object Array]': 'array',
    '[object Date]': 'date',
    '[object RegExp]': 'regExp',
    '[object Undefined]': 'undefined',
    '[object Null]': 'null',
    '[object Object]': 'object'
  }
  if (obj instanceof Element) {
    return 'element'
  }
  return map[toString.call(obj)]
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会说法语的猪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值