常用函数手写整理

322 阅读4分钟

debounce/throttle

debounce一段时间内持续触发事件时,只执行最后一次

function debounce(fn, delay = 1000){
    let timer = null
    return function(...args){
        timer && clearTimeout(timer)
        timer = setTimeout(() => {
            fn(...args)
        }, delay)
    }
}

throttle一段时间内持续触发事件时,只执行第一次

function throttle(fn, delay = 1000){
    let flag = true
    return function(...args){
        if(flag){
            flag = false
            setTimeout(() => {
                flag = true
            }, delay)
        }
    }
}

call/apply/bind

三者都可以改变函数运行时的this指向

call()方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

function call(context, ...args){
    const ctx = context || window
    const func = Symbol()
    ctx[func] = this
    const res = ctx[func](...args)
    delete ctx[func]
    return res
}

apply()方法使用一个指定this值,以及一个数组(或者类数组)形式的参数

function apply(context, ...args){
    const ctx = context || window
    const func = Symbol()
    ctx[func] = this
    const res = ctx[func](...args)
    delete ctx[func]
    return res
}

bind()方法创建一个新的函数,在bind被调用时,这个新函数的this被指定为bind函数的第一个参数,而其余参数将作为新函数的参数。

function bind(context, ...args){
    const fn = this
    return function(...newArgs){
        fn.call(context, ...args, ...newArgs)
    }
}

new

new关键字会进行如下操作

  • 创建一个空的简单JavaScript对象(即{})
  • 链接该对象(设置该对象的constructor)到另一个对象
  • 将新创建的对象作为this上下文
  • 如果该函数没有返回值就返回this
function myNew(ctor, ...args){
    // 创建新对象
    const obj = Object.create()
    // 链接该对象
    obj.prototype = ctor.prototype
    // 设置上下文
    const res = ctor.call(obj, ...args)
    // 返回
    return res instanceof Object? res: obj
    
}

instanceOf

instanceOf 运算符用于检测构造函数的prototype是否出现在某个实例的原型链上

// 用法 object instanceof constructor
function C(){}
function D(){}
const cExp = new C()
const dExp = new D()
// true 因为Object.getPrototypeOf(cExp) === C.prototype
cExp instanceof C 
// false D.prototype不在cExp的原型上

// 实现
function myInstanceof(obj=实例, Cla=类){
    let leftPro = Object.getPrototypeOf(obj)
    const rightPro = Cla.prototype
    while(true){
        if(!leftPro) trun false
        if(leftPro === rightPro) return true
        leftPro = Object.getPrototypeOf(leftPro)
    }
}

Object.create

Object.create()方法创建一个新对象,使用现有的对象来提供新对象的__proto__

function myCreate(ctor){
    function C(){}
    C.prototype = ctor
    return new C()
}

Object.assign

Object.assign()方法用于将所有可枚举的属性从一个或多个源对象分配到目标对象,它将返回目标对象

function myAssign(target, args){
    const to = Object(target)
    for(let i = 1; i< args.length; i++){
        const nextSource = args[i]
        for(let nextKey in nextSource){
            if(Object.prototype.hasOwnProperty(nextSource,nextKey)){
                to[nextKey] = nextSource[nextKey]
            }
        }
    }
}

deepClone

function deepClone(target){
    if(target instanceOf RegExp) return new RegExp(target)
    if(target instanceOf Date) return new Date(target)
    if(target !== null || typeof target !== 'object') return target
    let ctor = new target.constructor()
    for(let key in target){
        if(target.hasOwnProperty()key){
           ctor = deepClone(target[key])
        }
    }
    return ctor
}

数组扁平化

function myFlat(data){
    return Array.reduce((prev,cur) => {
        return prev.concat(myFlat(cur))
    },[])
}

节点遍历

function traverse(node, nodeList = []){
    nodeList.push(node)
    const children = node.children
    for(let i = 0; i < children.length; i++){
        traverse(children[i], nodeList)
    }
    return nodeList
}

function traverse2(node){
    let nodeList = []
    nodeList.push(node)
    const children = node.children
    for(let i = 0;i < children.length; i++){
        nodeList = nodeList.concat(traverse2(children[i]))
    }
    return nodeList
}

function traverse3(node){
    let nodeList = []
    let stack = []
    stack.push(node)
    while(stack.length){
        const item = stack.pop()
        nodeList.push(item)
        const children = item.children
        for(let i = 0; i< children.length; i++){
            stack.push(children[i])
        }
    }
    return nodeList
}

function traverse4(node){
    let nodeList = []
    let stack = []
    stack.push(node)
    while(stack.length){
        const item = stack.shift()
        const children = item.children
        for(let i = 0; i < children.length; i++){
            stack.push(children[i])
        }
    }
    return nodeList
}

函数柯里化

function sum(a, b, c){
    return a + b + c
}
const sum = curry(sum)
sum(1)(2)(3) // 6
function curry(fn, ...args){
    const fnLen = fn.length
    const argsLen = args.length
    //如果参数不够就继续返回curry
    //若参数够了就执行函数
    if(fnLen > argsLen){
        return function(...args2){
            return curry(fn, ...args,...args2)
        }
    }else{
        fn(...args)
    }
}

