목차
深拷贝的最终实现
1. JavaScript数据类型的拷贝原理
2. 深浅拷贝
2.1 浅拷贝
🎜딥 카피의 최종 구현🎜
🎜1. JavaScript 데이터 유형의 복사 원리🎜
🎜2. 어둡고 얕은 복사🎜
🎜 2.1 얕은 복사🎜
2.2 深拷贝
方法1:JSON.stringify()
方法2:递归基础版深拷贝
方法3:递归이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.
웹 프론트엔드 JS 튜토리얼 이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.

이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.

Oct 21, 2022 pm 07:37 PM
javascript

이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.

딥카피에 대한 글은 인터넷에 많이 있지만 품질도 다양하고, 잘 생각되지 않은 글도 많고, 쓰는 방법도 상대적으로 투박하고 불만족스럽습니다. 이 글은 완벽한 딥 카피를 완성하는 것을 목표로 합니다. 읽어보시고 궁금한 점이 있으시면 언제든지 추가하고 개선해 주세요.

전체 복사가 완료되었는지 평가하려면 다음 질문이 구현되었는지 확인하세요.

  • 기본 유형 데이터를 복사할 수 있나요? 基本类型数据是否能拷贝?

  • 键和值都是基本类型的普通对象是否能拷贝?

  • Symbol作为对象的key是否能拷贝?

  • DateRegExp对象类型是否能拷贝?

  • MapSet对象类型是否能拷贝?

  • Function对象类型是否能拷贝?(函数我们一般不用深拷贝)

  • 对象的原型是否能拷贝?

  • 不可枚举属性是否能拷贝?

  • 循环引用是否能拷贝?

怎样?你写的深拷贝够完善吗?

深拷贝的最终实现

这里先直接给出最终的代码版本,方便想快速了解的人查看,当然,你想一步步了解可以继续查看文章余下的内容:

function deepClone(target) {
    const map = new WeakMap()
    
    function isObject(target) {
        return (typeof target === '이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.ect' && target ) || typeof target === 'function'
    }

    function clone(data) {
        if (!isObject(data)) {
            return data
        }
        if ([Date, RegExp].includes(data.constructor)) {
            return new data.constructor(data)
        }
        if (typeof data === 'function') {
            return new Function('return ' + data.toString())()
        }
        const exist = map.get(data)
        if (exist) {
            return exist
        }
        if (data instanceof Map) {
            const result = new Map()
            map.set(data, result)
            data.forEach((val, key) => {
                if (isObject(val)) {
                    result.set(key, clone(val))
                } else {
                    result.set(key, val)
                }
            })
            return result
        }
        if (data instanceof Set) {
            const result = new Set()
            map.set(data, result)
            data.forEach(val => {
                if (isObject(val)) {
                    result.add(clone(val))
                } else {
                    result.add(val)
                }
            })
            return result
        }
        const keys = Reflect.ownKeys(data)
        const allDesc = Object.getOwnPropertyDescriptors(data)
        const result = Object.create(Object.getPrototypeOf(data), allDesc)
        map.set(data, result)
        keys.forEach(key => {
            const val = data[key]
            if (isObject(val)) {
                result[key] = clone(val)
            } else {
                result[key] = val
            }
        })
        return result
    }

    return clone(target)
}
로그인 후 복사

1. JavaScript数据类型的拷贝原理

先看看이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.图(除了Object,其他都是基础类型):
이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.
在JavaScript中,基础类型值的复制是直接拷贝一份新的一模一样的数据,这两份数据相互独立,互不影响。而引用类型值(Object类型)的复制是传递对象的引用(也就是对象所在的内存地址,即指向对象的指针),相当于多个变量指向同一个对象,那么只要其中的一个变量对这个对象进行修改,其他的变量所指向的对象也会跟着修改(因为它们指向的是同一个对象)。如下图:
이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.

2. 深浅拷贝

深浅拷贝主要针对的是Object类型,基础类型的值本身即是复制一模一样的一份,不区分深浅拷贝。这里我们先给出测试的拷贝对象,大家可以拿这个이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.对象来测试一下自己写的深拷贝函数是否完善:

// 测试的이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.对象
const 이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다. = {
    // =========== 1.基础数据类型 ===========
    num: 0, // number
    str: '', // string
    bool: true, // boolean
    unf: undefined, // undefined
    nul: null, // null
    sym: Symbol('sym'), // symbol
    bign: BigInt(1n), // bigint

    // =========== 2.Object类型 ===========
    // 普通对象
    이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.: {
        name: '我是一个对象',
        id: 1
    },
    // 数组
    arr: [0, 1, 2],
    // 函数
    func: function () {
        console.log('我是一个函数')
    },
    // 日期
    date: new Date(0),
    // 正则
    reg: new RegExp('/我是一个正则/ig'),
    // Map
    map: new Map().set('mapKey', 1),
    // Set
    set: new Set().add('set'),
    // =========== 3.其他 ===========
    [Symbol('1')]: 1  // Symbol作为key
};

// 4.添加不可枚举属性
Object.defineProperty(이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다., 'innumerable', {
    enumerable: false,
    value: '不可枚举属性'
});

// 5.设置原型对象
Object.setPrototypeOf(이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다., {
    proto: 'proto'
})

// 6.设置loop成循环引用的属性
이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다..loop = 이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.
로그인 후 복사

이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.对象在Chrome浏览器中的结果:

이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.

2.1 浅拷贝

浅拷贝: 创建一个新的对象,来接受你要重新复制或引用的对象值。如果对象属性是基本的数据类型,复制的就是基本类型的值给新对象;但如果属性是引用数据类型,复制的就是内存中的地址,如果其中一个对象改变了这个内存中的地址所指向的对象,肯定会影响到另一个对象。

首先我们看看一些浅拷贝的方法(详细了解可点击对应方法的超链接):

Symbol을 객체의 키로 복사할 수 있나요?
方法 使用方式 注意事项
Object.assign() Object.assign(target, ...sources)
说明:用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。
1.不会拷贝对象的继承属性;
2.不会拷贝对象的不可枚举的属性;
3.可以拷贝 Symbol 类型的属性。
展开语法 let 이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.Clone = { ...이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다. }; 缺陷和Object.assign()差不多,但是如果属性都是基本类型的值,使用扩展运算符进行浅拷贝会更加方便。
Array.prototype.concat()拷贝数组 const new_array = old_array.concat(value1[, value2[, ...[, valueN]]]) 浅拷贝,适用于基本类型值的数组
Array.prototype.slice()拷贝数组 arr.slice([begin[, end]]) 기본 유형의 키와 값이 있는 일반 객체도 복사할 수 있나요?
🎜🎜 DateRegExp 개체 유형을 복사할 수 있나요? 🎜🎜🎜MapSet 개체 유형을 복사할 수 있나요? 🎜🎜🎜Function 개체 유형을 복사할 수 있나요? (우리는 일반적으로 함수에 깊은 복사를 사용하지 않습니다)🎜🎜🎜객체의 프로토타입을 복사할 수 있나요? 🎜🎜🎜 열거 불가능한 속성을 복사할 수 있나요? 🎜🎜🎜 순환 참조를 복사할 수 있나요? 🎜🎜🎜어떻게? 당신이 작성한 딥 카피는 충분히 완벽합니까? 🎜
🎜

🎜딥 카피의 최종 구현🎜

🎜빠른 이해를 원하는 사람들의 편의를 위해 최종 코드 버전을 여기에 직접 제공합니다. 단계별로 이해하려면 기사의 나머지 부분을 계속 볼 수 있습니다. 🎜
function shallowClone(target) {
    if (typeof target === '이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.ect' && target !== null) {
        const cloneTarget = Array.isArray(target) ? [] : {};
        for (let prop in target) {
            if (target.hasOwnProperty(prop)) {
                cloneTarget[prop] = target[prop];
            }
        }
        return cloneTarget;
    } else {
        return target;
    }
}


// 测试
const shallowCloneObj = shallowClone(이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.)

shallowCloneObj === 이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.  // false,返回的是一个新对象
shallowCloneObj.arr === 이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다..arr  // true,对于对象类型只拷贝了引用
로그인 후 복사

