Javascript函数实现专题

140 阅读2分钟

概要

这篇专题旨在模拟实现 Javascript 语言中原生提供的或者某些库封装的实用函数如:call、apply、bind、map、fiter 等等

实现 call 方法

// 实现 call 和 apply 方法的关键在于将方法本身作为属性传递给指定的 this,并在指定的 this 上手动调用方法本身
Function.prototype.call2 = function(arg, ...args) {
    const context = arg || window

    context._fn_call = this

    const result = context._fn_call(...args)

    delete context._fn_call

    return result
}

模拟实现 apply 方法

// 实现 call 和 apply 方法的关键在于将方法本身作为属性传递给指定的
Function.prototype.apply2 = function(arg, args = []) {
    const context = arg || window

    context._fn_apply = this

    const result = context._fn_apply(...args)

    delete context._fn_apply

    return result
}

实现 bind 方法

Function.prototype.bind2 = function (arg, ...args) {
    const context = arg || window

    context._fn_bind = this

    return function (...argsInner) {
        const result = context._fn_bind(...args.concat(argsInner))

        delete context._fn_apply

        return result
    }
}

实现 map 方法

Array.prototype.map2 = function (fn) {
    const result = []

    for (let item of this) {
        result.push(fn(item))
    }

    return result
}

const arr = [1,2,3,4]
console.log(arr.map2(v => v * 2))
// [2,4,6,8]

模拟实现 filter 方法

Array.prototype.filter2 = function (fn) {
    const result = []

    for (let item of this) {
        if (fn(item)) {
            result.push(item)
        } 
    }

    return result
}

console.log(arr.filter2(item => item % 2 === 0))
// [2, 4]

实现 once 函数

某些场景下,当希望函数只执行一次时,可以通过以下函数实现

function once (fn) {
    let flag = false

    return function (...args) {
        if (!flag) {
            flag = true
            return fn(...args)
        }

        return
    }
}

function log () {
    console.log('foo')
}

const foo = once(log)

foo()
foo()
foo()
// foo , foo只会打印一次

实现 debounce 防抖函数

// 该函数通过第三个参数控制 fn 函数是否立即执行
function debounce(fn, time, immediate) {
    let timeout, result

    const debounced = function() {
        const args = arguments
        const that = this

        if (timeout) clearTimeout(timeout)

        if (immediate) {
            const callnow = !timeout
            timeout = setTimeout(function() {
                timeout = null
            }, time)
            if (callnow) result = fn.apply(that, args)
        } else {
            timeout = setTimeout(function() {
                result = fn.apply(that, args)
            }, time)
        }

        return result
    }

    debounced.cancel = function() {
        clearTimeout(timeout)
        timeout = null
    }

    return debounced
}

实现节流函数

节流函数的原理:即使你持续通过操作触发事件,每隔一段时间,只执行一次事件

function throttle(fn, wait) {
    let previos = 0
    let result

    return function() {
        const now = +new Date()
        if (now - wait > previos) {
            result = fn.apply(this, arguments)
            previos = now

            return result
        }
    }
}

实现对象的浅拷贝函数

function copy(obj) {
    if (typeof obj !== 'object') return

    const newObj = obj instanceof Array ? [] : {}
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            newObj[key] = obj[key]
        }
    }

    return newObj
}

实现对象的深拷贝函数

function copy(obj) {
    if (typeof obj !== 'object') return

    const newObj = obj instanceof Array ? [] : {}
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            newObj[key] = typeof obj[key] === 'object' ? copy(obj[key]) : obj[key]
        }
    }

    return newObj
}

实现多维数组扁平化

Note:处于数组的多层深拷贝会消耗性能,该函数设计为直接修改原数组

const arr = [1, [2, [3, 4]]];

function flatten(arr) {
    while (arr.some(item => Array.isArray(item))) {
        arr = [].concat(...arr)
    }

    return arr
}

console.log(flatten(arr))
// [ 1, 2, 3, 4 ]