JavaScript-深拷贝函数的实现

2021/12/15 12:16:58

本文主要是介绍JavaScript-深拷贝函数的实现,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

1、常见的一种实现方式

const info = JSON.parse(JSON.stringify(obj))

2、简单实现 deepClone

//工具函数
function isObject(value) {
    const type = typeof value
    return (value !== null) && (type === 'object' || type === 'function')
}

function deepClone(originValue) {
    //1.判断传入的originValue是不是一个对象类型
    if (!isObject(originValue)) return originValue

    //2.简单实现
    const newObj = {}
    for (const key in originValue) {
        newObj[key] = deepClone(originValue[key])
    }
    return newObj
}

//测试一下
const obj = {
    name: '当地小有名气可爱鬼',
    height: 170,
    eat: {
        first: '臭豆腐',
        second: '榴莲'
    }
}

const test = deepClone(obj)
console.log(test === obj); //false

obj.eat.first = '螺蛳粉'
console.log(test); //{ name: '当地小有名气可爱鬼', height: 170, eat: { first: '臭豆腐', second: '榴莲' } }

3、增加功能 - 处理其他数据类型

//工具函数
function isObject(value) {
    const type = typeof value
    return (value !== null) && (type === 'object' || type === 'function')
}

function deepClone(originValue) {
    //类型判断

    // 判断是否是一个Set类型
    if (originValue instanceof Set) {
        return new Set([...originValue])
    }

    // 判断是否是一个Map类型
    if (originValue instanceof Map) {
        return new Map([...originValue])
    }

    // 判断如果是Symbol的value, 那么创建一个新的Symbol
    if (typeof originValue === "symbol") {
        return Symbol(originValue.description)
    }

    // 判断如果是函数类型, 那么直接使用同一个函数
    if (typeof originValue === 'function') {
        return originValue
    }

    //判断传入的originValue是不是一个对象类型
    if (!isObject(originValue)) return originValue

    //判断传入的对象是数组,还是对象
    const newObject = Array.isArray(originValue) ? [] : {}
    for (const key in originValue) {
        newObject[key] = deepClone(originValue[key])
    }

    //处理一下symbol的key
    const symbolKeys = Object.getOwnPropertySymbols(originValue)
    for (const iterator of symbolKeys) {
        newObject[iterator] = deepClone(originValue[iterator])
    }

    return newObject
}

//测试一下
let s1 = Symbol('s1')
let s2 = Symbol('s2')

const info = {
    name: '当地小有名气可爱鬼',
    height: 170,
    eat: {
        first: '臭豆腐',
        second: '榴莲'
    },
    singer: ['周杰伦', '孙燕姿', '邓紫棋'],
    //symbol 可以做key\value
    [s1]: '我是s1',
    s2: s2,
    bar: function () {
        return '我是函数'
    },
    set: new Set(['1', '2', '3']),
    map: new Map([
        ['a', 'b'],
        ['c', 'd']
    ])
}

const check = deepClone(info)
console.log(check === info); //false

info.eat.first = '小龙虾'
console.log(info, '我是info');
// {
//     name: '当地小有名气可爱鬼',
//     height: 170,
//     eat: {
//         first: '小龙虾',
//         second: '榴莲'
//     },
//     singer: ['周杰伦', '孙燕姿', '邓紫棋'],
//     s2: Symbol(s2),
//     bar: [Function: bar],
//     set: Set(3) {
//         '1',
//         '2',
//         '3'
//     },
//     map: Map(2) {
//         'a' => 'b', 'c' => 'd'
//     },
//     [Symbol(s1)]: '我是s1'
// }
// 我是info
console.log(check, '我是check');
// {
//     name: '当地小有名气可爱鬼',
//     height: 170,
//     eat: {
//         first: '臭豆腐',
//         second: '榴莲'
//     },
//     singer: ['周杰伦', '孙燕姿', '邓紫棋'],
//     s2: Symbol(s2),
//     bar: [Function: bar],
//     set: Set(3) {
//         '1',
//         '2',
//         '3'
//     },
//     map: Map(2) {
//         'a' => 'b', 'c' => 'd'
//     },
//     [Symbol(s1)]: '我是s1'
// }
// 我是check
console.log(check.s1 === info.s1);//true
console.log(check.s2 === info.s2);//false

4、增加功能 - 解决循环引用的问题(WeakMap)

function isObject(value) {
    const type = typeof value
    return (value !== null) && (type === 'object' || type === 'function')
}

function deepClone(originValue , map = new WeakMap()) {
    //类型判断

    // 判断是否是一个Set类型
    if (originValue instanceof Set) {
        return new Set([...originValue])
    }

    // 判断是否是一个Map类型
    if (originValue instanceof Map) {
        return new Map([...originValue])
    }

    // 判断如果是Symbol的value, 那么创建一个新的Symbol
    if (typeof originValue === "symbol") {
        return Symbol(originValue.description)
    }

    // 判断如果是函数类型, 那么直接使用同一个函数
    if (typeof originValue === 'function') {
        return originValue
    }

    //判断传入的originValue是不是一个对象类型
    if (!isObject(originValue)) return originValue

    //处理数据循环引用的问题
    if (map.has(originValue)) {
        return map.get(originValue)
    }

    //判断传入的对象是数组,还是对象
    const newObject = Array.isArray(originValue) ? [] : {}

    map.set(originValue, newObject)
    
    for (const key in originValue) {
        newObject[key] = deepClone(originValue[key],map)
    }

    //处理一下symbol的key
    const symbolKeys = Object.getOwnPropertySymbols(originValue)
    console.log(symbolKeys, 'symbolKeys');
    for (const iterator of symbolKeys) {
        newObject[iterator] = deepClone(originValue[iterator],map)
    }

    return newObject
}

//测试一下:
let s1 = Symbol('s1')
let s2 = Symbol('s2')

const info = {
    name: '当地小有名气可爱鬼',
    height: 170,
    eat: {
        first: '臭豆腐',
        second: '榴莲'
    },
    singer: ['周杰伦', '孙燕姿', '邓紫棋'],
    //symbol 可以做key\value
    [s1]: '我是s1',
    s2: s2,
    bar: function () {
        return '我是函数'
    },
    set: new Set(['1', '2', '3']),
    map: new Map([
        ['a', 'b'],
        ['c', 'd']
    ])
}

info.list = info //设置一个引用,如果不做处理会报栈溢出: Maximum call stack size exceeded
const check = deepClone(info)
console.log(check === info); //false

info.eat.first = '小龙虾'
console.log(info);
console.log(check.s2 === info.s2)

简单拓展一下这里为什么使用weakMap:

1.Map可以使用任意类型的key值,不限字符串,对象等

2.WeakMap只能使用对象作为key值,是弱引用,当从WeakMap中移除时,会自动垃圾回收



这篇关于JavaScript-深拷贝函数的实现的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程