찾다
웹 프론트엔드JS 튜토리얼이 기사에서는 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명
이 기사는 csdn에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제
JavaScript 및 웹 : 핵심 기능 및 사용 사례JavaScript 및 웹 : 핵심 기능 및 사용 사례Apr 18, 2025 am 12:19 AM

웹 개발에서 JavaScript의 주요 용도에는 클라이언트 상호 작용, 양식 검증 및 비동기 통신이 포함됩니다. 1) DOM 운영을 통한 동적 컨텐츠 업데이트 및 사용자 상호 작용; 2) 사용자가 사용자 경험을 향상시키기 위해 데이터를 제출하기 전에 클라이언트 확인이 수행됩니다. 3) 서버와의 진실한 통신은 Ajax 기술을 통해 달성됩니다.

JavaScript 엔진 이해 : 구현 세부 사항JavaScript 엔진 이해 : 구현 세부 사항Apr 17, 2025 am 12:05 AM

보다 효율적인 코드를 작성하고 성능 병목 현상 및 최적화 전략을 이해하는 데 도움이되기 때문에 JavaScript 엔진이 내부적으로 작동하는 방식을 이해하는 것은 개발자에게 중요합니다. 1) 엔진의 워크 플로에는 구문 분석, 컴파일 및 실행; 2) 실행 프로세스 중에 엔진은 인라인 캐시 및 숨겨진 클래스와 같은 동적 최적화를 수행합니다. 3) 모범 사례에는 글로벌 변수를 피하고 루프 최적화, Const 및 Lets 사용 및 과도한 폐쇄 사용을 피하는 것이 포함됩니다.

Python vs. JavaScript : 학습 곡선 및 사용 편의성Python vs. JavaScript : 학습 곡선 및 사용 편의성Apr 16, 2025 am 12:12 AM

Python은 부드러운 학습 곡선과 간결한 구문으로 초보자에게 더 적합합니다. JavaScript는 가파른 학습 곡선과 유연한 구문으로 프론트 엔드 개발에 적합합니다. 1. Python Syntax는 직관적이며 데이터 과학 및 백엔드 개발에 적합합니다. 2. JavaScript는 유연하며 프론트 엔드 및 서버 측 프로그래밍에서 널리 사용됩니다.

Python vs. JavaScript : 커뮤니티, 라이브러리 및 리소스Python vs. JavaScript : 커뮤니티, 라이브러리 및 리소스Apr 15, 2025 am 12:16 AM

Python과 JavaScript는 커뮤니티, 라이브러리 및 리소스 측면에서 고유 한 장점과 단점이 있습니다. 1) Python 커뮤니티는 친절하고 초보자에게 적합하지만 프론트 엔드 개발 리소스는 JavaScript만큼 풍부하지 않습니다. 2) Python은 데이터 과학 및 기계 학습 라이브러리에서 강력하며 JavaScript는 프론트 엔드 개발 라이브러리 및 프레임 워크에서 더 좋습니다. 3) 둘 다 풍부한 학습 리소스를 가지고 있지만 Python은 공식 문서로 시작하는 데 적합하지만 JavaScript는 MDNWebDocs에서 더 좋습니다. 선택은 프로젝트 요구와 개인적인 이익을 기반으로해야합니다.

C/C에서 JavaScript까지 : 모든 것이 어떻게 작동하는지C/C에서 JavaScript까지 : 모든 것이 어떻게 작동하는지Apr 14, 2025 am 12:05 AM

C/C에서 JavaScript로 전환하려면 동적 타이핑, 쓰레기 수집 및 비동기 프로그래밍으로 적응해야합니다. 1) C/C는 수동 메모리 관리가 필요한 정적으로 입력 한 언어이며 JavaScript는 동적으로 입력하고 쓰레기 수집이 자동으로 처리됩니다. 2) C/C를 기계 코드로 컴파일 해야하는 반면 JavaScript는 해석 된 언어입니다. 3) JavaScript는 폐쇄, 프로토 타입 체인 및 약속과 같은 개념을 소개하여 유연성과 비동기 프로그래밍 기능을 향상시킵니다.

JavaScript 엔진 : 구현 비교JavaScript 엔진 : 구현 비교Apr 13, 2025 am 12:05 AM

각각의 엔진의 구현 원리 및 최적화 전략이 다르기 때문에 JavaScript 엔진은 JavaScript 코드를 구문 분석하고 실행할 때 다른 영향을 미칩니다. 1. 어휘 분석 : 소스 코드를 어휘 단위로 변환합니다. 2. 문법 분석 : 추상 구문 트리를 생성합니다. 3. 최적화 및 컴파일 : JIT 컴파일러를 통해 기계 코드를 생성합니다. 4. 실행 : 기계 코드를 실행하십시오. V8 엔진은 즉각적인 컴파일 및 숨겨진 클래스를 통해 최적화하여 Spidermonkey는 유형 추론 시스템을 사용하여 동일한 코드에서 성능이 다른 성능을 제공합니다.

브라우저 너머 : 실제 세계의 JavaScript브라우저 너머 : 실제 세계의 JavaScriptApr 12, 2025 am 12:06 AM

실제 세계에서 JavaScript의 응용 프로그램에는 서버 측 프로그래밍, 모바일 애플리케이션 개발 및 사물 인터넷 제어가 포함됩니다. 1. 서버 측 프로그래밍은 Node.js를 통해 실현되며 동시 요청 처리에 적합합니다. 2. 모바일 애플리케이션 개발은 재교육을 통해 수행되며 크로스 플랫폼 배포를 지원합니다. 3. Johnny-Five 라이브러리를 통한 IoT 장치 제어에 사용되며 하드웨어 상호 작용에 적합합니다.

Next.js (백엔드 통합)로 멀티 테넌트 SAAS 애플리케이션 구축Next.js (백엔드 통합)로 멀티 테넌트 SAAS 애플리케이션 구축Apr 11, 2025 am 08:23 AM

일상적인 기술 도구를 사용하여 기능적 다중 테넌트 SaaS 응용 프로그램 (Edtech 앱)을 구축했으며 동일한 작업을 수행 할 수 있습니다. 먼저, 다중 테넌트 SaaS 응용 프로그램은 무엇입니까? 멀티 테넌트 SAAS 응용 프로그램은 노래에서 여러 고객에게 서비스를 제공 할 수 있습니다.

See all articles

핫 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

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

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

SecList

SecList

SecLists는 최고의 보안 테스터의 동반자입니다. 보안 평가 시 자주 사용되는 다양한 유형의 목록을 한 곳에 모아 놓은 것입니다. SecLists는 보안 테스터에게 필요할 수 있는 모든 목록을 편리하게 제공하여 보안 테스트를 더욱 효율적이고 생산적으로 만드는 데 도움이 됩니다. 목록 유형에는 사용자 이름, 비밀번호, URL, 퍼징 페이로드, 민감한 데이터 패턴, 웹 셸 등이 포함됩니다. 테스터는 이 저장소를 새로운 테스트 시스템으로 간단히 가져올 수 있으며 필요한 모든 유형의 목록에 액세스할 수 있습니다.

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

ZendStudio 13.5.1 맥

ZendStudio 13.5.1 맥

강력한 PHP 통합 개발 환경