Skip to content

常用代码片段

杂记

收集开发中的一些常用代码片段

防抖

ts
/**
 * @description 函数防抖
 * @param {Function} method 延时调用函数
 * @param {Number} wait 延迟时长
 * @param {Boolean} immediate 立即执行选项
 */
export const Ddebounce = <T extends (...args: any[]) => any>(
  method: T,
  wait: number,
  immediate: boolean
) => {
  if (typeof method !== 'function') {
    throw new TypeError('Expected a function')
  }
  let timeout: ReturnType<typeof setTimeout> | null = null
  // Ddebounce函数为返回值
  // 使用Async/Await处理异步,如果函数异步执行,等待setTimeout执行完,拿到原函数返回值后将其返回
  // args为返回函数调用时传入的参数,传给method
  let Ddebounce = async (...args: Parameters<T>): Promise<ReturnType<T>> => {
    return new Promise((resolve) => {
      // 用于记录原函数执行结果
      let result: ReturnType<T>
      // 将method执行时this的指向设为 debounce 返回的函数被调用时的this指向
      let context = this
      // 如果存在定时器则将其清除
      if (timeout) {
        clearTimeout(timeout)
      }
      // 立即执行需要两个条件,一是immediate为true,二是timeout未被赋值或被置为null
      if (immediate) {
        // 如果定时器不存在,则立即执行,并设置一个定时器,wait毫秒后将定时器置为null
        // 这样确保立即执行后wait毫秒内不会被再次触发
        let callNow = !timeout
        timeout = setTimeout(() => {
          timeout = null
        }, wait)
        // 如果满足上述两个条件,则立即执行并记录其执行结果
        if (callNow) {
          result = method.apply(context, args)
          resolve(result)
        }
      } else {
        // 如果immediate为false,则等待函数执行并记录其执行结果
        // 并将Promise状态置为fullfilled,以使函数继续执行
        timeout = setTimeout(() => {
          // args是一个数组,所以使用fn.apply
          // 也可写作method.call(context, ...args)
          result = method.apply(context, args)
          resolve(result)
        }, wait)
      }
    })
  }

  // 在返回的 Ddebounce 函数上添加取消方法
  // Ddebounce.cancel = function () {
  //   clearTimeout(timeout)
  //   timeout = null
  // }
  return Ddebounce
}
javascript
const run = function () {
  console.log("i'm run")
}
window.addEventListener('resize', Ddebounce(run, 2000, false))

节流

ts
/**
 * 节流
 * @param func 回调函数
 * @param delay 延迟时间
 */
export const Dthrottle = <T extends (...args: any[]) => void>(func: T, delay: number) => {
  let timer: ReturnType<typeof setTimeout> | null = null

  return function (this: ThisParameterType<T>, ...args: Parameters<T>) {
    const context = this

    if (!timer) {
      timer = setTimeout(function () {
        func.apply(context, args)
        timer = null
      }, delay)
    }
  }
}
javascript
// 假设有一个需要被节流的函数
function expensiveOperation() {
  console.log('Running expensive operation...')
  // 这里可以是一些耗时较长的操作
}

// 使用 throttle 函数对 expensiveOperation 进行节流
const throttledFunc = throttle(expensiveOperation, 1000)

// 模拟频繁调用 expensiveOperation 函数
setInterval(function () {
  throttledFunc()
}, 500)

柯里化

ts
function selfCurryFn<T extends Function>(fn: T): T {
  const fnLen = fn.length
  function curry(...args: any[]): any {
    const argLen = args.length
    if (argLen < fnLen) {
      return function otherCurry(...args2: any[]): any {
        return curry(...args.concat(args2))
      }
    } else {
      return fn(...args)
    }
  }
  return curry as T
}
ts
// 示例用法:
function sum(a: number, b: number, c: number): number {
  return a + b + c
}

const curriedSum = selfCurryFn(sum)

console.log(curriedSum(1)(2)(3))
console.log(curriedSum(1, 2)(3))
console.log(curriedSum(1)(2, 3))
console.log(curriedSum(1, 2, 3))

拷贝

