手写

防抖&节流

// 防抖。使用场景: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)

你可能感兴趣的:(前端javascript)