JSONP

function JSONP(url, data){
    const callback = 'callback' + Math.random()
    const src = url + '?callback=' + callback
    const script = document.createElement('script')
    script.setAttribute('type', 'text/javascript')
    script.src = src
    return new Promise((resolve, reject) => {
        window[callback] = r => {
          resolve(r)
          headEle.removeChild(JSONP)
          delete window[callback]
        }
        headEle.appendChild(JSONP)
    })
}
JSONP().then(res => {
    //res就是我们要拿的数据
    console.log(res)
})

观察者模式

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新

class Subject{
    constructor(){
        this.name = 'JavaScript'
        this.observers = []
    }
    attach(obs){
        this.observers.push(obs)
    }
    detach(obs){
        this.observers.remove(obs)
    }
    notify(){
        this.observers.forEach(o => o.update(this.name))
    }
}
class Observer{
    update(){
        console.log('收到通知更新啦')
    }
}
const sub = new Subject()
const obsmm = new Observer()
const obsnn = new Observer()
sub.attach(obsmm)
sub.attach(obsnn)
sub.notify()

EventEmitter 事件侦听器

class EventEmitter{
    constructor(){
        this.events = {}
    }
    on(event, cb){
        const cbList = this.events[event] || []
        cbList.push(cb)
        this.events[event] = cbList
    }
    off(event, cb){
        const cbList = this.events[event] || []
        this.events[event] = cbList.filter(fn => fn !== cb)
        return this
    }
    emit(event, ...args){
        const cbList = this.events[event] || []
        cbList.forEach(fn => fn(...args))
        return this
    }
    this.once(event, cb){
        const fn = function(...args){
            cb(...args)
            this.off(event, fn)
        }
        this.on(event, fn)
    }
}

数组随机排序/生成随机数

let arr = [2,3,454,34,324,32]
arr.sort(randomSort)
function randomSort(a,b){
    return Math.random() > 0.5? -1: 1
}
// --------------------------------------
function getRandom(max, min){
    return Math.random() * (max - min) + min
}

sleep

sleep函数作用是让线程休眠,等到指定时间在重新唤起

function sleep(delay){
    const start = new Date().getTime()
    while(new Date().getTime() - start < delay){
        continue
    }
}
function test(){
    console.log('start')
    sleep(2000)
    console.log('end')
}
test()

对象数组去重

// 将value转成字符串,利用set去重
const arr = [{a:1,b:2,c:3},{b:2,c:3,a:1},{d:2,c:2}]
objSort(obj){
    let newObj = Object.create({})
    Object.keys(obj).forEach(key => {
        newObj[key] = obj[key]
    })
    return JSON.stringify(newObj)
}
function unique(arr){
    let set = new Set()
    for(let i = 0; i < arr.length; i++){
        set.add(objSort(arr[i]))
    }
    returm [...set].map(item => JSON.parse(item))
}

Promise

let id = 0
const PENDING = 0
const FULFILLED = 1
const REJECTED  = 2
class Promise{
    constructor(excutor){
        this.promiseId = ++id
        this.state = undefined
        this.result = undefined
        this.subscribers = []
        excutor(function resolvePromise(value){
            this.resolve(value)
        }, function rejectPromise(reason){
            this.reject(reason)
        })
    }
    resolve(value){
        if(this.state !== PENDING) return 
        this.state = FULFILLED
        this.result = value
        //异步任务执行的resolve需要执行一下收集的onFulfilled/onRejected方法(then里面的回调)
        if(this.subscribers.length){
            this.publish
        }
    },
    reject(reason){
        if(this.state !== PENDING) return
        this.state = REJECTED
        this.result = reason
    }
    then(onFulfilled,onRejected){
        const child = new Promise(function(){})
        //如果是同步任务执行的resolve或者reject那么此时state是有值的
        //那么需要执行then的回调,并给新的promise进行赋值
        //如果是异步任务执行resolve或者reject,那么将then的回调收集起来
        if(this.state){
            this.invokeCallback(this.state,child,onFulfilled,this.result)
        }else{
            this.subscribe(child,onFulfilled,onRejected)
        }
    }
    subscribe(promise, onFulfilled,onRejected){
        const length = this.subsceibers.length
        this.subscribers[length] = promise
        this.subscribers[length + FULFILLED] = onFulfilled
        this.subscribers[length + REJECTED] = onRejected
    }
    invokeCallback(state, promise, callback, result){
        let value = result
        if(getType(callback) === 'function'){
            value = callback()
        }
        // 给新生成的promise进行赋值
        promise.resolve(value)
    }
    publish(){
        for(let i=0;i<this.subscribers.length;i++){
            let child=this.subsctibers[i]
            let cb = this.subscribers[i + this.state]
            if(child){
                this.invokeCallback(this.state,child,cb,this.result)
            }else{
                cb(this.result)
            }
        }
    }
    
}
function getType(target){
    return Object.prototype.toString.call(target).slice(8,-1).toLowercase()
}