typescript
function deepClone<T>(obj: T, clonedMap = new WeakMap()): T {
  // 检查是否已经拷贝过该对象,避免循环引用导致的无限递归
  if (clonedMap.has(obj)) {
    return clonedMap.get(obj) as T
  }

  let newObj: T

  // 特殊类型的处理
  if (obj instanceof Date) {
    newObj = new Date(obj.getTime()) as T
  } else if (obj instanceof RegExp) {
    newObj = new RegExp(obj) as T
  } else if (typeof obj === 'function') {
    // 自定义需要特殊处理的函数类型
    // newObj = obj.bind(null) as T;
    newObj = obj as T
  } else if (Array.isArray(obj)) {
    newObj = [] as unknown as T
  } else if (obj && typeof obj === 'object') {
    newObj = {} as T
  } else {
    return obj
  }

  // 将当前对象添加到已拷贝的对象 Map 中
  clonedMap.set(obj, newObj)

  // 遍历对象的属性进行深拷贝
  for (let key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      newObj[key] = deepClone(obj[key], clonedMap)
    }
  }

  return newObj
}
javascript
// 基本类型
const num = 123
const str = 'hello'
const bool = true

console.log(deepClone(num)) // 123
console.log(deepClone(str)) // 'hello'
console.log(deepClone(bool)) // true

// 数组
const arr = [1, 2, [3, 4], { a: 5 }]
const clonedArr = deepClone(arr)

console.log(clonedArr) // [1, 2, [3, 4], { a: 5 }]
console.log(arr === clonedArr) // false

clonedArr[0] = 100
console.log(clonedArr) // [100, 2, [3, 4], { a: 5 }]
console.log(arr) // [1, 2, [3, 4], { a: 5 }]

clonedArr[2][0] = 300
console.log(clonedArr) // [100, 2, [300, 4], { a: 5 }]
console.log(arr) // [1, 2, [300, 4], { a: 5 }]

// 对象
const obj = { x: 1, y: { z: 2 } }
const clonedObj = deepClone(obj)

console.log(clonedObj) // { x: 1, y: { z: 2 } }
console.log(obj === clonedObj) // false

clonedObj.x = 100
console.log(clonedObj) // { x: 100, y: { z: 2 } }
console.log(obj) // { x: 1, y: { z: 2 } }

clonedObj.y.z = 200
console.log(clonedObj) // { x: 100, y: { z: 200 } }
console.log(obj) // { x: 1, y: { z: 200 } }

// 循环引用
const circularObj = { prop: null }
circularObj.prop = circularObj

const clonedCircularObj = deepClone(circularObj)
console.log(clonedCircularObj) // { prop: [Circular] }
console.log(clonedCircularObj.prop === clonedCircularObj) // true

手写 call、apply 及 bind 函数

javascript
Function.prototype.Dcall = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  var context = context || window
  context.say = this
  var args = []
  for (let i = 1; i < arguments.length; i++) {
    args.push('arguments[' + i + ']')
  }
  var result = eval('context.say(' + args + ')')
  delete context.say
  return result
}
javascript
Function.prototype.Dapply = function (context, arr) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  var context = context || window
  var result
  context.fn = this
  if (!arr) {
    result = context.fn()
  } else {
    var args = []
    for (let i = 0; i < arr.length; i++) {
      args.push('arr[' + i + ']')
    }
    result = eval('context.fn(' + args + ')')
  }
  return result
}
javascript
Function.prototype.Dbind = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  var self = this
  var args = Array.prototype.slice.call(arguments, 1)
  return function () {
    var bindArgs = Array.prototype.slice.call(arguments)
    return self.apply(context, args.concat(bindArgs))
  }
}
javascript
// 示例函数
function greet() {
  console.log(`Hello, ${this.name}!`)
}

// 1. 使用 Dcall 方法调用函数,并指定上下文对象和参数
const person1 = { name: 'Alice' }
greet.Dcall(person1) // 输出:Hello, Alice!

// 2. 使用 Dapply 方法调用函数,并指定上下文对象和参数数组
const person2 = { name: 'Bob' }
const args = ['Nice to meet you!']
greet.Dapply(person2, args) // 输出:Hello, Bob!

// 3. 使用 Dbind 方法创建新函数,预先绑定上下文和参数
const person3 = { name: 'Charlie' }
const greetPerson3 = greet.Dbind(person3)
greetPerson3() // 输出:Hello, Charlie!

new

javascript
Function.prototype.Dnew = function (fn, ...args) {
  var obj = Object.create(fn.prototype)
  var ret = fn.apply(obj, args)
  return ret instanceof Object ? ret : obj
}

// 或
Function.prototype.DDnew = function () {
  var obj = {}
  var Constructor = Array.prototype.shift.call(arguments)
  obj.__proto__ = Constructor.prototype
  var result = Constructor.call(obj, arguments)
  return result instanceof Object ? result : obj
}