🎜1. JavaScript 데이터 유형의 복사 원리🎜

🎜먼저 JS 데이터 유형 다이어그램을 살펴보겠습니다(객체, 그 외 모든 것은 기본 유형임):

자바스크립트에서 기본 타입 값을 복사한다는 것은 동일한 새로운 데이터를 직접 복사하는 것을 의미하며, 두 데이터는 서로 독립적이며 서로 영향을 미치지 않습니다. . 참조형 값(Object type)을 복사한다는 것은 객체의 참조(즉, 객체가 위치한 메모리 주소, 즉 객체에 대한 포인터)를 전달하는 것으로, 이는 여러 변수를 가리키는 것과 같습니다. 동일한 객체에 대한 변수 중 하나가 객체에 대한 참조를 갖고 있는 한 수정되면 다른 변수가 가리키는 객체도 수정됩니다(동일한 객체를 가리키기 때문입니다). 아래와 같이:
🎜

🎜2. 어둡고 얕은 복사🎜

🎜어둡고 얕은 복사는 주로 Object 유형을 대상으로 하며, 기본 유형 자체의 값은 어두운 복사본과 밝은 복사본을 구분하지 않고 그대로 복사합니다. 여기에서는 먼저 테스트용 복사 개체를 제공합니다. 이 이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다. 개체를 사용하여 작성한 전체 복사 기능이 완벽한지 테스트할 수 있습니다. 🎜
function deepClone(target) {
    if (typeof target === '이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.ect' && target !== null) {
        return JSON.parse(JSON.stringify(target));
    } else {
        return target;
    }
}

// 开头的测试이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.存在BigInt类型、循环引用,JSON.stringfy()执行会报错,所以除去这两个条件进行测试
const clonedObj = deepClone(이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.)

// 测试
clonedObj === 이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.  // false,返回的是一个新对象
clonedObj.arr === 이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다..arr  // false,说明拷贝的不是引用
로그인 후 복사
로그인 후 복사
🎜이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다. 개체가 탐색됩니다. 브라우저의 Chrome 결과: 🎜🎜이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.🎜

🎜 2.1 얕은 복사🎜

🎜🎜얕은 복사🎜: 다시 복사하거나 참조하려는 개체 값을 허용하는 새 개체를 만듭니다. 객체 속성이 기본 데이터 유형인 경우 기본 유형의 값이 새 객체에 복사되지만, 속성이 참조 데이터 유형인 경우 객체 중 하나가 가리키는 주소가 변경되면 메모리의 주소가 복사됩니다. 기억에 따르면 Object는 확실히 다른 객체에 영향을 미칩니다. 🎜🎜먼저 몇 가지 얕은 복사 방법을 살펴보겠습니다. 자세한 내용을 보려면 해당 방법에 대한 하이퍼링크를 클릭하세요. 🎜🎜
Method 사용법참고
Object.sign()🎜🎜 Object.Assign(target, ...sources)
설명: 하나 이상의 소스 객체에서 열거 가능한 모든 속성의 값을 대상 객체에 할당하는 데 사용됩니다. 대상 객체를 반환합니다. 🎜🎜1. 객체의 상속된 속성은 복사되지 않습니다.
2. 객체의 열거 불가능한 속성은 복사되지 않습니다.
3. 기호 유형 속성은 복사될 수 있습니다. 🎜🎜
확장 구문🎜🎜let 이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.Clone = { ...이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다. };🎜🎜결점은 Object.sign()과 유사하지만, 속성이 모두 기본 유형 값인 경우 스프레드 연산자를 사용하여 얕은 복사를 수행하는 것이 더 편리합니다. 🎜🎜
Array.prototype.concat()은 배열을 복사합니다🎜🎜const new_array = old_array.concat(value1[, value2[, ...[, valueN]]]) 🎜 🎜기본 유형 값의 배열에 적합한 얕은 복사🎜🎜
Array.prototype.slice()는 배열을 복사합니다.🎜🎜arr.slice([begin[, end]]) 🎜 🎜기본 유형 값의 배열에 적합한 얕은 복사본🎜🎜🎜🎜

