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(() => {})
  })
)

Layout Switch

Adjust the layout style of VitePress to adapt to different reading needs and screens.

Expand all
The sidebar and content area occupy the entire width of the screen.
Expand sidebar with adjustable values
Expand sidebar width and add a new slider for user to choose and customize their desired width of the maximum width of sidebar can go, but the content area width will remain the same.
Expand all with adjustable values
Expand sidebar width and add a new slider for user to choose and customize their desired width of the maximum width of sidebar can go, but the content area width will remain the same.
Original width
The original layout width of VitePress

Page Layout Max Width

Adjust the exact value of the page width of VitePress layout to adapt to different reading needs and screens.

Adjust the maximum width of the page layout
A ranged slider for user to choose and customize their desired width of the maximum width of the page layout can go.

Content Layout Max Width

Adjust the exact value of the document content width of VitePress layout to adapt to different reading needs and screens.

Adjust the maximum width of the content layout
A ranged slider for user to choose and customize their desired width of the maximum width of the content layout can go.

Spotlight

Highlight the line where the mouse is currently hovering in the content to optimize for users who may have reading and focusing difficulties.

ONOn
Turn on Spotlight.
OFFOff
Turn off Spotlight.