instanceof

javascript
{
  function new_instance_of(leftVaule, rightVaule) {
    let rightProto = rightVaule.prototype
    leftVaule = leftVaule.__proto__
    while (true) {
      if (leftVaule === null) return false
      if (leftVaule === rightProto) return true
      leftVaule = leftVaule.__proto__
    }
  }
}
{
  function new_instance_of(leftVaule, rightVaule) {
    let proto = Object.getPrototypeOf(leftVaule)
    while (true) {
      if (proto == null) return false
      if (proto === rightVaule.prototype) return true
      proto = Object.getPrototypeOf(proto)
    }
  }
}

解析 URL Params 为对象

ts
function parseParam(url: string): Record<string, any> | null {
  const paramsMatch = /.+\?(.+)$/.exec(url)
  if (!paramsMatch) {
    return null // 处理无效的 URL
  }

  const paramsStr = paramsMatch[1]
  const paramsArr = paramsStr.split('&')
  const paramsMap = new Map<string, any>()

  for (const param of paramsArr) {
    if (/=/.test(param)) {
      const [key, val] = param.split('=')
      const decodedVal = decodeURIComponent(val)
      let parsedVal: string | number | boolean = decodedVal

      if (/^\d+$/.test(decodedVal)) {
        parsedVal = parseFloat(decodedVal)
      } else if (decodedVal === 'true') {
        parsedVal = true
      } else if (decodedVal === 'false') {
        parsedVal = false
      }

      if (paramsMap.has(key)) {
        const existingVal = paramsMap.get(key)
        if (Array.isArray(existingVal)) {
          paramsMap.set(key, [...existingVal, parsedVal])
        } else {
          paramsMap.set(key, [existingVal, parsedVal])
        }
      } else {
        paramsMap.set(key, parsedVal)
      }
    } else {
      paramsMap.set(param, true)
    }
  }

  const paramsObj: Record<string, any> = {}
  for (const [key, value] of paramsMap.entries()) {
    paramsObj[key] = value
  }

  return paramsObj
}
js
const url = 'https://example.com/?name=wudi&age=27&interests=programming&interests=music'
const parsedParams = parseParam(url)

console.log(parsedParams)
// Output: { name: "wudi", age: 27, interests: ["programming", "music"] }

获取URL的中参数

ts
export function getQueryString(param: string): string {
  const queryParams = new URLSearchParams(window.location.search)
  const value = queryParams.get(param)
  return value || ''
}
js
// 示例用法:
const paramName = 'name'
const paramValue = getQueryString(paramName)

console.log(paramValue)
// Output: 根据实际 URL 查询参数的值,如果存在则输出相应的值,否则输出空字符串 ''

时间格式化

ts
type FormatToken =
  | 'YY'
  | 'YYYY'
  | 'M'
  | 'MM'
  | 'D'
  | 'DD'
  | 'H'
  | 'HH'
  | 'I'
  | 'II'
  | 'S'
  | 'SS'
  | 'W'
  | 'WW'

const formatMap: Map<FormatToken, (date: Date) => string> = new Map([
  ['YY', (date: Date) => ('' + date.getFullYear()).substr(2)],
  ['YYYY', (date: Date) => date.getFullYear().toString()],
  ['M', (date: Date) => (date.getMonth() + 1).toString()],
  ['MM', (date: Date) => (date.getMonth() + 1).toString().padStart(2, '0')],
  ['D', (date: Date) => date.getDate().toString()],
  ['DD', (date: Date) => date.getDate().toString().padStart(2, '0')],
  ['H', (date: Date) => date.getHours().toString()],
  ['HH', (date: Date) => date.getHours().toString().padStart(2, '0')],
  ['I', (date: Date) => date.getMinutes().toString()],
  ['II', (date: Date) => date.getMinutes().toString().padStart(2, '0')],
  ['S', (date: Date) => date.getSeconds().toString()],
  ['SS', (date: Date) => date.getSeconds().toString().padStart(2, '0')],
  [
    'W',
    (date: Date) => `星期${['日', '一', '二', '三', '四', '五', '六'][date.getDay()]}`,
  ],
  [
    'WW',
    (date: Date) =>
      ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][
        date.getDay()
      ],
  ],
])