这里只列举了常用的几种方式,除此之外当然还有其他更多的方式。注意,我们直接使用=赋值不是浅拷贝,因为它是直接指向同一个对象了,并没有返回一个新对象。

手动实现一个浅拷贝:

function shallowClone(target) {
    if (typeof target === '이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.ect' && target !== null) {
        const cloneTarget = Array.isArray(target) ? [] : {};
        for (let prop in target) {
            if (target.hasOwnProperty(prop)) {
                cloneTarget[prop] = target[prop];
            }
        }
        return cloneTarget;
    } else {
        return target;
    }
}


// 测试
const shallowCloneObj = shallowClone(이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.)

shallowCloneObj === 이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.  // false,返回的是一个新对象
shallowCloneObj.arr === 이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다..arr  // true,对于对象类型只拷贝了引用
로그인 후 복사

从上面这段代码可以看出,利用类型判断(查看typeof),针对引用类型的对象进行 for 循环遍历对象属性赋值给目标对象的属性(for...in语句以任意顺序遍历一个对象的除Symbol以外的可枚举属性,包含原型上的属性。查看for…in),基本就可以手工实现一个浅拷贝的代码了。

2.2 深拷贝

深拷贝:创建一个新的对象,将一个对象从内存中完整地拷贝出来一份给该新对象,并从堆内存中开辟一个全新的空间存放新对象,且新对象的修改并不会改变原对象,二者实现真正的分离。

看看现存的一些深拷贝的方法:

方法1:JSON.stringify()

JSON.stringfy() 其实就是将一个 JavaScript 对象或值转换为 JSON 字符串,最后再用 JSON.parse() 的方法将JSON 字符串生成一个新的对象。(点这了解:JSON.stringfy()、JSON.parse())

使用如下:

function deepClone(target) {
    if (typeof target === '이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.ect' && target !== null) {
        return JSON.parse(JSON.stringify(target));
    } else {
        return target;
    }
}

// 开头的测试이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.存在BigInt类型、循环引用,JSON.stringfy()执行会报错,所以除去这两个条件进行测试
const clonedObj = deepClone(이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.)

// 测试
clonedObj === 이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.  // false,返回的是一个新对象
clonedObj.arr === 이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다..arr  // false,说明拷贝的不是引用
로그인 후 복사
로그인 후 복사

浏览器执行结果:

이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.
从以上结果我们可知JSON.stringfy() 存在以下一些问题:

  • 执行会报错:存在BigInt类型、循环引用。

  • 拷贝Date引用类型会变成字符串。

  • 键值会消失:对象的值中为FunctionUndefinedSymbol 这几种类型,。

  • 键值变成空对象:对象的值中为MapSetRegExp这几种类型。

  • 无法拷贝:不可枚举属性、对象的原型链。

  • 补充:其他更详细的内容请查看官方文档:JSON.stringify()

由于以上种种限制条件,JSON.stringfy() 方式仅限于深拷贝一些普通的对象,对于更复杂的数据类型,我们需要另寻他路。

方法2:递归基础版深拷贝

手动递归实现深拷贝,我们只需要完成以下2点即可:

  • 对于基础类型,我们只需要简单地赋值即可(使用=)。

  • 对于引用类型,我们需要创建新的对象,并通过遍历键来赋值对应的值,这个过程中如果遇到 Object 类型还需要再次进行遍历。

function deepClone(target) {
    if (typeof target === '이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.ect' && target) {
        let cloneObj = {}
        for (const key in target) { // 遍历
            const val = target[key]
            if (typeof val === '이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.ect' && val) {
                cloneObj[key] = deepClone(val) // 是对象就再次调用该函数递归
            } else {
                cloneObj[key] = val // 基本类型的话直接复制值
            }
        }
        return cloneObj
    } else {
        return target;
    }
}

// 开头的测试이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.存在循环引用,除去这个条件进行测试
const clonedObj = deepClone(이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.)

// 测试
clonedObj === 이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.  // false,返回的是一个新对象
clonedObj.arr === 이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다..arr  // false,说明拷贝的不是引用
로그인 후 복사

浏览器执行结果:

이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.
该基础版本存在许多问题:

  • 不能处理循环引用。

  • 只考虑了Object对象,而Array对象、Date对象、RegExp对象、Map对象、Set对象都变成了Object对象,且值也不正确。

  • 丢失了属性名为Symbol类型的属性。

  • 丢失了不可枚举的属性。

  • 原型上的属性也被添加到拷贝的对象中了。

如果存在循环引用的话,以上代码会导致无限递归,从而使得堆栈溢出。如下例子:

const a = {}
const b = {}
a.b = b
b.a = a
deepClone(a)
로그인 후 복사

对象 a 的键 b 指向对象 b,对象 b 的键 a 指向对象 a,查看a对象,可以看到是无限循环的:
이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.
对对象a执行深拷贝,会出现死循环,从而耗尽内存,进而报错:堆栈溢出
이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.
如何避免这种情况呢?一种简单的方式就是把已添加的对象记录下来,这样下次碰到相同的对象引用时,直接指向记录中的对象即可。要实现这个记录功能,我们可以借助 ES6 推出的 WeakMap 对象,该对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。(WeakMap相关见这:WeakMap)

针对以上基础版深拷贝存在的缺陷,我们进一步去完善,实现一个完美的深拷贝

方法3:递归이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.

对于基础版深拷贝存在的问题,我们一一改进:

存在的问题改进方案
1. 不能处理循环引用使用 WeakMap 作为一个Hash表来进行查询
2. 只考虑了Object对象当参数为 DateRegExpFunctionMapSet,则直接生成一个新的实例返回
3. 属性名为Symbol的属性
4. 丢失了不可枚举的属性
针对能够遍历对象的不可枚举属性以及 Symbol 类型,我们可以使用 Reflect.ownKeys()
Reflect.ownKeys(이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.)相当于[...Object.getOwnPropertyNames(이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.), ...Object.getOwnPropertySymbols(이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.)]
4. 原型上的属性Object.getOwnPropertyDescriptors()设置属性描述对象,以及Object.create()方式继承原型链

代码实现:

function deepClone(target) {
    // WeakMap作为记录对象Hash表(用于防止循环引用)
    const map = new WeakMap()

    // 判断是否为이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.ect类型的辅助函数,减少重复代码
    function isObject(target) {
        return (typeof target === '이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.ect' && target ) || typeof target === 'function'
    }

    function clone(data) {

        // 基础类型直接返回值
        if (!isObject(data)) {
            return data
        }

        // 日期或者正则对象则直接构造一个新的对象返回
        if ([Date, RegExp].includes(data.constructor)) {
            return new data.constructor(data)
        }

        // 处理函数对象
        if (typeof data === 'function') {
            return new Function('return ' + data.toString())()
        }

        // 如果该对象已存在,则直接返回该对象
        const exist = map.get(data)
        if (exist) {
            return exist
        }

        // 处理Map对象
        if (data instanceof Map) {
            const result = new Map()
            map.set(data, result)
            data.forEach((val, key) => {
                // 注意:map中的值为이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.ect的话也得深拷贝
                if (isObject(val)) {
                    result.set(key, clone(val))
                } else {
                    result.set(key, val)
                }
            })
            return result
        }

        // 处理Set对象
        if (data instanceof Set) {
            const result = new Set()
            map.set(data, result)
            data.forEach(val => {
                // 注意:set中的值为이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.ect的话也得深拷贝
                if (isObject(val)) {
                    result.add(clone(val))
                } else {
                    result.add(val)
                }
            })
            return result
        }

        // 收集键名(考虑了以Symbol作为key以及不可枚举的属性)
        const keys = Reflect.ownKeys(data)
        // 利用 Object 的 getOwnPropertyDescriptors 方法可以获得对象的所有属性以及对应的属性描述
        const allDesc = Object.getOwnPropertyDescriptors(data)
        // 结合 Object 的 create 方法创建一个新对象,并继承传入原对象的原型链, 这里得到的result是对data的浅拷贝
        const result = Object.create(Object.getPrototypeOf(data), allDesc)

        // 新对象加入到map中,进行记录
        map.set(data, result)

        // Object.create()是浅拷贝,所以要判断并递归执行深拷贝
        keys.forEach(key => {
            const val = data[key]
            if (isObject(val)) {
                // 属性值为 对象类型 或 函数对象 的话也需要进行深拷贝
                result[key] = clone(val)
            } else {
                result[key] = val
            }
        })
        return result
    }

    return clone(target)
}



