JS优秀编码欣赏--分析vue作者-游雨溪-深拷贝的优雅写法

我之前对深拷贝对处理

核心处理思路肯定都是一样的,使用递归,从基础类型数据进行赋值。

/*******************
 * 多层深拷贝
 */
function copyDeep (origin) {
    if (obj !== null && typeof obj === 'object') {
        if (isArray(origin)) {
            var targetArr = []
            for (var i = 0; i < origin.length; i++) {
                targetArr.push(copyDeep(origin[i]))
            }
            return targetArr
        } else {
            var targetObj = {}
            for (var item in origin) {
                targetObj[item] = copyDeep(origin[item])
            }
            return targetObj
        }
    } else {

        return origin
    }
    function isArray (value) {

        return !!value && value instanceof Array
    }
}

接下来看看 游雨溪 大大在 VUEX中用深拷贝时的方法

/**
 * Get the first item that pass the test
 * by second argument function
 *
 * @param {Array} list
 * @param {Function} f
 * @return {*}
 */
function find (list, f) {
  return list.filter(f)[0]
}

/**
 * Deep copy the given object considering circular structure.
 * This function caches all nested objects and its copies.
 * If it detects circular structure, use cached copy to avoid infinite loop.
 *
 * @param {*} obj
 * @param {Array <Object> } cache
 * @return {*}
 */
function deepCopy (obj, cache = []) {
    // just return if obj is immutable value
    if (obj === null || typeof obj !== 'object') {
        return obj
    }

    // if obj is hit, it is in circular structure
    const hit = find(cache, c => c.original === obj)
    if (hit) {
        return hit.copy
    }

    const copy = Array.isArray(obj) ? [] : {}
    // put the copy into cache at first
    // because we want to refer it in recursive deepCopy
    cache.push({
        original: obj,
        copy
    })

    Object.keys(obj).forEach(key => {
        copy[key] = deepCopy(obj[key], cache)
    })

    return copy
}

注释的规范

首先用英文还是汉字这个先不纠结,看看它的方式

Deep copy the given object considering circular structure. :描述方法功能,通过循环深度拷贝object

This function caches all nested objects and its copies. : 函数将所有潜逃的对象和副本缓存了下来

If it detects circular structure, use cached copy to avoid infinite loop. : 如果检测到循环结构,则使用缓存副本以避免无限循环。

通过三行标注,写明方法的主要功能和处理思路,标出需要特别注意的地方

@param {*} obj : 第一个参数是json对象

@param {Array <Object> } : 第二个参数是数组,数组内是object

@return {*} : 返回的是一个json对象

/**
 * Deep copy the given object considering circular structure.
 * This function caches all nested objects and its copies.
 * If it detects circular structure, use cached copy to avoid infinite loop.
 *
 * @param {*} obj
 * @param {Array <Object> } cache
 * @return {*}
 */

处理逻辑

var matchData = {
    version: '1.1',
    prize: {
        first: '铅笔盒',
        second: '铅笔',
        third: '橡皮擦',
    },
    person: [
        {
            age: '14',
            name: '小明'
        },
        {
            age: '18',
            name: '小红'
        },
    ]
}
var matchDataCp = deepCopy(matchData)

function find (list, f) {
    return list.filter(f)[0]
}

function deepCopy (obj, cache = []) {
    // just return if obj is immutable value
    // 如果不是引用类型,直接返回值,完成copy
    if (obj === null || typeof obj !== 'object') {
        return obj
    }

    // if obj is hit, it is in circular structure
    // 如果缓存中的对象和传入的对象相等,则拷贝完成,直接返回缓存中的copy值
    const hit = find(cache, c => c.original === obj)
    if (hit) {
        return hit.copy
    }

    // 如果obj是否是数组,数组的话赋值[],否则赋值{}
    const copy = Array.isArray(obj) ? [] : {}
    // put the copy into cache at first
    // 将copy放入缓存
    // because we want to refer it in recursive deepCopy
    // 因为我们深拷贝的时候会用到
    cache.push({
        original: obj,
        copy
    })

    // 对obj进行遍历
    Object.keys(obj).forEach(key => {
        copy[key] = deepCopy(obj[key], cache)
    })

    return copy
}

代码比对

首先拿出我的代码来分析一下,先判断传入值是否是Object类型,不是的话直接返回值。Object类型中,判断是否是数组类型,是的话进行push操作,不是的话按照json对象处理

核心逻辑肯定是没问题的,因为在实际项目中我就是这么用的。

游大大的代码比我的强在哪?

核心代码对比如下


function deepCopy (obj, cache = []) {
    if (obj === null || typeof obj !== 'object') {
        return obj
    }
    const copy = Array.isArray(obj) ? [] : {}
    Object.keys(obj).forEach(key => {
        copy[key] = deepCopy(obj[key], cache)
    })
    return copy
}


function copyDeep (origin) {
    if (obj !== null && typeof obj === 'object') {
        if (isArray(origin)) {
            var targetArr = []
            for (var i = 0; i < origin.length; i++) {
                targetArr.push(copyDeep(origin[i]))
            }
            return targetArr
        } else {
            var targetObj = {}
            for (var item in origin) {
                targetObj[item] = copyDeep(origin[item])
            }
            return targetObj
        }
    } else {
        return origin
    }
    function isArray (value) {
        return !!value && value instanceof Array
    }
}

从简练程度上说,就比我的好很多,他只用了一个if判断,如果是null或者不是object,直接返回

我的isArray方法他这里直接用的Array下的isArray。

我进行了Object和Array的判断,因为我当时觉得object是键值对,array是数组,写入的方式是不一样的

但是他这里利用了Object.keys(obj)将这一个判断忽略掉了。因为Object.keys对数组对操作,返回的是索引的列表。

虽然有些偏差,但大体逻辑一致,下面说一下他的另一部分我没处理过的思路

栈溢出

对于栈溢出,我这里是没有处理过的。因为是自己用的小工具,一来自己用的时候肯定会注意,二来处理意识也没有那么强。下面分析一下游大大的处理思路。

他对每一步处理都添加到了cache缓存。缓存中有每次传入对数据和当前copy得到的数据。用了一个Array.filter方法,判断如果当前要拷贝的数据和cache中某一个origin的值是完全相等的, 责返回缓存时生成的copy值。

function find (list, f) {
    return list.filter(f)[0]
}
const hit = find(cache, c => c.original === obj)
if (hit) {
    return hit.copy
}

他的写法的问题

首先必须声明的是,站在他的高度,所谓的问题,应该是他认为这里没有必要深入处理的问题。但是作为我们,还是要尽力理解到位才行

他这里通过与缓存数据的对比,解决了重复引用时栈溢出的问题。但是他是直接将cache中的copy返回了就去,而copy是引用类型的。

作者只是将结果返回回去了,他们的引用关系并没有断开,仍在一条原型链上。所以说这并不是真正意义上的深拷贝。

执行JSON.stringify(matchDataCp)的时候,仍然会报错

随机浏览