// format: yyyy/mm/dd hh:ii:ss(2019/07/24 09:45:43) yy/m/d hh:ii:ss(19/07/24 09:45:43) yyyy/mm/dd w(2019/07/24 星期三) mm/dd/yyyy(07/24/2019)
export function formatTime(
  time?: string | number,
  format: string = 'yyyy-mm-dd'
): string {
  const date = time ? new Date(time) : new Date()

  return format.replace(
    /(YY){1,2}|M{1,2}|D{1,2}|H{1,2}|I{1,2}|S{1,2}|W{1,2}/gi,
    (token) => {
      const formatter = formatMap.get(token as FormatToken)
      return formatter ? formatter(date) : token
    }
  )
}
js
// 示例用法:
const currentTime = Date.now()
const formattedTime = formatTime(currentTime, 'yyyy-mm-dd HH:II:SS')
console.log(formattedTime)
// Output: 根据当前时间生成的格式化后的时间字符串,如 "2023-08-25 09:30:15"
// iOS 解析 YYYY-MM-DD HH:mm:ss 这种日期格式会报错 Invalid Date
// 从代码习惯上, 建议使用 /

日期格式化

js
function getNowFormatDate() {
  return new Date().toLocaleString() + '.' + new Date().getMilliseconds()
}

数组扁平化

javascript
/**
 * @method 数组扁平化
 * @param {*} arry
 */
export const deepFlatten = (arr) => {
  return arr
    .toString()
    .split(',')
    .map((item) => +item)
  // or
  // return [].concat(...arr.map(item => (Array.isArray(item) ? deepFlatten(item) : item)));
}

判断类型

ts
export function getType(obj: any): string {
  const objectType = Object.prototype.toString.call(obj)
  const matchResult = objectType.match(/\s([a-zA-Z]+)/)

  if (matchResult) {
    return matchResult[1].toLowerCase()
  }

  throw new Error('Failed to determine object type')
}
js
// 示例用法:
const examples = [
  123,
  'Hello',
  true,
  [],
  {},
  null,
  undefined,
  new Date(),
  function () {},
  /regex/,
  Symbol('symbol'),
]

examples.forEach((example, index) => {
  const type = getType(example)
  console.log(`Example ${index + 1}:`, example)
  console.log(`Type:`, type)
  console.log('--------------------')
})

// Example 1: 123
// Type: number
// --------------------
// Example 2: Hello
// Type: string
// --------------------
// Example 3: true
// Type: boolean
// --------------------
// Example 4: []
// Type: array
// --------------------
// Example 5: {}
// Type: object
// --------------------
// Example 6: null
// Type: null
// --------------------
// Example 7: undefined
// Type: undefined
// --------------------
// Example 8: Fri Aug 25 2023 14:14:21 GMT+0800 (中国标准时间)
// Type: date
// --------------------
// Example 9: ƒ () {}
// Type: function
// --------------------
// Example 10: /regex/
// Type: regexp
// --------------------
// Example 11: Symbol(symbol)
// Type: symbol
// --------------------

html 转义

javascript
export const escapeHtml = (str) => {
  if (!str) return
  str = str.replace(/&/g, '&amp;')
  str = str.replace(/</g, '&lt;')
  str = str.replace(/>/g, '&gt;')
  str = str.replace(/“/g, '&quto;')
  str = str.replace(/'/g, '&#39;')
  return str
}

等待函数

ts
export function wait(milliseconds: number): Promise<void> {
  return new Promise((resolve) => setTimeout(resolve, milliseconds))
}
js
// Usage example:
async function delayedAction() {
  console.log('Start execution')
  await wait(2000)
  console.log('Executed after a delay of 2 seconds')
}

delayedAction()
// Output:
// Start execution
// Executed after a delay of 2 seconds

对象判空

注:永远不要使用构造函数创建对象

ts
function isEmpty1(obj: Record<string, any>): boolean {
  return JSON.stringify(obj) === '{}'
}

function isEmpty2(obj: Record<string, any>): boolean {
  return Object.keys(obj).length === 0
}

function isEmpty3(obj: Record<string, any>): boolean {
  return Object.getOwnPropertyNames(obj).length === 0
}

function isEmpty4(obj: Record<string, any>): boolean {
  let flag = true
  for (const key in obj) {
    if (key) {
      flag = false
      break
    }
  }
  return flag
}

function isEmpty5(obj: Record<string, any>): boolean {
  return Reflect.ownKeys(obj).length === 0 && obj.constructor === Object
}

function isEmpty6(obj: Record<string, any>): boolean {
  return (
    Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj)).length === 0
  )
}

