Некоторые подводные камни глубокого копирования JavaScript

интервью внешний интерфейс JavaScript регулярное выражение

предисловие

Когда я раньше ходил в компанию на собеседование, интервьюер задал мне вопрос и сказал: «Как я могу глубоко скопировать объект». В то время я немного обрадовался: стоит ли еще думать над таким простым вопросом? Поэтому он выпалил: «Есть два широко используемых метода: первый — это JSON.parse(JSON.stringify(obj)), а второй можно сделать с помощью for…in плюс рекурсия». Интервьюер кивнул и был вполне удовлетворен. В то время я не слишком заботился об этой проблеме, пока не вспомнил об этой проблеме некоторое время назад и не обнаружил, что два метода, упомянутых выше, имеют ошибки.

Задайте вопрос

Так что же это за ошибка, упомянутая выше?

  • копия специального объекта

Сначала давайте представим объект со следующими членами, независимо от обычных типов:


const obj = {
    arr: [111, 222],
    obj: {key: '对象'},
    a: () => {console.log('函数')},
    date: new Date(),
    reg: /正则/ig
}

Затем мы используем два вышеуказанных метода для копирования один раз

JSON-метод

JSON.parse(JSON.stringify(obj))

Выходной результат:

image.png

Отсюда видно,objОбычные объекты и массивы могут быть скопированы, однакоdateОбъект становится строкой, функция сразу исчезает, а регулярка становится пустым объектом.

посмотри сноваfor...inдобавить рекурсивный метод

рекурсия

function isObj(obj) {
    return (typeof obj === 'object' || typeof obj === 'function') && obj !== null
}
function deepCopy(obj) {
    let tempObj = Array.isArray(obj) ? [] : {}
    for(let key in obj) {
        tempObj[key] = isObj(obj[key]) ? deepCopy(obj[key]) : obj[key]
    }
    return tempObj
}

результат:

image.png

в заключении

В приведенном выше тесте видно, что ни один из этих двух методов не может скопировать функцию,date,regобъект типа;

  • звенеть
Что такое кольцо?

Кольцо — это циклическая ссылка на объект, в результате чего само становится замкнутым циклом, например следующий объект:


var a = {}

a.a = a

image.png

Использование двух вышеуказанных методов для его копирования приведет к прямому сообщению об ошибке.

image.png

image.png

решение

  • звенеть

может использоватьWeakMapВ структуре хранятся объекты, которые были скопированы, и каждый раз, когда делается копия,WeakMapЗапрос, был ли скопирован объект, если он был скопирован, выньте объект и верните, будетdeepCopyФункция преобразуется следующим образом


function deepCopy(obj, hash = new WeakMap()) {
    if(hash.has(obj)) return hash.get(obj)
    let cloneObj = Array.isArray(obj) ? [] : {}
    hash.set(obj, cloneObj)
    for (let key in obj) {
        cloneObj[key] = isObj(obj[key]) ? deepCopy(obj[key], hash) : obj[key];
    }
    return cloneObj
}

Копировать результат кольца:

image.png

  • копия специального объекта

Решение этой проблемы более хлопотное, потому что слишком много типов объектов, требующих специального обращения, поэтому я обращаюсь к MDNструктурированная копия, а затем объедините решения, чтобы решить кольцо:


// 只解决date,reg类型,其他的可以自己添加

function deepCopy(obj, hash = new WeakMap()) {
    let cloneObj
    let Constructor = obj.constructor
    switch(Constructor){
        case RegExp:
            cloneObj = new Constructor(obj)
            break
        case Date:
            cloneObj = new Constructor(obj.getTime())
            break
        default:
            if(hash.has(obj)) return hash.get(obj)
            cloneObj = new Constructor()
            hash.set(obj, cloneObj)
    }
    for (let key in obj) {
        cloneObj[key] = isObj(obj[key]) ? deepCopy(obj[key], hash) : obj[key];
    }
    return cloneObj
}

Скопируйте результат:

image.png

Полную версию можно посмотретьглубокая копия lodash

  • копия функции

Но структурированная копия на MDN по-прежнему не имеет копии функции решения

image.png

Пока я думал только об использованииevalМетод копирует функцию, но этот метод может действовать только на стрелочные функции, если онfun(){}Ошибка в этой форме

Скопируйте функцию, чтобы добавить тип функции

image.png

скопировать результат

image.png

тип ошибки

image.png

постскриптум

Глубокая копия JavaScript — это не только упомянутые выше ямки, но и проблема, как скопировать свойства в цепочке прототипов? Как скопировать неперечислимые свойства Как скопироватьErrorЯмки предметов и т.п. здесь повторяться не будут.

Однако в повседневном процессе все же рекомендуется использовать метод JSON.Этот метод покрыл большую часть потребностей бизнеса, поэтому нет необходимости усложнять простые вещи.Однако, если интервьюер сталкивается с резким ответом на этот вопрос во время интервью, это определенно будет шоу У него есть лицо.