实现deepClone函数的思路

602 阅读1分钟

基础:递归实现dependArray和dependObject

这两个不用说,实现起来非常简单

function dependArray(array){
    let length = array.length
    let clone = new Array(length)
    let cur = length
    let curTarget

    while(cur--){
        curTarget = array[cur]
        // 第二个参数的用处下面会说
        clone[cur] = chooseOpeByType(curTarget,clone)
    }
    return clone
}

唯一要注意的是,利用while(cur--)这样的技巧时,cur不要初始化为length - 1

function dependObject(object){
    let clone = Object.create(null)
    let keys = Object.keys(object)
    let cur = keys.length
    let curTarget

    while(cur--){
        curTarget = object[keys[cur]]
        clone[keys[cur]] = chooseOpeByType(curTarget,clone)
    }
    return clone
}

chooseOpeByType只是一个选择函数

function chooseOpeByType(curTarget,obj=undefined){
    switch(Object.prototype.toString.call(curTarget)){
        case '[object Object]':
            return dependObject(curTarget)
        case '[object Array]':
            return dependArray(curTarget)
        case '[object Function]':
            return dependFunc(curTarget,obj)
        default:
            return curTarget
    }
}

执行函数

接下来是重点,如何clone一个函数?我们想想deepClone有哪几种

  • messageChannel 无法发送函数
  • JSON.stringify clone了无法使用

但是我们又想到,在实现webworker时,我们可以利用func.string -> blob-> createObjectURL -> webworker实现定制化无需脚本的webworker,我们这里能不能使用呢?

function dependFunc(func){
    let funcStr = func.toString()
    return function(){
        eval(`(${funcStr}).call(ctx)`)
    }
}

尝试一下,确实是可以运行的。

正确执行

函数能运行了,问题又来了,this和箭头函数怎么处理呢?

this和箭头函数的执行规律

一眼以蔽之

  • 箭头函数的this是最近的非箭头函数的函数this值,或全局this
  • this就很简单了,谁调用归谁

于是我们似乎可以暂时不管箭头函数,反正它不属于调用者管。那么对普通函数,this就是调用的作用域咯,把obj传进去试试?

function dependFunc(func,obj){
    const ctx = obj
    let funcStr = func.toString()
    return function(){
        eval(`(${funcStr}).call(ctx)`)
    }
}

数组?

数组的this很奇怪,它有额外的length属性,所以我们把数组也搞进去吧,于是dependArray和dependObject时传入第二个参数,也就是clone后返回的对象。

问题

当然,箭头函数问题还是没能解决,当它在函数内调用时,箭头函数clone时就会丢掉函数作用域,需要把箭头函数改为非箭头函数,然后让用户自行传入外层作用域,这就比较复杂了。 有好的建议可以分享