function isEmpty7(value: any): boolean {
  return value && Object.keys(value).length === 0 && value.constructor === Object
}

function isEmpty8(value: any): boolean {
  return (
    Object.prototype.toString.call(value) === '[object Object]' &&
    JSON.stringify(value) === '{}'
  )
}

const key = Symbol('key')
const obj: Record<symbol, string> = {
  [key]: 'value',
}

console.log('[1], obj is empty:', isEmpty1(obj))
console.log('[2], obj is empty:', isEmpty2(obj))
console.log('[3], obj is empty:', isEmpty3(obj))
console.log('[4], obj is empty:', isEmpty4(obj))
console.log('[5], obj is empty:', isEmpty5(obj))
console.log('[6], obj is empty:', isEmpty6(obj))
console.log('[7], obj is empty:', isEmpty7(obj))
console.log('[8], obj is empty:', isEmpty8(obj))

// [1], obj is empty: true
// [2], obj is empty: true
// [3], obj is empty: true
// [4], obj is empty: true
// [5], obj is empty: false
// [6], obj is empty: false
// [7], obj is empty: true
// [8], obj is empty: true
js
// Lodash
_.isEmpty({})
js
// jQuery
jQuery.isEmptyObject({})

检查函数是否是promise

javascript
// 可先看vue源码实现(不严谨,但是够用)
function isDef(v) {
  return v !== undefined && v !== null
}

function isPromise(fn) {
  return isDef(fn) && typeof fn.then === 'function' && typeof fn.catch === 'function'
}

console.log(isPromise(new Promise())) // true

// graphql-js 的实现方式
function isPromise_graphql(fn) {
  return Boolean(fn && typeof fn.then === 'function')
}

// 利用 instanceof
function isPromise_instanceof(fn) {
  return fn instanceof Promise
}

// 利用 prototype
function isPromise_prototype(fn) {
  return fn && Object.prototype.toString.call(fn) === '[object Promise]'
}

检查函数是否是一个生成器

javascript
const test = function* () {
  yield undefined
}

console.log(x.constructor.name === 'GeneratorFunction') // true

// 或者

const GeneratorFunction = function* () {
  yield undefined
}.constructor

console.log(test instanceof GeneratorFunction) // true

// 需要注意
foo = test.bind(bar)
console.log(foo instanceof GeneratorFunction) // false

判断浏览器运行环境

ts
/**
 * 判断浏览器运行环境
 */