// 测试
const clonedObj = deepClone(이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.)
clonedObj === 이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.  // false,返回的是一个新对象
clonedObj.arr === 이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다..arr  // false,说明拷贝的不是引用
clonedObj.func === 이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다..func  // false,说明function也复制了一份
clonedObj.proto  // proto,可以取到原型的属性
로그인 후 복사

详细的说明见代码中的注释,更多测试希望大家自己动手尝试验证一下以加深印象。

在遍历 Object 类型数据时,我们需要把 Symbol 类型的键名也考虑进来,所以不能通过 Object.keys 获取键名或 for...in 方式遍历,而是通过Reflect.ownKeys()获取所有自身的键名(getOwnPropertyNamesgetOwnPropertySymbols 函数将键名组合成数组也行:[...Object.getOwnPropertyNames(이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.), ...Object.getOwnPropertySymbols(이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.)]),然后再遍历递归,最终实现拷贝。

浏览器执行结果:
이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.
可以发现我们的cloneObj对象和原来的이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.对象一模一样,并且修改cloneObj对象的各个属性都不会对이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.对象造成影响。其他的大家再多尝试体会哦!

【相关推荐:javascript视频教程编程视频

위 내용은 이 기사에서는 JavaScript의 딥 카피에 대한 자세한 이해를 제공합니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

WebSocket과 JavaScript를 사용하여 온라인 음성 인식 시스템을 구현하는 방법 WebSocket과 JavaScript를 사용하여 온라인 음성 인식 시스템을 구현하는 방법 Dec 17, 2023 pm 02:54 PM

WebSocket 및 JavaScript를 사용하여 온라인 음성 인식 시스템을 구현하는 방법 소개: 지속적인 기술 개발로 음성 인식 기술은 인공 지능 분야의 중요한 부분이 되었습니다. WebSocket과 JavaScript를 기반으로 한 온라인 음성 인식 시스템은 낮은 대기 시간, 실시간, 크로스 플랫폼이라는 특징을 갖고 있으며 널리 사용되는 솔루션이 되었습니다. 이 기사에서는 WebSocket과 JavaScript를 사용하여 온라인 음성 인식 시스템을 구현하는 방법을 소개합니다.

WebSocket 및 JavaScript: 실시간 모니터링 시스템 구현을 위한 핵심 기술 WebSocket 및 JavaScript: 실시간 모니터링 시스템 구현을 위한 핵심 기술 Dec 17, 2023 pm 05:30 PM

WebSocket과 JavaScript: 실시간 모니터링 시스템 구현을 위한 핵심 기술 서론: 인터넷 기술의 급속한 발전과 함께 실시간 모니터링 시스템이 다양한 분야에서 널리 활용되고 있다. 실시간 모니터링을 구현하는 핵심 기술 중 하나는 WebSocket과 JavaScript의 조합입니다. 이 기사에서는 실시간 모니터링 시스템에서 WebSocket 및 JavaScript의 적용을 소개하고 코드 예제를 제공하며 구현 원칙을 자세히 설명합니다. 1. 웹소켓 기술

JavaScript 및 WebSocket을 사용하여 실시간 온라인 주문 시스템을 구현하는 방법 JavaScript 및 WebSocket을 사용하여 실시간 온라인 주문 시스템을 구현하는 방법 Dec 17, 2023 pm 12:09 PM

JavaScript 및 WebSocket을 사용하여 실시간 온라인 주문 시스템을 구현하는 방법 소개: 인터넷의 대중화와 기술의 발전으로 점점 더 많은 레스토랑에서 온라인 주문 서비스를 제공하기 시작했습니다. 실시간 온라인 주문 시스템을 구현하기 위해 JavaScript 및 WebSocket 기술을 사용할 수 있습니다. WebSocket은 TCP 프로토콜을 기반으로 하는 전이중 통신 프로토콜로 클라이언트와 서버 간의 실시간 양방향 통신을 실현할 수 있습니다. 실시간 온라인 주문 시스템에서는 사용자가 요리를 선택하고 주문을 하면

