>웹 프론트엔드 >JS 튜토리얼 >JavaScript deep clone_javascript 기술 구현 방법

JavaScript deep clone_javascript 기술 구현 방법

WBOY
WBOY원래의
2016-05-16 15:14:441388검색

"속성 복사 패턴"이라는 코드 재사용 패턴 유형이 있습니다. 코드 재사용에 대해 생각할 때 코드 상속을 생각할 가능성이 가장 높지만 궁극적인 목표, 즉 코드를 재사용하고 싶다는 것을 기억하는 것이 중요합니다. 상속은 코드 재사용을 위한 수단일 뿐 유일한 방법은 아닙니다. 속성을 복사하는 것도 상속과 다른 재사용 패턴입니다. 이 패턴에서 개체는 단순히 멤버를 복사하여 다른 개체에서 멤버를 얻습니다. jQuery를 사용해 본 사람이라면 이 메소드에 $.extend() 메소드가 있다는 것을 알 것입니다. 타사 플러그인을 확장하는 것 외에도 속성을 복사하는 데에도 사용할 수 있습니다. extend() 함수의 구현 코드를 살펴보겠습니다. (이것은 jQuery의 소스 코드가 아니며 단순한 예입니다.)

function extend(parent, child) {
var i;
//如果不传入第二参数child
//那么就创建一个新的对象
child = child || {}; 
//遍历parent对象的所有属性
//并且过滤原型上的属性
//然后将自身属性复制到child对象上
for(i in parent) {
if(parent.hasOwnProperty(i)) {
child[i] = parent[i];
}
}
//返回目标对象child
return child;
} 

위 코드는 간단한 구현으로, 상위 개체의 멤버만 순회하여 하위 개체에 복사합니다. 위의 확장() 메서드를 사용하여 테스트해 보겠습니다.

var dad = {name: "Adam"};
var kid = extend(dad);
console.log(kid.name); //Adam 

extend() 메소드가 이미 정상적으로 작동하는 것을 확인했습니다. 그러나 위에 제시된 것은 소위 얕은 복제(shallow clone)라는 문제입니다. 얕은 복사를 사용할 때 하위 개체의 속성이 변경되고 해당 속성이 개체인 경우 이 작업으로 인해 상위 개체도 수정되는 경우가 많지만 이는 우리가 원하는 결과가 아닙니다. 다음 사항을 고려하세요.

var dad = {
counts: [1, 2, 3],
reads: {paper: true}
};
var kid = extend(dad) //调用extend()方法将dad的属性复制到kid上面
kid.counts.push(4); //把4追加到kid.counts数组里面
console.log(dad.counts); //[1, 2, 3, 4] 

위의 예를 통해 kid.counts 속성을 수정한 후(요소 4 추가), father.counts도 영향을 받는다는 것을 알 수 있습니다. 이는 얕은 복사를 사용할 때 객체가 참조로 전달되기 때문에, 즉 kid.counts와 Dad.counts가 동일한 배열을 가리키기 때문입니다(또는 메모리에서는 동일한 힙 주소를 가리키기 때문입니다).

다음으로, 확장() 함수를 수정하여 전체 복사를 구현해 보겠습니다. 우리가 해야 할 일은 상위 객체의 각 속성을 확인하고, 해당 속성이 객체인 경우 해당 객체의 속성을 재귀적으로 복사하는 것입니다. 또한 객체가 배열인지 여부도 감지해야 합니다. 이는 배열의 리터럴 생성 방법이 객체의 리터럴 생성 방법과 다르기 때문입니다. 배열을 감지하려면 Object.prototype.toString() 메서드를 사용할 수 있습니다. 배열인 경우 "[object Array]"가 반환됩니다. 딥 카피 버전의 확장() 기능을 살펴보겠습니다.

function extendDeep(parent, child) {
child = child || {};
for(var i in parent) {
if(parent.hasOwnProperty(i)) {
//检测当前属性是否为对象
if(typeof parent[i] === "object") {
//如果当前属性为对象,还要检测它是否为数组
//这是因为数组的字面量表示和对象的字面量表示不同
//前者是[],而后者是{}
child[i] = (Object.prototype.toString.call(parent[i]) === "[object Array]") ? [] : {};
//递归调用extend
extendDeep(parent[i], child[i]);
} else {
child[i] = parent[i];
}
}
}
return child;
} 

자, 딥 카피 기능이 작성되었습니다. 예상대로 작동하는지, 즉 딥 카피가 가능한지 테스트해 보겠습니다.

var dad = {
counts: [1, 2, 3],
reads: {paper: true}
};
var kid = extendDeep(dad);
//修改kid的counts属性和reads属性
kid.counts.push(4);
kid.reads.paper = false;
console.log(kid.counts); //[1, 2, 3, 4]
console.log(kid.reads.paper); //false
console.log(dad.counts); //[1, 2, 3]
console.log(dad.reads.paper); //true 

위의 예를 통해 자식 객체의 kid.counts와 kid.reads가 수정되더라도 부모 객체의 father.counts와 kid.reads는 변경되지 않아 목적이 달성되었음을 알 수 있습니다.

다음은 Deep Copy 구현을 위한 기본 아이디어를 요약한 것입니다.

1. 현재 속성이 객체인지 확인하세요

2. 배열은 특수 객체이기 때문에 객체인 경우 속성이 배열인지 감지해야 합니다.

3. 배열인 경우 [] 빈 배열을 생성하고, 그렇지 않으면 {} 빈 객체를 생성하여 하위 객체의 현재 속성에 할당합니다. 그런 다음,extendDeep 함수가 재귀적으로 호출됩니다.

위의 예에서는 재귀 알고리즘을 사용하여 자체 구현한 딥 카피 방식을 사용합니다. 실제로 ES5의 새로운 JSON 개체가 제공하는 두 가지 메서드, 즉 JSON.stringify()와 JSON.parse()도 개체를 문자열로 변환하는 데 사용되며 후자는 사용됩니다. 문자열을 개체로 변환합니다. 아래에서는 이 방법을 사용하여 전체 복사 기능을 구현합니다.

function extendDeep(parent, child) {
var i,
proxy;
proxy = JSON.stringify(parent); //把parent对象转换成字符串
proxy = JSON.parse(proxy) //把字符串转换成对象,这是parent的一个副本
child = child || {};
for(i in proxy) {
if(proxy.hasOwnProperty(i)) {
child[i] = proxy[i];
}
}
proxy = null; //因为proxy是中间对象,可以将它回收掉
return child;
} 

다음은 테스트 예시입니다.

var dad = {
counts: [1, 2, 3],
reads: {paper: true}
};
var kid = extendDeep(dad);
//修改kid的counts属性和reads属性
kid.counts.push(4);
kid.reads.paper = false;
console.log(kid.counts); //[1, 2, 3, 4]
console.log(kid.reads.paper); //false
console.log(dad.counts); //[1, 2, 3]
console.log(dad.reads.paper); //true 

테스트 결과 딥 카피도 달성된 것으로 나타났습니다. JSON.parse 및 JSON.stringify는 내장 함수이고 처리 속도가 더 빠르기 때문에 일반적으로 후자의 방법을 사용하는 것이 좋습니다. 게다가 이전 방법은 재귀 호출을 사용합니다. 우리 모두는 재귀가 상대적으로 비효율적인 알고리즘이라는 것을 알고 있습니다.

JavaScript 딥클론 구현 방법에 대한 모든 것입니다. 도움이 되셨으면 좋겠습니다!

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