export const BrowserType = () => {
  // 权重:系统 + 系统版本 > 平台 > 内核 + 载体 + 内核版本 + 载体版本 > 外壳 + 外壳版本
  const ua = navigator.userAgent.toLowerCase()
  // const ua = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3872.0 Safari/537.36 Edg/78.0.244.0'.toLowerCase();
  // const ua = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3314.0 Safari/537.36 SE 2.X MetaSr 1.0'.toLowerCase();
  const testUa = (regexp: RegExp) => regexp.test(ua)
  const testVs = (regexp: RegExp) =>
    ua
      .match(regexp)
      ?.toString()
      .replace(/[^0-9|_.]/g, '')
      .replace(/_/g, '.') ?? ''
  // 系统
  let system = 'unknow'
  if (testUa(/windows|win32|win64|wow32|wow64/g)) {
    system = 'windows' // windows系统
  } else if (testUa(/macintosh|macintel/g)) {
    system = 'macos' // macos系统
  } else if (testUa(/x11/g)) {
    system = 'linux' // linux系统
  } else if (testUa(/android|adr/g)) {
    system = 'android' // android系统
  } else if (testUa(/ios|iphone|ipad|ipod|iwatch/g)) {
    system = 'ios' // ios系统
  }
  // 系统版本
  let systemVs = 'unknow'
  if (system === 'windows') {
    if (testUa(/windows nt 5.0|windows 2000/g)) {
      systemVs = '2000'
    } else if (testUa(/windows nt 5.1|windows xp/g)) {
      systemVs = 'xp'
    } else if (testUa(/windows nt 5.2|windows 2003/g)) {
      systemVs = '2003'
    } else if (testUa(/windows nt 6.0|windows vista/g)) {
      systemVs = 'vista'
    } else if (testUa(/windows nt 6.1|windows 7/g)) {
      systemVs = '7'
    } else if (testUa(/windows nt 6.2|windows 8/g)) {
      systemVs = '8'
    } else if (testUa(/windows nt 6.3|windows 8.1/g)) {
      systemVs = '8.1'
    } else if (testUa(/windows nt 10.0|windows 10/g)) {
      systemVs = '10'
    }
  } else if (system === 'macos') {
    systemVs = testVs(/os x [\d._]+/g)
  } else if (system === 'android') {
    systemVs = testVs(/android [\d._]+/g)
  } else if (system === 'ios') {
    systemVs = testVs(/os [\d._]+/g)
  }
  // 平台
  let platform = 'unknow'
  if (system === 'windows' || system === 'macos' || system === 'linux') {
    platform = 'desktop' // 桌面端
  } else if (system === 'android' || system === 'ios' || testUa(/mobile/g)) {
    platform = 'mobile' // 移动端
  }
  // 内核和载体
  let engine = 'unknow'
  let supporter = 'unknow'
  if (testUa(/applewebkit/g)) {
    engine = 'webkit' // webkit内核
    if (testUa(/edg/g)) {
      supporter = 'edge' // edge浏览器
    } else if (testUa(/opr/g)) {
      supporter = 'opera' // opera浏览器
    } else if (testUa(/chrome/g)) {
      supporter = 'chrome' // chrome浏览器
    } else if (testUa(/safari/g)) {
      supporter = 'safari' // safari浏览器
    }
  } else if (testUa(/gecko/g) && testUa(/firefox/g)) {
    engine = 'gecko' // gecko内核
    supporter = 'firefox' // firefox浏览器
  } else if (testUa(/presto/g)) {
    engine = 'presto' // presto内核
    supporter = 'opera' // opera浏览器
  } else if (testUa(/trident|compatible|msie/g)) {
    engine = 'trident' // trident内核
    supporter = 'iexplore' // iexplore浏览器
  }
  // 内核版本
  let engineVs = 'unknow'
  if (engine === 'webkit') {
    engineVs = testVs(/applewebkit\/[\d._]+/g)
  } else if (engine === 'gecko') {
    engineVs = testVs(/gecko\/[\d._]+/g)
  } else if (engine === 'presto') {
    engineVs = testVs(/presto\/[\d._]+/g)
  } else if (engine === 'trident') {
    engineVs = testVs(/trident\/[\d._]+/g)
  }
  // 载体版本
  let supporterVs = 'unknow'
  if (supporter === 'chrome') {
    supporterVs = testVs(/chrome\/[\d._]+/g)
  } else if (supporter === 'safari') {
    supporterVs = testVs(/version\/[\d._]+/g)
  } else if (supporter === 'firefox') {
    supporterVs = testVs(/firefox\/[\d._]+/g)
  } else if (supporter === 'opera') {
    supporterVs = testVs(/opr\/[\d._]+/g)
  } else if (supporter === 'iexplore') {
    supporterVs = testVs(/(msie [\d._]+)|(rv:[\d._]+)/g)
  } else if (supporter === 'edge') {
    supporterVs = testVs(/edg\/[\d._]+/g)
  }
  // 外壳和外壳版本
  let shell = ''
  let shellVs = ''
  if (testUa(/micromessenger/g)) {
    shell = 'wechat' // 微信浏览器
    shellVs = testVs(/micromessenger\/[\d._]+/g)
  } else if (testUa(/qqbrowser/g)) {
    shell = 'qq' // QQ浏览器
    shellVs = testVs(/qqbrowser\/[\d._]+/g)
  } else if (testUa(/ucbrowser/g)) {
    shell = 'uc' // UC浏览器
    shellVs = testVs(/ucbrowser\/[\d._]+/g)
  } else if (testUa(/qihu 360se/g)) {
    shell = '360' // 360浏览器(无版本)
  } else if (testUa(/2345explorer/g)) {
    shell = '2345' // 2345浏览器
    shellVs = testVs(/2345explorer\/[\d._]+/g)
  } else if (testUa(/metasr/g)) {
    shell = 'sougou' // 搜狗浏览器(无版本)
  } else if (testUa(/lbbrowser/g)) {
    shell = 'liebao' // 猎豹浏览器(无版本)
  } else if (testUa(/maxthon/g)) {
    shell = 'maxthon' // 遨游浏览器
    shellVs = testVs(/maxthon\/[\d._]+/g)
  }
  return Object.assign(
    {
      /** webkit gecko presto trident */
      engine,
      engineVs,
      /** desktop mobile */
      platform,
      /** chrome safari firefox opera iexplore edge */
      supporter,
      supporterVs,
      /** windows macos linux android ios */
      system,
      systemVs,
    },
    shell === ''
      ? {}
      : {
          /** wechat qq uc 360 2345 sougou liebao maxthon */
          shell,
          shellVs,
        }
  )
}

