heycn / code-more Goto Github PK
View Code? Open in Web Editor NEW尽量去写更多的代码!
尽量去写更多的代码!
例如:url="https://alibaba.com?a=1&b=2&c=3#/abc/def?a=2&b=3&c=4"
const url = 'https://alibaba.com?a=1&b=2&c=3#/abc/def?a=2&b=3&c=4'
const getParamFromURL = (url, key) => {
const str = new RegExp(`/\?${key}=(.+)\#/`)
return (str.exec(url))[1][0]
}
getParamFromURL(url, 'c') // '3'
解决当前系统时间与服务器时间对比不准确问题
有一个需求,需要对服务器某个时间进行对比,并在页面实时展示,但是当用户修改系统时间时,就会导致结果不准确
需要服务端返回
对比时间
和服务器时间
,使用moment
进行对比,自定义secondToTime
和useInterval
用于格式化时间和计时
import React, { useEffect, useState, useRef } from 'react'
import moment from 'moment'
const secondToTime = second => {
if (!second) {
return 0 + _t('分钟')
} else if (second < 60) {
return second + _t('秒')
} else if (second >= 60 && second < 3600) {
let sec = second % 60
let min = (second - sec) / 60
if (sec == 0) {
return min + _t('分钟')
} else {
return _t('{{min}}分{{sec}}秒', {min, sec})
}
} else if (second >= 3600) {
let min = Math.floor(second / 60)
let hour = Math.floor(min / 60)
min = min % 60
if (min == 0) {
return hour + _t('小时')
}
return hour + _t('小时') + min + _t('分钟')
}
}
const useInterval = (callback, delay) => {
const savedCallback = useRef()
useEffect(() => {
savedCallback.current = callback
}, [callback])
useEffect(() => {
function tick() {
savedCallback.current()
}
if (delay !== null) {
let id = setInterval(tick, delay)
return () => clearInterval(id)
}
}, [delay])
}
const App = ({flagTime, serverTime})=> {
const [flagTime] = React.useState(flagTime)
const [serverTime, setServerTime] = React.useState(serverTime)
useInterval(() => {
let n = 0
setServerTime(moment(serverTime).add(n+=1, 's'))
}, 1000)
const runderTime = () => {
if (flagTime && serverTime) {
let diff = moment(serverTime).diff(moment(), 's')
if (diff > 30 || diff < -30) {
return secondToTime(moment(serverTime).diff(flagTime, 's'))
} else {
return secondToTime(moment().diff(flagTime, 's'))
}
} else {
return ''
}
}
return (
<div>{runderTime}</div>
)
}
这样子就可以尽可能地实时对比时间了
避免陷入闭包陷阱
import React, {useEffect, useState} from 'react'
const useCountDown = second => {
const [seconds, setSeconds] = useState(second)
useEffect(() => {
setTimeout(() => {
if (seconds > 0) {
setSeconds(seconds - 1)
}
}, 1000)
}, [seconds])
return [seconds, setSeconds]
}
const App = () => {
const [seconds, setSeconds] = useCountDown(0)
return (
<button disabled={seconds !== 0} onClick={() => setSeconds(60)}>
{seconds > 0 ? `${seconds}s后可点击` : '点击开始倒计时'}
</button>
)
}
j = n => n === 1 ? 1 : n * j(n-1)
fib = n =>
n === 0 ? 0 :
n === 1 ? 1 :
fib(n-1) + fib(n-2)
注意:每次进入函数需要往 调用栈
里「压栈」,调用栈
用来记录「回到哪」,如果需要记录得过多,就会「爆栈」。
所以,需要降低「压栈」或者计算次数,以下是优化方案
使用迭代代替递归
fib = n => fib_inner()
fib_inner = (start, end, prev1, prev2) =>
start === end ? prev1 + prev2
: fib_inner(start+1, end, prev1+prev2, prev1)
这就是「尾递归」
为什么尾递归可以优化?因为不用「压栈」了,「压栈」的目的是为了记录回到哪,那如果我不回来,那我就不需要「压栈」了。
注意:但是!JS 有 Bug,就算你不压栈,它还是会把一些不相关的操作压进去(例如环境
),所以,JS 不存在「尾递归优化」!
所以,以上代码,依然会「压栈」,虽然会快一些,但是没有什么区别。
所有的递归都可以改写成循环
fib_loop = n => {
let array = [0, 1]
for(let i = 0; i <= n-2; i++) {
array[i+2] = array[i+1] + array[i]
}
return array[array.length-1]
}
以上代码把结果都记录在数组里面,那么就不需要重复的计算某些值了。
以下是 ChatGPT
对以上代码的解释:
以上代码是一个计算斐波那契数列第 n 项的函数,采用了循环方式来计算。具体解释如下:
1. `fib_loop = n => {...}`:定义一个名为 `fib_loop` 的函数,它接受一个参数 `n`,使用箭头函数方式定义。
2. `let array = [0, 1]`:初始化一个数组 `array`,它包含斐波那契数列的初始两个元素。
3. `for(let i = 0; i <= n-2; i++) {...}`:循环遍历 `array` 数组,从第3项开始依次计算出斐波那契数列的每一项,直到计算出第n项。
4. `array[i+2] = array[i+1] + array[i]`:计算斐波那契数列的每一项,并存储在 `array` 数组中。
5. `return array[array.length-1]`:返回 `array` 数组的最后一项,也就是斐波那契数列的第 n 项。
使用循环就能解决,这样看起来递归很无用呀?是的,有时候递归就是这样。
因为 JS 在递归方面就没有提供很好的工具,所以在 JS 中写递归会让你的同事看不懂,并且性能又差,那怎么办呢?
使用「记忆化」 —— 这是一个很好的解决办法。
如果我曾经算过了,那就不要算了
可以减少重复计算,大大减低压栈次数
以下是 Lodash 记忆化函数的实现:lodash/memoize.js
LongPressable.tsx
import type { ReactNode, TouchEvent } from 'react'
import { useRef } from 'react'
type Props = {
children: ReactNode
onEnd?: () => void
}
export const LongPressable: React.FC<Props> = (props) => {
const { children, onEnd } = props
const touchTimer = useRef<number>()
const touchPosition = useRef<{ x?: number; y?: number }>({ x: undefined, y: undefined })
const onTouchStart = (e: TouchEvent) => {
touchTimer.current = window.setTimeout(() => {
onEnd?.()
}, 500)
const { clientX: x, clientY: y } = e.touches[0]
touchPosition.current = { x, y }
}
const onTouchMove = (e: TouchEvent) => {
const { clientX: newX, clientY: newY } = e.touches[0]
const { x, y } = touchPosition.current
if (x === undefined || y === undefined) { return }
const distance = Math.sqrt((newX - x) ** 2 + (newY - y) ** 2)
if (distance > 10) {
window.clearTimeout(touchTimer.current)
touchTimer.current = undefined
}
}
const onTouchEnd = (e: TouchEvent) => {
if (touchTimer.current) {
window.clearTimeout(touchTimer.current)
touchTimer.current = undefined
}
}
return (
<div onTouchStart={onTouchStart} onTouchMove={onTouchMove} onTouchEnd={onTouchEnd}>
{children}
</div>
)
}
<LongPressable onEnd={() => console.log(long pressable!)}>
<h1>long pressable me!</h1>
</LongPressable>
interface Data {
[k: string | number]: JSONValue
}
type Rule<T> = {
key: keyof T
message: string
} & (
{ type: 'required' } |
{ type: 'chinese' } |
{ type: 'equalField'; field: keyof T } |
{ type: 'pattern'; regex: RegExp } |
{ type: 'notEqual'; value: JSONValue } |
{ type: 'length'; min?: number; max?: number }
)
type Rules<T> = Rule<T>[]
type FormError<T> = {
[k in keyof T]?: string[]
}
export type { Rules, Rule, Data, FormError }
export const validate = <T extends Data>(formData: T, rules: Rules<T>): FormError<T> => {
const error: FormError<T> = {}
rules.forEach((rule) => {
const { key, type, message } = rule
const value = formData[key]
switch (type) {
case 'required':
if (isEmpty(value)) {
error[key] = error[key] ?? []
error[key]?.push(message)
}
break
case 'pattern':
if (!isEmpty(value) && !rule.regex.test(value!.toString())) {
error[key] = error[key] ?? []
error[key]?.push(message)
}
break
case 'notEqual':
if (!isEmpty(value) && value === rule.value) {
error[key] = error[key] ?? []
error[key]?.push(message)
}
break
case 'length':
if (!isEmpty(value)) {
if (rule.min && value!.toString().length < rule.min) {
error[key] = error[key] ?? []
error[key]?.push(message)
}
if (rule.max && value!.toString().length > rule.max) {
error[key] = error[key] ?? []
error[key]?.push(message)
}
}
break
case 'chinese':
if (!isEmpty(value) && !/^[\u4E00-\u9FA5]+$/.test(value!.toString())) {
error[key] = error[key] ?? []
error[key]?.push(message)
}
break
case 'equalField':
if (!isEmpty(value) && value !== formData[rule.field]) {
error[key] = error[key] ?? []
error[key]?.push(message)
}
break
default:
break
}
})
return error
}
function isEmpty(value: undefined | JSONValue | Data) {
return value === null
|| value === undefined
|| value === ''
|| (Array.isArray(value) && value.length === 0)
}
export function hasError(errors?: Record<string, string[]>) {
// return Object.values(errors)
// .reduce((result, value) => result + value.length, 0) > 0
if (!errors) { return false }
let result = false
for (const key in errors) {
if (errors[key]?.length > 0) {
result = true
break
}
}
return result
}
如果没有接受函数式的一整套理论,就没有必要用柯里化
柯里化 Currying:让所有函数只接受一个参数
我们把一个只接受一个参数的函数叫作「单参数函数」,基于这种函数,已经衍生出非常多的理论知识,而且是关于数学方面的,跟编程关系不大
如 λ演算
—— lambda演算
那么支持接受两个参数呢?
一般来说,在函数式编程中,不使用对象,而是使用闭包,因为 —— 对象是穷人的闭包
如果不理解上面这句话,可以看下我写过的一篇博客对象是穷人的闭包,闭包是穷人的对象
我们有闭包了,就不需要使用对象
const add = ({a, b}) => a + b
add({a: 1, b: 2})
上面这种方式在函数式编程中,特别的傻
const add = a => b => a + b
add(1)(2)
如果没见过上面这种写法,是不是看不太习惯?或者会疑问:闭包在哪里呢?
请继续往下看:
// 以上的代码,基本等价与下面的代码
add = function(a) {
return function(b) {
return a + b // 这个 a 就是和这个函数形成了闭包
}
}
add(1)(2)
但是如果只写过 JS 的人,会感觉有点不方便对不对?
没错,如果没有接受过函数式的一整套理论,你会觉得这就是在耍花招,没什么用
但是你如果接受过一系列函数式的东西,你就会觉得柯里化是非常重要的部分,如果没学过,那就没必要用柯里化
请把三参数函数 add(1,2,3)
变成 curriedAdd(1)(2)(3)
形式:
const curriedAdd =
a =>
b =>
c =>
add(a, b, c)
完毕,非常的简单。
假设:
currify
函数,使得他们分别接受 2、3、4 次参数,如:
currify(addTow)(1)(2) // 3
currify(addThree)(1)(2)(3) // 6
currify(addFour)(1)(2)(3)(4) // 10
也就是说,currify 函数能将任意接受固定个参数的函数,变成单一参数的函数
const currify = (fn, params = []) => {
return (arg) => {
const newParams = params.concat(arg)
if(newParams.length === fn.length) {
return fn(...newParams)
} else {
return currify(fn, newParams)
}
}
}
const addTwo = (a, b) => a + b
我想支持 currify(addFour)(1)(2,3)(4) // 10
那么需要这么改写
const currify = (fn, params = []) => {
- return (arg) => {
+ return (...args) => {
- const newParams = params.concat(arg)
- if(newParams.length === fn.length) {
+ if(params.length + args.length === fn.length) {
- return fn(...newParams)
+ return fn(...params, ...args)
} else {
- return currify(fn, newParams)
+ return currify(fn, [...params, ...args])
}
}
}
const currify = (fn, params = []) =>
(...args) =>
params.length + args.length === fn.length
? fn(...params, ...args)
: currify(fn, [...params, ...args])
这样,是不是对「柯里化函数」有一点点理解了呢?
useSwipe.tsx
import type { RefObject } from 'react'
import { useEffect, useRef, useState } from 'react'
interface Config {
onTouchStart?: (e: TouchEvent) => void
onTouchMove?: (e: TouchEvent) => void
onTouchEnd?: (e: TouchEvent) => void
}
export const useSwipe = (elementRef: RefObject<HTMLElement>, config?: Config) => {
const [direction, setDirection] = useState<'' | 'left' | 'right'>('')
const startX = useRef(-1)
const onTouchStart = (e: TouchEvent) => {
config?.onTouchStart?.(e)
startX.current = e.touches[0].clientX
}
const onTouchMove = (e: TouchEvent) => {
config?.onTouchMove?.(e)
const newX = e.touches[0].clientX
const distance = newX - startX.current
if (Math.abs(distance) < 3) setDirection('')
if (distance > 0) setDirection('right')
if (distance < 0) setDirection('left')
}
const onTouchEnd = (e: TouchEvent) => {
config?.onTouchEnd?.(e)
setDirection('')
}
useEffect(() => {
if (!elementRef.current) return
elementRef.current.addEventListener('touchstart', onTouchStart)
elementRef.current.addEventListener('touchmove', onTouchMove)
elementRef.current.addEventListener('touchend', onTouchEnd)
return () => {
if (!elementRef.current) return
elementRef.current.removeEventListener('touchstart', onTouchStart)
elementRef.current.removeEventListener('touchmove', onTouchMove)
elementRef.current.removeEventListener('touchend', onTouchEnd)
}
}, [])
return direction
}
使用:
const swipeDiv = useRef<HTMLDivElement>(null)
const direction = useSwipe(swipeDiv, { onTouchStart: e => e.preventDefault() })
const createPopover = text => {
if (!text) return null
const popoverWrapper = document.createElement('div')
const popover = document.createElement('div')
popoverWrapper.classList.add('popoverWrapper')
popover.innerHTML = text
popover.classList.add('popover')
popover.style.maxWidth = '240px'
popoverWrapper.appendChild(popover)
return popoverWrapper
}
// 当 DOMContentLoaded 事件触发时执行的回调函数
document.addEventListener('DOMContentLoaded', () => {
// 获取所有具有 data-popover-text 属性的元素,并遍历每个元素
const elementsWithPopover = document.querySelectorAll('[data-popover-text]')
elementsWithPopover.forEach(element => {
// 获取 element 元素的 data-popover-text 属性值,并赋值给 popoverText 变量
const popoverText = element.dataset.popoverText
// 创建一个包含指定文本内容的 popover 元素,并赋值给 popover 变量
const popover = createPopover(popoverText)
// 如果 popover 存在,则执行以下代码
if (popover) {
// 隐藏 popover
popover.style.opacity = '0'
popover.style.pointerEvents = 'none'
// 将 popover 添加到 element 的父元素中
const parent = element.parentElement
parent.classList.add('relative') // 自动添加相对定位
parent.style.cursor = 'pointer' // 自动添加鼠标指针样式
parent.appendChild(popover)
// 当鼠标移入 element 元素时,显示 popover,并计算 popover 的位置
element.addEventListener('mouseenter', () => {
popover.style.opacity = '1'
popover.style.transition = 'opacity 0.35s'
popover.style.pointerEvents = 'auto'
const { top, left, height } = element.getBoundingClientRect()
const popoverHeight = popover.offsetHeight
const newTop = top + window.pageYOffset - (popoverHeight - height) / 2
const newLeft = left - popover.offsetWidth
popover.style.top = `${newTop}px`
popover.style.left = `${newLeft - 12}px`
})
// 当鼠标移出 element 元素时,隐藏 popover
element.addEventListener('mouseleave', () => {
popover.style.opacity = '0'
popover.style.transition = 'opacity 0.35s'
popover.style.pointerEvents = 'none'
})
}
})
})
在 HTML 里使用:
<aside>
<p data-popover-text="This is popover content...">Hover Me!</p>
</aside>
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.