123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 |
- /**
- * 返回一个自带节流效果的函数,用res表示。
- *
- * fn:需要被节流的函数
- * interval:最短多长时间允许执行一次fn
- *
- * 功能要点:
- * 1.fn代码里如有this,res被执行时,this会指向res的调用者;
- * 2.res被执行时的实参会映射到fn的形参;
- * 3.第一次调用res时,会立即执行fn。
- */
- export function throttle(fn, interval) {
- let lastRunTime = 0
- return function (...args) {
- let elapsedTime = Date.now() - lastRunTime
- if (elapsedTime < interval) {
- return
- }
- let context = this
- lastRunTime = Date.now()
- fn.apply(context, args)
- }
- }
- /**
- * 返回一个自带消抖效果的函数,下文用fnDebounced表示。
- *
- * fn: 需要被消抖的函数
- * delay: 消抖时长
- * isImmediateCall: 是否在一组操作中的第一次调用时立即执行fn
- * isRememberLastCall:是否在一组中最后一次调用后等delay时长再执行fn
- *
- * 如果isRememberLastCall为false,意味着fn不会被延迟执行,所以fnDebounced执行时,要么在内部调用fn,同步返回fn返回值;要么内部决定本次不调用fn,同步返回null。
- * 如果isRememberLastCall为true,意味着fn可能被延迟执行,所以fnDebounced会返回一个Promise,在fn被调用时用其返回值resolve该Promise,或者在fn的延时调用计划被取消时用'canceled'resolve该Promise。(不宜reject,否则又没有人去catch,会导致浏览器报错。)
- */
- export function debounce(fn, delay = 250, isImmediateCall = false, isRememberLastCall = true) {
- console.assert(isImmediateCall || isRememberLastCall, 'isImmediateCall 和 isRememberLastCall 至少应有一个是true,否则没有意义!')
- let timer = null
- let retPromiseLastTimeResolver = null
- // 上次调用的时刻
- let lastCallTime = 0
- if (isImmediateCall && !isRememberLastCall) {
- return function (...args) {
- let ret = null
- const currentTime = Date.now()
- if (currentTime - lastCallTime >= delay) {
- ret = fn.apply(this, args)
- }
- lastCallTime = currentTime
- return ret
- }
- } else if (!isImmediateCall && isRememberLastCall) {
- return function (...args) {
- if (timer) {
- clearTimeout(timer)
- timer = null
- }
- if (retPromiseLastTimeResolver) {
- retPromiseLastTimeResolver('canceled')
- retPromiseLastTimeResolver = null
- }
- const ret = new Promise((resolve, reject) => {
- retPromiseLastTimeResolver = resolve
- timer = setTimeout(() => {
- timer = null
- retPromiseLastTimeResolver = null
- resolve(fn.apply(this, args))
- }, delay)
- })
- return ret
- }
- } else if (isImmediateCall && isRememberLastCall) {
- return function (...args) {
- const currentTime = Date.now()
- if (currentTime - lastCallTime >= delay) { // 一组操作中的第一次
- const res = fn.apply(this, args)
- lastCallTime = currentTime
- return Promise.resolve(res)
- } else { // 一组中的后续调用
- if (timer) { // 在此之前存在中间调用
- lastCallTime = currentTime
- clearTimeout(timer)
- timer = null
- }
- if (retPromiseLastTimeResolver) {
- retPromiseLastTimeResolver('canceled')
- retPromiseLastTimeResolver = null
- }
- const ret = new Promise((resolve, reject) => {
- retPromiseLastTimeResolver = resolve
- timer = setTimeout(() => {
- lastCallTime = 0
- timer = null
- retPromiseLastTimeResolver = null
- resolve(fn.apply(this, args))
- }, delay)
- })
- return ret
- }
- }
- } else {
- console.error('不应该执行到这里!')
- }
- }
- /**
- 同时验证⼿机号码和固定电话号码(带区号或不带区号或带分机号)
- 规则说明:
- 1、可以是1开头的11位数字(⼿机号)
- 2、可以是“区号-电话号-分机号”或者是“(区号)电话号-分机号”格式
- 3、区号是0开头的3~4位数字,可以没有区号
- 4、电话号是5~8位数字,不能以0开头
- 5、分机号是1~8位数字,可以没有分机号
- 合法数据⽰例:
- ①13812341234
- ②010-12345678
- ③(0432)1234567-1234
- ④12345678
- */
- export function isValidPhoneNumber(value) {
- const reg = /^1\d{10}$|^400[0-9]{7}|^(0\d{2,3}-?|\(0\d{2,3}\))?[1-9]\d{4,7}(-\d{1,8})?$/
- // const reg = /^400[0-9]{7}|^1[34578]\d{9}$|^0[0-9]{2,3}-[0-9]{8}/
- return reg.test(value)
- }
- // 深拷贝,为了解决循环引用和共同引用的问题,引入了WeakMap,又因为引入WeakMap可能会导致被拷贝对象被挂上【作为WeakMap的探针的】匿名函数(是pollyfill的行为吧?),所以不会拷贝非根元素的匿名函数。
- export function deepClone(target, hash = new WeakMap()) {
- // 定义一个变量
- let result = null
- // 如果当前需要深拷贝的是一个对象的话
- if (typeof target === 'object') {
- if (hash.has(target)) { // 如果是循环引用
- result = hash.get(target)
- } else if (Array.isArray(target)) { // 如果是一个数组的话
- result = [] // 将result赋值为一个数组,并且执行遍历
- hash.set(target, result)
- for (let i in target) {
- if (!(typeof(target[i]) === 'function' && !target.name)) {
- // 递归克隆数组中的每一项
- result.push(deepClone(target[i], hash))
- }
- }
- // 判断如果当前的值是null的话;直接赋值为null
- } else if (target === null) {
- result = null
- // 判断如果当前的值是一个RegExp对象的话,直接赋值
- } else if (target.constructor === RegExp) {
- result = target
- } else {
- // 否则是普通对象,直接for in循环,递归赋值对象的所有值
- result = {}
- hash.set(target, result)
- for (let i in target) {
- if (!(typeof(target[i]) === 'function' && !target.name)) {
- result[i] = deepClone(target[i], hash)
- }
- }
- }
- } else if (typeof target === 'function') {
- result = target
- } else { // 如果不是对象也不是函数,直接赋值
- result = target
- }
- // 返回最终结果
- return result
- }
- export function isObjectBroad(p) {
- return typeof(p) === 'object' || typeof(p) === 'function'
- }
- // 判断两个对象内容是否相同。未考虑循环引用、共同引用的情况。
- export function isSameObject(object1, object2) {
- const keys1 = Object.keys(object1)
- const keys2 = Object.keys(object2)
-
- if (keys1.length !== keys2.length) {
- return false
- }
- for (let index = 0; index < keys1.length; index++) {
- const val1 = object1[keys1[index]]
- const val2 = object2[keys2[index]]
- const areObjects = isObjectBroad(val1) && isObjectBroad(val2)
- if (
- (areObjects && !isSameObject(val1, val2)) ||
- (!areObjects && (val1 !== val2))
- ) {
- return false
- }
- }
- return true
- }
- export function ossImagePreviewUrlSuffix(downScaleRate = 10) {
- return `?x-oss-process=image/resize,p_${downScaleRate}&${Math.random()}`
- }
- export function postOrderTraversal(root, routine) {
- if (root.children && Array.isArray(root.children)) {
- for (const child of root.children) {
- postOrderTraversal(child, routine)
- }
- }
- routine(root)
- }
- export function nodeIdList2nodeInfoListByNodeTree(nodeIdList, nodeTree) {
- console.assert(nodeIdList && nodeTree && nodeTree.id && nodeTree.name, 'nodeIdList2nodeInfoListByNodeTree: invalid param!')
- if (nodeIdList.length === 0) {
- return null
- }
- console.assert(nodeTree.id === nodeIdList[0], 'nodeIdList2nodeInfoListByNodeTree: 不可能的任务!')
- let ret = [
- {
- id: nodeTree.id,
- name: nodeTree.name,
- }
- ]
- if (nodeIdList[1] || nodeIdList[1] === 0) {
- console.assert(nodeTree.children && nodeTree.children.length > 0, 'nodeIdList2nodeInfoListByNodeTree: 不可能的任务2!')
- const nextLevelRoot = nodeTree.children.find((item) => {
- return item.id === nodeIdList[1]
- })
- console.assert(nextLevelRoot, 'nodeIdList2nodeInfoListByNodeTree: invalid param 2!')
- ret = ret.concat(nodeIdList2nodeInfoListByNodeTree(nodeIdList.slice(1, nodeIdList.length), nextLevelRoot))
- }
- return ret
- }
- export function capitalize(str) {
- if (!str) {
- return
- }
- if (str.length === 1) {
- return str[0].toUpperCase()
- }
- return str[0].toUpperCase() + str.slice(1, str.length)
- }
|