function handleUpdateBrowser() {
  const { supporter, shell = '' } = BrowserType()
  // Chrome: https://www.google.cn/intl/zh-CN/chrome/
  // Microsoft Edge: https://www.microsoft.com/zh-cn/edge
  // QQ: https://browser.qq.com/
  // 搜狗浏览器: https://ie.sogou.com/
  // IE: https://www.microsoft.com/zh-cn/edge
  // Firefox: http://www.firefox.com.cn/
  // Safari: https://support.apple.com/zh-cn/HT204416
  // Opera: https://www.opera.com/zh-cn
  // MiuiBrowser: https://www.google.cn/intl/zh-CN/chrome/
  // 其它浏览器: https://www.google.cn/intl/zh-CN/chrome/
  // chrome safari firefox opera iexplore edge qq sougou
  if (supporter || ['qq', 'sougou'].includes(shell)) {
    switch (supporter) {
      case 'chrome':
        shell === 'sougou'
          ? window.open('https://ie.sogou.com/')
          : window.open('https://www.google.cn/intl/zh-CN/chrome/')
        break
      case 'safari':
        window.open('https://support.apple.com/zh-cn/HT204416')
        break
      case 'firefox':
        window.open('http://www.firefox.com.cn/')
        break
      case 'opera':
        window.open('https://www.opera.com/zh-cn')
        break
      case 'edge':
      case 'iexplore':
        window.open('https://www.microsoft.com/zh-cn/edge')
        break
      default:
        shell === 'qq'
          ? window.open('https://browser.qq.com/')
          : shell === 'sougou'
          ? window.open('https://ie.sogou.com/')
          : window.open('https://www.google.cn/intl/zh-CN/chrome/')
        break
    }
  } else {
    window.open('https://www.google.cn/intl/zh-CN/chrome/')
  }
}

生成 -0

javascript
// one

// 首先创建一个8位的ArrayBuffer
const buffer = new ArrayBuffer(8)
// 创建DataView对象操作buffer
const dataView = new DataView(buffer)
// 将第1个字节设置为0x80,即最高位为1
dataView.setUint8(0, 0x80)
// 将buffer内容当做Float64类型返回
console.log(dataView.getFloat64(0)) // -0

// two
console.log(0 * -1) // -0

H5(ios)-在点击输入框,出现键盘后,弹出层被顶上去,而光标还停留在原处,即出现错位情况

javascript
{
  // 单个input(textarea)
  $('input.van-field__control, textarea.van-field__control').blur(function () {
    setTimeout(function () {
      var currentPosition = document.documentElement.scrollTop || document.body.scrollTop
      window.scrollTo(0, currentPosition) //页面向上滚动
    }, 200)
  })

  // 多个input(textarea)
  $(function () {
    var setTimerTop = 0
    $(document)
      .on('blur', 'input.van-field__control, textarea.van-field__control', function () {
        event.preventDefault()
        setTimerTop = setTimeout(function () {
          window.scrollBy(0, 5) // 页面向上滚动
          window.scrollBy(0, -5)
        }, 500)
      })
      .on('focus', 'input.van-field__control, textarea.van-field__control', function () {
        clearTimeout(setTimerTop)
      })
  })

  // 多个input(textarea)-iframe情况
  $(function () {
    var setTimerTop = 0
    $(document)
      .on('blur', 'input.van-field__control, textarea.van-field__control', function () {
        event.preventDefault()
        setTimerTop = setTimeout(function () {
          parent.scrollBy(0, 5) // 页面向上滚动
          parent.scrollBy(0, -5)
          $('#hide-area-cb').focus()
        }, 500)
      })
      .on('focus', 'input.van-field__control, textarea.van-field__control', function () {
        clearTimeout(setTimerTop)
      })
      .on('focus', 'input.van-field__control[disabled]', function () {
        setTimerTop = setTimeout(function () {
          parent.scrollBy(0, 5) // 页面向上滚动
          parent.scrollBy(0, -5)
        }, 500)
      })
  })
}

实现数字金额转大写金额

