자바 펼쳐에서 딥하는 방법
Javascript 객체를 어떻게 심층 복제합니까?
파운더하는-frame 내가 워크를-based으로 다양한 기능 현관이 알고 JSON.parse(JSON.stringify(o))
하고 $.extend(true, {}, o)
있지만, 그와 같은 프레임 워크를 사용하지 않습니다 .
딥 생성을 생성하는 가장 우아하고 효율적인 방법은 무엇입니까?
우리는 어레이 복제와 같은 엣지 케이스에 관심이 있습니다. 전용 타입 체인을 깨지 않고 자체 참조를 처리합니다.
우리는 DOM 객체의 복사를 지원하는 데 관심이 없습니다 .cloneNode
. 그 이유 때문에 존재하기 때문입니다.
주로 node.js
V8 엔진의 ES5 기능 을 사용할 때 사용할 수 있습니다.
[편집하다]
제안하기 전에 누군가 object-에서 프로토-type으로 상속하여 복제본을 생성하는 것과 복제 하는을 구석으로 사이에는 뚜렷한 차이가 있습니다. 전자는 타입으로 체인을 엉망으로 만듭니다.
[추가 편집]
귀하의 답변을 읽은 후 전체 개체를 복제하는 것이 매우 위험하고 어려운 게임이라는 성가신 발견에 도달했습니다. 예를 들어 다음 클로저 기반을 가지고 있습니다.
var o = (function() {
var magic = 42;
var magicContainer = function() {
this.get = function() { return magic; };
this.set = function(i) { magic = i; };
}
return new magicContainer;
}());
var n = clone(o); // how to implement clone to support closures
객체를 복제하고 복제 시점에 동일한 상태를 o
JS에서 JS 파서 작성하지 않고 는 상태를 설명 수없는 복제 함수를 작성하는 방법이 있습니까?
더 이상 기능이 필요하지 않습니다. 이것은 단순한 학문적 관심사입니다.
복제하려는 항목에 따라. 이 진정한 진정한 JSON 객체입니까 아니면 JavaScript의 객체입니까? 복제를 수행 한 경우 문제가 발생합니다. 어떤 문제? 아래에서 설명하겠습니다. 먼저 객체 리터럴, 프리미티브, 배열 및 DOM 노드를 복제하는 코드 예제입니다.
function clone(item) {
if (!item) { return item; } // null, undefined values check
var types = [ Number, String, Boolean ],
result;
// normalizing primitives if someone did new String('aaa'), or new Number('444');
types.forEach(function(type) {
if (item instanceof type) {
result = type( item );
}
});
if (typeof result == "undefined") {
if (Object.prototype.toString.call( item ) === "[object Array]") {
result = [];
item.forEach(function(child, index, array) {
result[index] = clone( child );
});
} else if (typeof item == "object") {
// testing that this is DOM
if (item.nodeType && typeof item.cloneNode == "function") {
result = item.cloneNode( true );
} else if (!item.prototype) { // check that this is a literal
if (item instanceof Date) {
result = new Date(item);
} else {
// it is an object literal
result = {};
for (var i in item) {
result[i] = clone( item[i] );
}
}
} else {
// depending what you would like here,
// just keep the reference, or create new object
if (false && item.constructor) {
// would not advice to do that, reason? Read below
result = new item.constructor();
} else {
result = item;
}
}
} else {
result = item;
}
}
return result;
}
var copy = clone({
one : {
'one-one' : new String("hello"),
'one-two' : [
"one", "two", true, "four"
]
},
two : document.createElement("div"),
three : [
{
name : "three-one",
number : new Number("100"),
obj : new function() {
this.name = "Object test";
}
}
]
})
이제 REAL 복제를 보관할 때 보관 수있는 문제에 대해 이야기하겠습니다. 나는 지금 당신이 다음과 같은 것을 창조하고 있습니다.
var User = function(){}
var newuser = new User();
물론 도메인을 복제 할 수 있습니다. 그것은 문제가되지 않습니다. 모든 클래스 생성자 속성을 노출하고 객체를 복제하는 데 사용할 수 있습니다. for in
이 개체에 대해 간단하게 진행할 수 있습니다. 또한 코드 복제 기능을 포함했지만 if( false )
문에 의해 제외되었습니다 .
복제 복제가 왜 고통 스러울 수 있습니까? 우선 모든 수업 / 인스턴스에는 상태가있을 수 있습니다. 예를 들어 확신에 개인 변수가 없는지 확신 할 수 있습니다.
상태가 상상 상상합니다. 괜찮습니다. 여전히 또 다른 문제가 있습니다. "생성자"방법을 용이하게 복제하는 또 다른 장애물을 제공합니다. 인수입니다. 이 개체를 만든 사람이 어떤 종류의
new User({
bike : someBikeInstance
});
이 경우 운이 좋지 않은 경우 someBikeInstance가 포함되지 않은 경우 생성 폐지 수 있고 해당되는 복제 메소드에 대해 알 수 없습니다.
그래서 뭘 할건데? 은 여전히 당신 for in
솔루션을 할 수 있고 그러한 object-를 또는 일반 object-리터럴처럼 취급 할 수 있지만, 그러한 객체를 전혀 복제하지 않고 단지이 객체의 참조를 전달하는 것이 아이디어일까요 ?
또 다른 접근은 복제해야하는 모든 객체가 부분을 스스로 구현하고 API 메서드 (예 : cloneObject)를 제공하는 규칙을 복제해야합니다. 뭔가 뭔가 cloneNode
DOM에 대해하고있다.
당신이 결정합니다.
아주 간단한 방법, 어쩌면 너무 간단 할 수도 있습니다.
var cloned = JSON.parse(JSON.stringify(objectToClone));
JSON.parse(JSON.stringify())
딥 카피 자바 상상 의 조합은 JSON 데이터를위한 것이기 때문에 비효율적 인 해킹입니다. 자바 스크립트를 JSON으로 "문자열 화"(마샬링 할 때 무시합니다 )undefined
또는 샬링 값을 지원하지 않습니다 .function () {}
null
더 나은 해결책은 전체 복사 기능을 사용하는 것입니다. 아래 함수는 필요하지 않은 딥 복사하며 라이브러리 (jQuery, LoDash 등)가 있습니다.
function copy(aObject) {
if (!aObject) {
return aObject;
}
let v;
let bObject = Array.isArray(aObject) ? [] : {};
for (const k in aObject) {
v = aObject[k];
bObject[k] = (typeof v === "object") ? copy(v) : v;
}
return bObject;
}
다음은 순환 참조가있는 객체 작동하는 ES6 함수입니다.
function deepClone(obj, hash = new WeakMap()) {
if (Object(obj) !== obj) return obj; // primitives
if (obj instanceof Set) return new Set(obj); // See note about this!
if (hash.has(obj)) return hash.get(obj); // cyclic reference
const result = obj instanceof Date ? new Date(obj)
: obj instanceof RegExp ? new RegExp(obj.source, obj.flags)
: obj.constructor ? new obj.constructor()
: Object.create(null);
hash.set(obj, result);
if (obj instanceof Map)
Array.from(obj, ([key, val]) => result.set(key, deepClone(val, hash)) );
return Object.assign(result, ...Object.keys(obj).map (
key => ({ [key]: deepClone(obj[key], hash) }) ));
}
// Sample data
var p = {
data: 1,
children: [{
data: 2,
parent: null
}]
};
p.children[0].parent = p;
var q = deepClone(p);
console.log(q.children[0].parent.data); // 1
세트 및 맵에 대한 참고 사항
세트 및 맵의 키를 다루는 방법은 논쟁의 여지가 있습니다. 이러한 키는 종종 원시적이지만 (이 경우 논쟁이 없음) object-가 될 수도 있습니다. 이 경우 질문은 다음과 가변합니다. 해당 키를 복제해야합니까?
이 작업을 수행해야한다고 주장 할 수 있기 때문에 해당 개체가 복사본에서 변경하면 원본의 개체는 영향을받지 않습니다.
반면에 키 has
를 설정 / 매핑 하는 경우 원본과 사본 모두에서 사실이어야합니다. 그냥 둘 중 하나에 변경이 이루어지기 전에 말입니다 . 복사본이 이전에 발생하지 않은 키가있는 Set / Map이면 이상 할 것 (복제 프로세스 생성 되었기 때문에). 입력하거나 입력하지 않습니다.
아시다시피 저는 두 번째 의견에 가깝습니다. 세트 및 맵의 키 는 동일하게 유지되어야하는 값 ( 참고 문헌 일 수 있음 )입니다.
사용자 정의는 사용자 정의 개체와 함께 표시됩니다. 복제 된 작동 개체가 특정 경우에 어떻게 작동할지에 따라 달라지는 해결 방법은 없습니다.
Underscore.js있는 기여 라이브러리 라이브러리 라이브러리라는 함수가 스냅 샷을 깊은 등장 오브젝트 그
소스의 스 니펫 :
snapshot: function(obj) {
if(obj == null || typeof(obj) != 'object') {
return obj;
}
var temp = new obj.constructor();
for(var key in obj) {
if (obj.hasOwnProperty(key)) {
temp[key] = _.snapshot(obj[key]);
}
}
return temp;
}
라이브러리가 프로젝트에 연결 간단히 다음을 사용하여 함수를 호출합니다.
_.snapshot(object);
이것이 제가 사용하는 딥 클로닝 방법입니다. 좋습니다. 제안 해 주시기 바랍니다.
function deepClone (obj) {
var _out = new obj.constructor;
var getType = function (n) {
return Object.prototype.toString.call(n).slice(8, -1);
}
for (var _key in obj) {
if (obj.hasOwnProperty(_key)) {
_out[_key] = getType(obj[_key]) === 'Object' || getType(obj[_key]) === 'Array' ? deepClone(obj[_key]) : obj[_key];
}
}
return _out;
}
이제 Underscore.js 의 상위 집합 인 Lo-Dash 에는 몇 가지 가지 딥 기능이 있습니다.
_.cloneDeep(object)
_.cloneDeepWith(object, (val) => {if(_.isElement(val)) return val.cloneNode(true)})
두 번째 매개 변수는 복제 된 값을 생성하기 위해 호출되는 함수입니다.
lodash underscore
최신 안정 버전의 Underscore와의 가용성을 보장하기 위해 빌드가 제공됩니다.
다른 사람들이 이것과 질문에 대해 언급했듯이, 자바 스크립트에서 "객체"를 복제합니다.
그러나 "데이터"객체라고 부르는 클래스가 있습니다. 즉, 간소화 된 { ... }
속성 할당으로 구성하거나 복제하려는 것이 JSON에서 역화 배열 입니다. 오늘은 설치 데이터 세트에 대해 어떤 일이 발생하는지 테스트하기 위해 서버에서 수신 한 데이터를 인위적으로 5 배 확장하고 싶고 사물 (배열)과 그는 사물이 제대로 작동 할 수 있습니다. 복제를 통해이 작업을 수행하여 데이터 세트를 늘릴 수 있습니다.
return dta.concat(clone(dta),clone(dta),clone(dta),clone(dta));
내가 종종 데이터 개체를 복제하는 다른 곳은 데이터를 보내기 전에 데이터 모델의 개체에서 상태 필드를 제거하려는 호스트에 데이터를 다시 출시하는 것입니다. 예를 들어, 복제 될 때 개체에서 "_"로 시작하는 모든 필드를 제거 할 수 있습니다.
다음은 복제 할 멤버를 선택하는 선택기와 지원 배열 (컨텍스트를 결정하기 위해 "경로"문자열 사용)을 포함하여 일반적 으로이 작업을 수행하기 위해 코드입니다.
function clone(obj,sel) {
return (obj ? _clone("",obj,sel) : obj);
}
function _clone(pth,src,sel) {
var ret=(src instanceof Array ? [] : {});
for(var key in src) {
if(!src.hasOwnProperty(key)) { continue; }
var val=src[key], sub;
if(sel) {
sub+=pth+"/"+key;
if(!sel(sub,key,val)) { continue; }
}
if(val && typeof(val)=='object') {
if (val instanceof Boolean) { val=Boolean(val); }
else if(val instanceof Number ) { val=Number (val); }
else if(val instanceof String ) { val=String (val); }
else { val=_clone(sub,val,sel); }
}
ret[key]=val;
}
return ret;
}
Null이 아닌 루트 개체를 가정하고 멤버 선택이 가정하는 가장 간단하고 합리적인 솔루션은 다음과 가능합니다.
function clone(src) {
var ret=(src instanceof Array ? [] : {});
for(var key in src) {
if(!src.hasOwnProperty(key)) { continue; }
var val=src[key];
if(val && typeof(val)=='object') { val=clone(val); }
ret[key]=val;
}
return ret;
}
아래에서 함수는 자바 발현 객체를 딥 열성하는 가장 쉬운 방법입니다.
function deepCopy(obj){
if (!obj || typeof obj !== "object") return obj;
var retObj = {};
for (var attr in obj){
var type = obj[attr];
switch(true){
case (type instanceof Date):
var _d = new Date();
_d.setDate(type.getDate())
retObj[attr]= _d;
break;
case (type instanceof Function):
retObj[attr]= obj[attr];
break;
case (type instanceof Array):
var _a =[];
for (var e of type){
//_a.push(e);
_a.push(deepCopy(e));
}
retObj[attr]= _a;
break;
case (type instanceof Object):
var _o ={};
for (var e in type){
//_o[e] = type[e];
_o[e] = deepCopy(type[e]);
}
retObj[attr]= _o;
break;
default:
retObj[attr]= obj[attr];
}
}
return retObj;
}
var obj = {
string: 'test',
array: ['1'],
date: new Date(),
object:{c: 2, d:{e: 3}},
function: function(){
return this.date;
}
};
var copyObj = deepCopy(obj);
console.log('object comparison', copyObj === obj); //false
console.log('string check', copyObj.string === obj.string); //true
console.log('array check', copyObj.array === obj.array); //false
console.log('date check', copyObj2.date === obj.date); //false
console.log('object check', copyObj.object === obj.object); //false
console.log('function check', copyObj.function() === obj.function()); //true
Map은 특별한 처리가 필요합니다.
function deepClone( obj ) {
if( !obj || true == obj ) //this also handles boolean as true and false
return obj;
var objType = typeof( obj );
if( "number" == objType || "string" == objType ) // add your immutables here
return obj;
var result = Array.isArray( obj ) ? [] : !obj.constructor ? {} : new obj.constructor();
if( obj instanceof Map )
for( var key of obj.keys() )
result.set( key, deepClone( obj.get( key ) ) );
for( var key in obj )
if( obj.hasOwnProperty( key ) )
result[key] = deepClone( obj[ key ] );
return result;
}
더 이상 기능이 필요하지 않습니다. 이것은 단순한 학문적 관심사입니다.
순전히 운동이기 때문에 더 기능적인 방법입니다. 그것은의 확장입니다 @ tfmontague 대답의 으로 내가 제안했던 가드 블록이 추가. 그러나 내가 ES6에 강요하고 모든 것을 기능화하고 느끼는 것을 보면, 여기 내 포주 버전이 있습니다. 배열을 매핑하고 배열을 복잡하게 만듭니다.
function cloner(x) {
const recurseObj = x => typeof x === 'object' ? cloner(x) : x
const cloneObj = (y, k) => {
y[k] = recurseObj(x[k])
return y
}
// Guard blocks
// Add extra for Date / RegExp if you want
if (!x) {
return x
}
if (Array.isArray(x)) {
return x.map(recurseObj)
}
return Object.keys(x).reduce(cloneObj, {})
}
const tests = [
null,
[],
{},
[1,2,3],
[1,2,3, null],
[1,2,3, null, {}],
[new Date('2001-01-01')], // FAIL doesn't work with Date
{x:'', y: {yx: 'zz', yy: null}, z: [1,2,3,null]},
{
obj : new function() {
this.name = "Object test";
}
} // FAIL doesn't handle functions
]
tests.map((x,i) => console.log(i, cloner(x)))
모든 답변에 대한 나의 추가
deepCopy = arr => {
if (typeof arr !== 'object') return arr
if(arr.pop) return [...arr].map(deepCopy)
const copy = {}
for (let prop in arr)
copy[prop] = deepCopy(arr[prop])
return copy
}
배열, 배열 및 기본 요소에 대해 작동합니다. 두 개의 순회 방법 사이를 전환하는 이중 재귀 알고리즘 :
const deepClone = (objOrArray) => {
const copyArray = (arr) => {
let arrayResult = [];
arr.forEach(el => {
arrayResult.push(cloneObjOrArray(el));
});
return arrayResult;
}
const copyObj = (obj) => {
let objResult = {};
for (key in obj) {
if (obj.hasOwnProperty(key)) {
objResult[key] = cloneObjOrArray(obj[key]);
}
}
return objResult;
}
const cloneObjOrArray = (el) => {
if (Array.isArray(el)) {
return copyArray(el);
} else if (typeof el === 'object') {
return copyObj(el);
} else {
return el;
}
}
return cloneObjOrArray(objOrArray);
}
우리는 deepCopy를 사용하여 재귀를 사용할 수 있습니다. 배열, 객체, 배열, 기능이있는 객체의 복사본을 생성합니다. 원하는 경우지도 생성 같은 다른 유형의 데이터 구조에 대한 기능을 추가 할 수 있습니다.
function deepClone(obj) {
var retObj;
_assignProps = function(obj, keyIndex, retObj) {
var subType = Object.prototype.toString.call(obj[keyIndex]);
if(subType === "[object Object]" || subType === "[object Array]") {
retObj[keyIndex] = deepClone(obj[keyIndex]);
}
else {
retObj[keyIndex] = obj[keyIndex];
}
};
if(Object.prototype.toString.call(obj) === "[object Object]") {
retObj = {};
for(key in obj) {
this._assignProps(obj, key, retObj);
}
}
else if(Object.prototype.toString.call(obj) == "[object Array]") {
retObj = [];
for(var i = 0; i< obj.length; i++) {
this._assignProps(obj, i, retObj);
}
};
return retObj;
};
immutableJS 사용
import { fromJS } from 'immutable';
// An object we want to clone
let objA = {
a: { deep: 'value1', moreDeep: {key: 'value2'} }
};
let immB = fromJS(objA); // Create immutable Map
let objB = immB.toJS(); // Convert to plain JS object
console.log(objA); // Object { a: { deep: 'value1', moreDeep: {key: 'value2'} } }
console.log(objB); // Object { a: { deep: 'value1', moreDeep: {key: 'value2'} } }
// objA and objB are equalent, but now they and their inner objects are undependent
console.log(objA === objB); // false
console.log(objA.a === objB.a); // false
console.log(objA.moreDeep === objB.moreDeep); // false
import merge from 'lodash/merge'
var objA = {
a: [{ 'b': 2 }, { 'd': 4 }]
};
// New deeply cloned object:
merge({}, objA );
// We can also create new object from several objects by deep merge:
var objB = {
a: [{ 'c': 3 }, { 'e': 5 }]
};
merge({}, objA , objB ); // Object { a: [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }
이 순환 참조를 사용하여 나를 위해 작동합니다.
//a test-object with circular reference :
var n1 = { id:0, text:"aaaaa", parent:undefined}
var n2 = { id:1, text:"zzzzz", parent:undefined }
var o = { arr:[n1,n2], parent:undefined }
n1.parent = n2.parent = o;
var obj = { a:1, b:2, o:o }
o.parent = obj;
function deepClone(o,output){
if(!output) output = {};
if(o.______clone) return o.______clone;
o.______clone = output.______clone = output;
for(var z in o){
var obj = o[z];
if(typeof(obj) == "object") output[z] = deepClone(obj)
else output[z] = obj;
}
return output;
}
console.log(deepClone(obj));
참고 URL : https://stackoverflow.com/questions/4459928/how-to-deep-clone-in-javascript
'IT' 카테고리의 다른 글
JSON.Net 자체 참조 루프 감지 (0) | 2020.08.31 |
---|---|
Laravel- 요청시 세션 저장소가 설정되지 않음 (0) | 2020.08.31 |
함수형 프로그래밍 (Scala 및 Scala API)에서 reduce와 foldLeft / fold의 차이점은 무엇입니까? (0) | 2020.08.31 |
정적 제네릭 메서드 호출 (0) | 2020.08.30 |
전체 커밋이 아닌 하나의 파일에 변경 사항을 선택하는 방법 (0) | 2020.08.30 |