手写

头像
Juanha
    阅读 19 分钟

    防抖&节流

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

    Juanha
    0 声望0 粉丝

    下一篇 »
    算法