ts
export function convertToChineseAmount(amount: number): string {
  const chineseNumbers: string[] = [
    '',
    '壹',
    '贰',
    '叁',
    '肆',
    '伍',
    '陆',
    '柒',
    '捌',
    '玖',
  ]
  const chineseUnits: string[] = ['', '拾', '佰', '仟', '万', '亿', '兆']

  if (amount === 0) {
    return '零元'
  }

  let integerPart = Math.floor(amount)
  let decimalPart = Math.round((amount - integerPart) * 100)

  const integerPartStr = convertIntegerPart(integerPart, chineseNumbers, chineseUnits)
  const decimalPartStr = convertDecimalPart(decimalPart, chineseNumbers)

  let result = ''

  if (integerPartStr !== '') {
    result += integerPartStr + '元'
  }

  if (decimalPartStr !== '') {
    result += decimalPartStr
  } else {
    result += '整'
  }

  return result
}

function convertIntegerPart(
  amount: number,
  chineseNumbers: string[],
  chineseUnits: string[]
): string {
  let result = ''
  let unitIndex = 0
  let isFirstDigitZero = true

  while (amount > 0) {
    const digit = amount % 10
    if (digit !== 0) {
      const number = chineseNumbers[digit]
      const unit = chineseUnits[unitIndex % 7]
      result = number + unit + result
      isFirstDigitZero = false
    } else {
      if (!isFirstDigitZero) {
        result = '零' + result
      }
    }

    unitIndex++
    amount = Math.floor(amount / 10)
  }

  return result
}

function convertDecimalPart(amount: number, chineseNumbers: string[]): string {
  if (amount === 0) {
    return ''
  }

  const digit1 = Math.floor(amount / 10)
  const digit2 = amount % 10

  let result = ''

  if (digit1 !== 0) {
    result += chineseNumbers[digit1] + '角'
  }

  if (digit2 !== 0) {
    result += chineseNumbers[digit2] + '分'
  }

  return result
}
js
// 示例用法:
const amount1 = 123456789.12
const amount2 = 1000.01
const amount3 = 0.5

console.log(convertToChineseAmount(amount1)) // Output: "壹亿贰千叁百肆拾伍万陆仟柒佰捌拾玖元壹角贰分"
console.log(convertToChineseAmount(amount2)) // Output: "壹仟元零壹分"
console.log(convertToChineseAmount(amount3)) // Output: "伍角"

平均数

javascript
// 平均数
const average = (...nums) => nums.reduce((acc, val) => acc + val, 0) / nums.length
console.log(average(...[1, 2, 3])) // 2
console.log(average(1, 2, 3)) // 2

平滑滚动至顶部

javascript
const scrollToTop = () => {
  const c = document.documentElement.scrollTop || document.body.scrollTop
  if (c > 0) {
    window.requestAnimationFrame(scrollToTop)
    window.scrollTo(0, c - c / 8)
  }
}

滚动到指定元素区域

javascript
const smoothScroll = (element) =>
  document.querySelector(element).scrollIntoView({
    behavior: 'smooth',
  })
smoothScroll('#fooBar')

npm 获取版本号

javascript
import axios from 'axios'

async function getPackageVersion(packageName, versionRange = '') {
  const registryUrl = 'https://registry.npmjs.org'
  const encodedPackageName = encodeURIComponent(packageName).replace(/^%40/, '@')
  const url = `${registryUrl}/${encodedPackageName}/${versionRange}`

  try {
    const response = await axios.get(url)
    return response.data.version
  } catch (error) {
    console.error(`Failed to fetch package version for '${packageName}':`, error)
    throw error
  }
}

;(async () => {
  try {
    const packageName = 'axios'
    const versionRange = 'latest' // ^0.21.4

    const packageVersion = await getPackageVersion(packageName, versionRange)
    console.log(
      `Version of ${packageName} satisfying the range "${versionRange}": ${packageVersion}`
    )
  } catch (error) {
    console.log('🚀 ~ file: getPackageVersion ~ error:', error)
  }
})()

判断函数是否 async

js
function isAsyncFunction(func) {
  if (typeof func !== 'function') {
    throw new Error('Invalid argument: func must be a function')
  }

  return func[Symbol.toStringTag] === 'AsyncFunction'
}
js
console.log(
  'function ',
  isAsyncFunction(() => {})
)
console.log(
  'async function ',
  isAsyncFunction(async () => {})
)
console.log(
  'function Promise ',
  isAsyncFunction(() => {
    return new Promise(() => {})
  })
)