防抖&节流
// 防抖。使用场景:input搜索
function debounce(fn, delay) {
let timer = null
return function(...args) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, args)
}, delay)
}
}
// 节流。使用场景:滚动,resize,表单提交
function throttle(fn, delay) {
let flag = false
return function(...args) {
if (flag) return
flag = true
fn.apply(this, args)
setTimeout(() => {
flag = false
}, delay)
}
}
发布订阅
// 发布&订阅者
class EventEmitter {
constructor() {
this.events = {}
}
once = function(type, cb) {
const self = this
function temp(...args) {
cb.apply(null, args)
self.off(type, temp)
}
self.on(type, temp)
}
emit = function(type, ...args) {
const events = this.events[type]
events && events.forEach(cb => {
cb.call(null, ...args)
})
}
on = function(type, cb) {
if (this.events[type]) {
this.events[type].push(cb)
} else {
this.events[type] = [cb]
}
}
off = function(type, cb) {
if (this.events[type]) {
const index = this.events[type].indexOf(cb)
this.events[type].splice(index, 1)
}
}
}
Scheduler,控制并发次数
class Scheduler {
constructor() {
this.count = 2
this.queue = []
this.run = []
}
add = (task) => {
this.queue.push(task)
return this.scheduler()
}
scheduler = () => {
if (this.run.length < this.count && this.queue.length) {
const p = this.queue.shift()
const promise = p().then(res => {
this.run.splice(this.run.indexOf(promise), 1)
})
this.run.push(promise)
return promise
} else {
return Promise.race(this.run).then(this.scheduler)
}
}
}
const timeout = (time) => new Promise(resolve => {
setTimeout(resolve, time)
})
const scheduler = new Scheduler()
const addTask = (time, order) => {
scheduler.add(() => timeout(time)).then(() => console.log(order))
}
addTask(10000, '1')
addTask(5000, '2')
addTask(3000, '3')
addTask(4000, '4')
// 2,3,1,4
Promise
class MyPromise {
constructor(exector) {
this.status = 'pending' // pending | resolved | rejected
this.callbacks = []
this.data = ''
try {
exector(this.resolve, this.reject)
} catch (err) {
throw new Error(err)
}
}
resolve = (value) => {
if (this.status !== 'pending') {
return
}
this.status = 'resolved'
this.data = value
if (this.callbacks.length) {
setTimeout(() => {
this.callbacks.forEach(({ onFulfill }) => {
onFulfill(this.data)
})
})
}
}
reject = (value) => {
if (this.status !== 'pending') {
return
}
this.status = 'rejected'
this.data = value
if (this.callbacks.length) {
setTimeout(() => {
this.callbacks.forEach(({ onReject }) => {
onReject(this.data)
})
})
}
}
then = (resolvecb, rejectcb) => {
const onFulfill = typeof resolvecb === 'function' ? resolvecb : (value) => value
const onReject = typeof rejectcb === 'function' ? rejectcb : (reason) => { throw new Error(reason) }
return new MyPromise((resolve, reject) => {
const handle = (cb) => {
try {
const res = cb(this.data)
if (res instanceof MyPromise) {
res.then(resolve, reject)
} else {
resolve(res)
}
} catch (e) {
reject(e)
}
}
if (this.status === 'pending') {
this.callbacks.push({
onFulfill: () => handle(onFulfill),
onReject: () => handle(onReject)
})
} else if (this.status === 'resolved') {
setTimeout(() => {
handle(onFulfill)
})
} else {
setTimeout(() => {
handle(onReject)
})
}
})
}
catch = (cb) => {
return new MyPromise((resolve, reject) => {
this.then(undefined, reason => cb(reason))
})
}
}
MyPromise.resolve = function (val) {
return new MyPromise((resolve, reject) => {
if (val instanceof MyPromise) {
val.then(resolve, reject)
} else {
resolve(val)
}
})
}
MyPromise.reject = function (reason) {
return new MyPromise((resolve, reject) => {
if (reason instanceof MyPromise) {
reason.then(resolve, reject)
} else {
reject(reason)
}
})
}
MyPromise.allSettled = function (arr) {
const res = []
return new MyPromise((resolve, reject) => {
arr.forEach((p, index) => {
p.then(val => {
res.push({
type: 'resolve',
data: val
})
if (res.length === arr.length) {
resolve(res)
}
}, reason => {
res.push({
type: 'reject',
data: reason
})
if (res.length === arr.length) {
resolve(res)
}
})
})
})
}
promise 限制并发次数
function asyncPools(limit, arr, iteratorFn) {
let ret = []
let executing = [] // 进行中
let i = 0
const enqueue = function () {
if (i === arr.length) {
return Promise.resolve()
}
const item = arr[i++]
const p = iteratorFn(item)
ret.push(p)
executing.push(p)
p.then(() => executing.splice(executing.indexOf(p), 1))
if (executing.length >= limit) {
return Promise.race(executing).then(enqueue)
} else {
return Promise.resolve().then(() => enqueue())
}
}
return enqueue().then(() => {
console.log('ret', ret)
return Promise.all(ret)
})
}
const timeout = i => new Promise(resolve => {
console.log(i);
setTimeout(() => {
console.log('cb', i)
return resolve(i)
}, i)
});
asyncPools(3, [1000, 5000, 3500, 2000], timeout).then((res) => {
console.log('res', res)
})
LazyMan实现
new LazyMan('tony').eat('lunch').sleep(2000).eat('dinner').sleepFirst(3000)
class LazyMan {
constructor(name) {
this.name = name
this.queue = []
this.init()
}
init() {
const task = () => {
console.log('hi ' + this.name)
setTimeout(() => {
this.next()
})
}
this.queue.push(task)
setTimeout(() => {
this.next()
})
}
next() {
if (this.queue.length) {
const task = this.queue.shift()
task()
}
}
eat(something) {
const task = () => {
console.log('eat ' + something)
setTimeout(() => {
this.next()
})
}
this.queue.push(task)
return this
}
sleep(time) {
const task = () => {
console.log('wait' + time)
setTimeout(() => {
this.next()
}, time)
}
this.queue.push(task)
return this
}
sleepFirst(time) {
const task = () => {
console.log('wait first ' + time)
setTimeout(() => {
this.next()
}, time)
}
this.queue.unshift(task)
return this
}
}
fetchWithRetry 超时或异常重试
function fetchWithRetry(url, timeout, retryLimits = 0) {
const p1 = fetch(url)
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('fetch timeout'))
}, timeout);
})
return Promise.race([p1, p2]).catch(err => {
if (retryLimits > 0) {
return fetchWithRetry(url, timeout, --retryLimits)
}
})
}
Generator模拟实现async await
function* generatorFun() {
let a = yield Promise.resolve('A')
let b = yield Promise.resolve('B')
return a + b
}
function run(gen) {
return new Promise(function (resolve) {
// 执行Generator函数
let g = gen()
const next = (context) => {
let { done, value } = g.next(context)
if (done) {
// 完成返回
resolve(value)
} else {
// 未完成继续执行next函数,并传入本次执行结果
value.then(next)
}
}
next()
})
}
// run 为执行函数
run(generatorFun).then(res => console.log(res)) // AB
call/apply/bind/new/create
// call
Function.prototype.myCall = function(context, ...args) {
context.fn = this
if (typeof context.fn !== 'function') {
throw new Error('not function')
}
const res = context.fn(...args)
delete context.fn
return res
}
// apply
Function.prototype.myApply = function(context, args) {
context.fn = this
if (typeof context.fn !== 'function') {
throw new Error('not function')
}
const res = context.fn(...args)
delete context.fn
return res
}
// Object.create
function create(prototype, properties = {}) {
function F() {}
F.prototype = prototype
const obj = new F()
Object.defineProperties(obj, properties)
return obj
}
// bind
function.prototype.myBind = function (parentContext, ...args1) {
if (typeof this !== 'function') {
throw new Error('not function')
}
const fn = this
const fBound = function (...args2) {
const context = this instanceof fBound ? this : parentContext
return fn.myApply(context, args1.concat(args2))
}
// 访问原函数的原型上的方法,通过Object.create拷贝
fBound.prototype = create(fn.prototype)
return fBound
}
// new
function myNew(fn, ...args) {
var obj = create(fn.prototype)
const res = fn.myApply(obj, args)
return typeof res === 'object' ? res : obj
}
组合继承
function SuperType(name) {
this.name = name
this.colors = ['red']
}
SuperType.prototype.getName = function() {
console.log('getName', this.name)
}
function SubType(name, age) {
SuperType.call(this, name)
this.age = age
}
// 减少父类少调用一次
function inheritPrototype(subType, superType) {
let prototype = Object.create(superType.prototype)
prototype.constructor = subType
subType.prototype = prototype
}
inheritPrototype(SubType, SuperType)
// 扩展原型方法
SubType.prototype.getAge = function() {
console.log('getAge', this.age)
}
JSONP的实现
// 客户端
function jsonp({url, params, callback}) {
return new Promise((resolve, reject) => {
//创建script标签
let script = document.createElement('script');
//将回调函数挂在 window 上
window[callback] = function(data) {
resolve(data);
//代码执行后,删除插入的script标签
document.body.removeChild(script);
}
//回调函数加在请求地址上
params = {...params, callback} //wb=b&callback=show
let arrs = [];
for(let key in params) {
arrs.push(`${key}=${params[key]}`);
}
script.src = `${url}?${arrs.join('&')}`;
document.body.appendChild(script);
});
}
// 使用
function show(data) {
console.log(data);
}
jsonp({
url: 'http://localhost:3000/show',
params: {
//code
},
callback: 'show'
}).then(data => {
console.log(data);
});
// 服务端
//express启动一个后台服务
let express = require('express');
let app = express();
app.get('/show', (req, res) => {
let {callback} = req.query; //获取传来的callback函数名,callback是key
res.send(`${callback}('Hello!')`);
});
app.listen(3000);
柯里化&组合
// 柯里化
function curry(fn) {
return function next(...args) {
if (args.length < fn.length) {
return function(..._args) {
return next(...args.concat(..._args))
}
}
return fn(...args)
}
}
function add(a, b, c) {
return a + b + c
}
const newAdd = curry(add)
console.log(newAdd(1)(2)(3))
console.log(newAdd(1,2)(3))
console.log(newAdd(1)(2,3))
console.log(newAdd(1,2,3))
// 组合
function compose(...fns) {
return function(...args) {
return fns.reverse().reduce((acc, cur) => {
return typeof acc === 'function' ? cur(acc(...args)) : cur(acc)
})
}
}
function fn1(x){
return x + 1
}
function fn2(x){
return x * 10
}
function fn3(x){
return x - 1
}
var fn = compose(fn1, fn2, fn3)
console.log(fn(2))
redux
function createStore(reducer, prePayload) {
let currentState = prePayload;
let listeners = [];
function getState() {
return currentState;
}
function dispatch(action) {
currentState = reducer(currentState, action);
for(let i = 0; i < listeners.length; i++) {
listeners[i]();
}
}
function subscribe(func) {
let isSubscribed = false;
if (typeof func === 'function') {
if (!listeners.includes(func)) {
listeners.push(func);
isSubscribed = true;
}
}
return function unSubscribe() {
if (!isSubscribed) {
return;
}
let index = listeners.indexOf(func);
listeners.splice(index, 1);
}
}
dispatch({type: 'INIT'});
return {
getState,
dispatch,
subscribe,
}
}
export default function combineReducers(reducers) {
const reducerKeys = Object.keys(reducers);
return function combine(state = {}, action) {
let nextState = {};
let isChanged = false;
for(let i = 0; i < reducerKeys.length; i++) {
let key = reducerKeys[i];
let reducer = reducers[key];
let stateForKey = state[key];
nextState[key] = reducer(stateForKey, action);
isChanged = isChanged || nextState !== state;
}
return isChanged ? nextState : state;
}
}
// immer 简单实现
function immer(state, thunk) {
let copies = new Map(); // Map 的 key 可以是一个对象,非常适合用来缓存被修改的对象
const handler = {
get(target, prop) { // 增加一个 get 的劫持,返回一个 Proxy
return new Proxy(target[prop], handler);
},
set(target, prop, value) {
const copy = {...target}; // 浅拷贝
copy[prop] = value; // 给拷贝对象赋值
copies.set(target, copy);
}
};
function finalize(state) { // 增加一个 finalize 函数
const result = {...state};
Object.keys(state).map(key => { // 以此遍历 state 的 key
const copy = copies.get(state[key]);
if(copy) { // 如果有 copy 表示被修改过
result[key] = copy; // 就是用修改后的内容
} else {
result[key] = state[key]; // 否则还是保留原来的内容
}
});
return result;
}
const proxy = new Proxy(state, handler);
thunk(proxy);
return finalize(state);
}
hooks
const states = []
let index = 0
function useState(initState) {
const state = states[index] || initState
function dispatch(newState) {
states[index] = newState
render()
}
index++
return [state, dispatch]
}
function render() {
index = 0
ReactDOM.render( , document.getElementById('root'))
}
function useReducer(reducer, initState) {
function dispatch(action) {
states = reducer(states, action)
render()
}
states = states || initState
return [states, dispatch]
}
let memorizedState = []
function useEffect(callback, deps) {
if (!memorizedState[index]) {
memorizedState[index++] = deps
setTimeout(callback)
} else {
const lastDeps = memorizedState[index]
const hasChange = deps.every((item, index) => item == lastDeps[index])
if (hasChange) {
setTimeout(callback)
}
memorizedState[index++] = deps
}
}
function useCallback(callback, deps) {
if (!memorizedState[index]) {
memorizedState[index++] = deps
return callback
} else {
const [lastCallback, lastDeps] = memorizedState[index]
const hasChange = deps.every((item, index) => item == lastDeps[index])
if (hasChange) {
memorizedState[index++] = [callback, deps]
return callback
} else {
index++
return lastCallback
}
}
}
function useMemo(memoFn, deps) {
if (!memorizedState[index]) {
memorizedState[index++] = [memoFn(),deps]
return memoFn()
} else {
const [lastMemo, lastDeps] = memorizedState[index]
const hasChange = deps.every((item, index) => item == lastDeps[index])
if (hasChange) {
const newMemo = memoFn()
memorizedState[index++] = [newMemo, deps]
return newMemo
} else {
index++
return lastMemo
}
}
}
const useContext = context => {
return context._currentValue
}
let lastRef
const useRef = value => {
lastRef = lastRef || { current: value }
return lastRef
}
数字转中文
function num2Text(num) {
const NUMBERS = {
'0': '零',
'1': '一',
'2': '二',
'3': '三',
'4': '四',
'5': '五',
'6': '六',
'7': '七',
'8': '八',
'9': '九'
}
const UNITS = {10: '十', 100: '百', 1000: '千', 10000: '万', 100000: '十万',1000000: '百万', 10000000: '千万',10000000: '亿'}
let numText = []
let newNum = num
let singleNum = 0
let prevSingleNum = 0
let unitNum = 1
while(newNum) {
// 取得最后一位数字
singleNum = newNum % 10
// 判断单位
if (UNITS[unitNum]) {
numText.unshift(UNITS[unitNum])
}
// 处理多个零的情况
if (!(singleNum === 0 && prevSingleNum === 0)) {
numText.unshift(NUMBERS[singleNum])
}
unitNum *= 10
newNum = Math.floor(newNum / 10)
prevSingleNum = singleNum
}
res = numText.join('')
return res
}
拍平转数
const orgArr = [
{ id: '0', parentId: null},
{ id: '1', parentId: null},
{id: '1', parentId: '0'} ,
{id: '2', parentId: '1'},
{id: '3', parentId: '2'}
]
function flatternToTree(arr) {
const newArr = []
if (arr.length === 0) return []
for(let i = 0; i< arr.length; i++) {
const { id } = arr[i]
const obj = { id }
const children = orgArr.filter(item =>{
return item.parentId === id
})
obj.children = flatternToTree(children)
newArr.push(obj)
}
return newArr
}
function demo(arr) {
const newArr = arr.filter(item => item.parentId === null)
return flatternToTree(newArr)
}
// console.log(arr)
console.log(JSON.stringify(demo(orgArr)))
数组拍平
Array.prototype.flatten = function() {
var arr = this
return arr.reduce((acc, cur) => acc.concat(Array.isArray(cur) ? cur.flatten() : cur), [])
}
var arr = [1,2,3,[4,3,4, [2,3,33]]]
console.log(arr.flatten())
对象拍平
function flattenObj(input) {
if (!input || typeof input !== 'object') return input
const output = {}
function flatten(data, keyName) {
if (!data || typeof data !== 'object') {
output[keyName] = data
return
}
if (Array.isArray(data)) {
data.forEach((val, key) => {
flatten(val, keyName ? `${keyName}[${key}]` : key)
})
} else {
Object.keys(data).forEach(key => {
flatten(data[key], keyName ? `${keyName}.${key}` : key)
})
}
}
flatten(input, '')
return output
}
const input = {
a: 1,
b: "string",
c: null,
d: {
a: 2,
b: "string2",
c: false,
d: {
a: 3,
b: "string3",
c: [1, 2, 3]
},
e: ["de1", "de2", "de3"]
},
}
const output = {
a: 1,
b: "string",
c: true,
"d.a": 2,
"d.b": "string2",
"d.c": false,
"d.d.a": 3,
"d.d.b": "string3",
"d.d.c[0]": 1,
"d.d.c[1]": 2,
"d.d.c[2]": 3,
"d.e[0]": "de1",
"d.e[1]": "de2",
"d.e[2]": "de3",
}
console.log(flattenObj(input))
柯力化
function fn() {
let args = [...arguments]
function sum() {
return args.reduce((sum, cur) => sum + cur)
}
function next() {
args = args.concat([...arguments])
next.val = sum()
return next
}
next.val = sum()
return next
}
console.log(fn(1).val)
console.log(fn(1)(2)(3).val)
console.log(fn(10)(100)(1000).val)
sum(1, 2, 3).sumOf(); //6
sum(2, 3)(2).sumOf(); //7
sum(1)(2)(3)(4).sumOf(); //10
sum(2)(4, 1)(2).sumOf(); //9
function sum() {
let add = [...arguments]
let addr = function () {
add.push(...arguments)
return addr
}
addr.sumOf = function () {
return add.reduce((a, b) => a + b)
}
return addr
}
console.log(sum(1, 2, 3).sumOf())
深拷贝
function deepClone(obj) {
function getType(obj) {
const str = Object.prototype.toString.call(obj)
return str.replace(/\[|object|\]/g, '')
}
let newObj = {}
const type = getType(obj)
if (type === 'object') {
for(let item in obj) {
newObj[item] = deepClone(obj[item])
}
} else if(type === 'array') {
newObj = []
obj.forEach((item, index) => {
newObj[index] = deepClone(item)
})
} else {
newObj = obj
}
return newObj
}
console.log(deepClone({ a: 12,b:222,c: [{ a: 1, b:2}]}))
reducer实现map
Array.prototype.myMap = function (fn, thisarg) {
const result = []
const arr = this
arr.reduce((pre, cur, i, arr) => {
result.push(fn.call(this, cur, i, arr))
}, [])
return result
}
let a = [1, 2, 3, 5]
let b = a.myMap((item) => {
if (item > 2) return item
})
console.log(b)