JS手写系列,持续更新中。。。

59 阅读1分钟

防抖

function debounce(fn, time = 3000) {
    let timer = null
    return (...args) => {
        if (timer) clearTimeout(timer)
        timer = setTimeout(() => {
            fn.call(undefined, ...args)
            timer = null
        }, time)
    }
}

节流

function throttle(fn, time = 3000) {
    let timer = null
    return (...args) => {
        if (timer) return
        timer = setTimeout(() => {
            fn.call(undefined, ...args)
            timer = null
        }, time)
    }
}

发布订阅

const eventHub = {
    map: {},
    on: (name, fn) => {
        eventHub.map[name] = eventHub.map[name] || []
        eventHub.map[name].push(fn)
    },
    emit: (name, data) => {
        const q = eventHub.map[name]
        if (!q) return
        q.map(f => f.call(undefined, data))
    },
    off: (name, fn) => {
        const q = eventHub.map[name]
        if (!q) return
        const index = q.index(fn)
        if (index < 0) return
        q.splice(index, 1)
    }
}

eventHub.on('click', console.log)
eventHub.on('click', console.error)

setTimeout(() => {
    eventHub.emit('click', '小刺猬fy')
}, 2000)

还可以用class实现:

class EventHub {
    map = {}
    on(name, fn) {
        eventHub.map[name] = eventHub.map[name] || []
        eventHub.map[name].push(fn)
    }
    emit(name, data) {
        const q = eventHub.map[name]
        if (!q) return
        q.map(f => f.call(undefined, data))
    }
    off(name, fn) {
        const q = eventHub.map[name]
        if (!q) return
        const index = q.index(fn)
        if (index < 0) return
        q.splice(index, 1)
    }
}

const eventHub = new EventHub()

eventHub.on('click', console.log)
eventHub.on('click', console.error)

setTimeout(() => {
    eventHub.emit('click', '小刺猬fy')
}, 2000)

AJAX

const ajax = (methods, url, data, success, fail) => {
    let request = new XMLHttpRequest()
    request.open(methods, url)
    request.onreadystatechange = function () {
        if (request.readyState === 4) {
            if (request.status >= 200 && request.status < 300 || request.status === 304) {
                success(request)
            } else {
                fail(request)
            }
        }
    }

    request.send(data)
}

数组去重

这个答案有很多,以下例举3个解法:

// 1. 使用 Set(面试已经禁止这种了,因为太简单)
var uniq = function (arr = []) {
    return Array.from(new Set(arr))
}

// 2. 使用计数排序的思路,缺点是只支持字符串
var uniq = function (arr = []) {
    var map = {}
    for (let i = 0; i < arr.length; i++) {
        let num = arr[i]
        if (!map[num]) {
            map[num] = true
        }
    }
    return [...Object.keys(map)]
}

// 3. 使用 Map,缺点是兼容性差了一点
var uniq = function (arr = []) {
    var map = new Map()
    for (let i = 0; i < arr.length; i++) {
        let num = arr[i]
        if (!map.has[num]) {
            map.set(num, true)
        }
    }
    return [...map.keys()]
}

深拷贝

  1. 用JSON
const b = JSON.parse(JSON.stringify(a))

这个方法有如下缺点:

  • 不支持正则、undefined、Date、函数等数据;
  • 不支持引用,即环状结构。 所以有了方法2.
  1. 用递归
function deepClone(source, cache) {
    if (!cache) {
        cache = new Map()
    }
    if (source instanceof Object) {
        if (cache.get(source)) return cache.get(source) // 检查环
        let result
        if (a instanceof Function) { // 判断类型
            if (a.prototype) {
                result = function () {
                    return source.apply(this, arguments)
                }
            } else {
                result = (...args) => {
                    return source.call(undefined, ...args)
                }
            }
        } else if (source instanceof Array) { // 判断类型
            result = []
        } else if (source instanceof Date) { // 判断类型
            result = new Date(source - 0)
        } else if (source instanceof RegExp) { // 判断类型
            result = new RegExp(source.source, source.flags)
        } else {
            result = {}
        }
        cache.set(source, result)
        for (let key in source) {
            if (source.hasOwnProperty(key)) { // 不拷贝原型上的属性
                result[key] = deepClone(source[key], cache) // 递归
            }
        }
        return result
    }
    return source
}

// 以下为测试代码
const a = {
    number: 1,
    bool: false,
    str: 'hi',
    empty1: undefined,
    empty2: null,
    array: [{
            name: 'frank',
            age: 18
        },
        {
            name: 'jacky',
            age: 19
        }
    ],
    date: new Date(2000, 0, 1, 20, 30, 0),
    regex: /\.(j|t)sx/i,
    obj: {
        name: 'frank',
        age: 18
    },
    f1: (a, b) => a + b,
    f2: function (a, b) {
        return a + b
    }
}
a.self = a
const b = deepClone(a)
b.self === b // true
b.self = 'hi'
a.self !== 'hi' //true

要点: 1. 递归 2. 判断类型 3. 检查环 4. 不拷贝原型上的属性