WebSocket과 JavaScript를 사용하여 온라인 예약 시스템을 구현하는 방법 WebSocket과 JavaScript를 사용하여 온라인 예약 시스템을 구현하는 방법 Dec 17, 2023 am 09:39 AM

WebSocket과 JavaScript를 사용하여 온라인 예약 시스템을 구현하는 방법 오늘날의 디지털 시대에는 점점 더 많은 기업과 서비스에서 온라인 예약 기능을 제공해야 합니다. 효율적인 실시간 온라인 예약 시스템을 구현하는 것이 중요합니다. 이 기사에서는 WebSocket과 JavaScript를 사용하여 온라인 예약 시스템을 구현하는 방법을 소개하고 구체적인 코드 예제를 제공합니다. 1. WebSocket이란 무엇입니까? WebSocket은 단일 TCP 연결의 전이중 방식입니다.

JavaScript와 WebSocket: 효율적인 실시간 일기예보 시스템 구축 JavaScript와 WebSocket: 효율적인 실시간 일기예보 시스템 구축 Dec 17, 2023 pm 05:13 PM

JavaScript 및 WebSocket: 효율적인 실시간 일기 예보 시스템 구축 소개: 오늘날 일기 예보의 정확성은 일상 생활과 의사 결정에 매우 중요합니다. 기술이 발전함에 따라 우리는 날씨 데이터를 실시간으로 획득함으로써 보다 정확하고 신뢰할 수 있는 일기예보를 제공할 수 있습니다. 이 기사에서는 JavaScript 및 WebSocket 기술을 사용하여 효율적인 실시간 일기 예보 시스템을 구축하는 방법을 알아봅니다. 이 문서에서는 특정 코드 예제를 통해 구현 프로세스를 보여줍니다. 우리

간단한 JavaScript 튜토리얼: HTTP 상태 코드를 얻는 방법 간단한 JavaScript 튜토리얼: HTTP 상태 코드를 얻는 방법 Jan 05, 2024 pm 06:08 PM

JavaScript 튜토리얼: HTTP 상태 코드를 얻는 방법, 특정 코드 예제가 필요합니다. 서문: 웹 개발에서는 서버와의 데이터 상호 작용이 종종 포함됩니다. 서버와 통신할 때 반환된 HTTP 상태 코드를 가져와서 작업의 성공 여부를 확인하고 다양한 상태 코드에 따라 해당 처리를 수행해야 하는 경우가 많습니다. 이 기사에서는 JavaScript를 사용하여 HTTP 상태 코드를 얻는 방법과 몇 가지 실용적인 코드 예제를 제공합니다. XMLHttpRequest 사용

자바스크립트에서 insertBefore를 사용하는 방법 자바스크립트에서 insertBefore를 사용하는 방법 Nov 24, 2023 am 11:56 AM

사용법: JavaScript에서 insertBefore() 메서드는 DOM 트리에 새 노드를 삽입하는 데 사용됩니다. 이 방법에는 삽입할 새 노드와 참조 노드(즉, 새 노드가 삽입될 노드)라는 두 가지 매개 변수가 필요합니다.

JavaScript 및 WebSocket: 효율적인 실시간 이미지 처리 시스템 구축 JavaScript 및 WebSocket: 효율적인 실시간 이미지 처리 시스템 구축 Dec 17, 2023 am 08:41 AM

JavaScript는 웹 개발에 널리 사용되는 프로그래밍 언어인 반면 WebSocket은 실시간 통신에 사용되는 네트워크 프로토콜입니다. 두 가지의 강력한 기능을 결합하면 효율적인 실시간 영상 처리 시스템을 만들 수 있습니다. 이 기사에서는 JavaScript와 WebSocket을 사용하여 이 시스템을 구현하는 방법을 소개하고 구체적인 코드 예제를 제공합니다. 첫째, 실시간 영상처리 시스템의 요구사항과 목표를 명확히 할 필요가 있다. 실시간 이미지 데이터를 수집할 수 있는 카메라 장치가 있다고 가정해 보겠습니다.

See all articles