2010-02-14 5 views
20

자바 스크립트에서 객체를 정확하게 복제하려고했습니다. , 객체 유형이 분실되는 것을정확히 자바 스크립트에서 객체를 복제합니다.

var newObject = jQuery.extend({}, oldObject); 
// Or 
var newObject = jQuery.extend(true, {}, oldObject); 

하지만의 문제 :

var MyClass = function(param1, param2) { 
    alert(param1.a + param2.a); 
}; 
var myObj = new MyClass({a: 1},{a: 2}); 
var myObjClone = jQuery.extend(true, {}, myObj); 
alert(myObj instanceof MyClass);  // => true 
alert(myObjClone instanceof MyClass); // => false 

두 번째 경고에 진정한 얻을 수있는 모든 솔루션이 있는가 나는 다음과 같은 솔루션을 사용하여 jQuery를 알아?

+1

David, 다른 모든 복제 질문과의 차이점은 객체 유형 속성을 유지하는 방법을 묻는 것입니다. – Tom

답변

12

jQuery.extend는 instanceof 연산자를 사용할 것으로 예상하지 않습니다. 그것은 진정한 복제품이 아닌 영광스럽고 복잡한 복제품을 만들고 있습니다. 요소를 반복하는 것만으로는 충분하지 않습니다. 또한 생성자를 호출하는 것이 인수를 잃어 버리는 가장 좋은 원인은 아닙니다. 이 시도 :

var MyClass = function(param1, param2) { 
    alert(param1.a + param2.a); 
    this.p1 = param1; 
    this.p2 = param2; 
}; 

function Clone() { } 
function clone(obj) { 
    Clone.prototype = obj; 
    return new Clone(); 
} 

var myObj = new MyClass({a: 1},{a: 2}); 
var myObjClone = clone(myObj); 
alert(myObj instanceof MyClass);  // => true 
alert(myObjClone instanceof MyClass); // => true 
console.log(myObj);  //note they are 
console.log(myObjClone) //exactly the same 

는 프로토 타입이 이제 망하는 (myObj로) 다시 점 때문에, myObj로의 변경이 myObjClone에 반영된다는 점에 유의해야합니다. Javascript의 프로토 타입 상속은 다소 까다 롭습니다. 새로운 객체가 정확한 프로토 타입을 가지고 있는지, 그리고 올바른 생성자를 가지고 있는지 확인해야합니다.

Admitadly, Javascript가 머리를 아프게합니다. 그럼에도 불구하고, 나는 내가 ECMAScript language spec에서,이 권리를 읽고 있어요 생각 :

13.2.2 [[구축]을가 [[]를 구축합니다] Function 객체 F에 대한 내부 방법이 가능 불려
비어있는 인수 목록에서 다음 단계를 수행합니다.

  1. obj를 새로 만든 네이티브 ECMAScript 개체로 둡니다.
  2. 8.12에 지정된대로 obj의 모든 내부 메소드를 설정하십시오.
  3. obj의 [Class] 내부 속성을 "Object"로 설정하십시오.
  4. obj의 [Extensible]] 내부 속성을 true로 설정하십시오.
  5. proto는 인수> "prototype"인 F의 [[Get]] 내부 속성을 호출하는 값입니다.
  6. Type (proto)이 Object 인 경우 obj의 [Prototype]] 내부 속성을 proto로 설정하십시오.
  7. Type (proto)이 Object가 아닌 경우 15.2.4에서 설명한대로 표준 내장 객체 프로토 타입 객체로 obj의 [Prototype] 내부 속성을 설정합니다.
  8. 결과는 F의 [[Call]] 내부 속성을 호출하고,> obj를이 값으로 제공하고 [[Construct]]로 전달 된 인수 목록을 args로 제공 한 결과입니다.
  9. Type (결과)이 Object이면 결과를 반환합니다.
  10. 돌아 가기 obj.

This person는 나보다 훨씬 더 개념을 이해하는 것 같다. K, 나는 지금 자바로 돌아갈거야. 어디에서 수영 할까?

+0

Thas는 매우 훌륭한 솔루션입니다. 또한 복제본 프로토 타입의 모든 속성을 복제본에 복사하여 myObj에 대한 변경 사항이 myObjClone에 반영되지 않도록 할 수 있습니다. 그러나 다른 객체에서 복제를 다시 호출하면 myObjClone의 프로토 타입이 변경됩니까? – Tom

+0

"myNewObjClone = clone (myObj)"을 시도한 후 아니요, myObjClone의 프로토 타입을 변경하지 마십시오. Clone 객체는 clone 함수 내부에서만 존재하므로 clone (obj)을 호출 할 때마다 새로운 객체를 얻습니다. 이것은 클로저를 사용하여 변수 (이 경우 Clone 객체)를 "숨기기"위한 예제입니다. – Stephano

4

클론 function suggested here을 사용해 보셨습니까?

function clone(obj){ 
    if(obj == null || typeof(obj) != 'object'){ 
     return obj; 
    } 

    var temp = new obj.constructor(); 
    for(var key in obj){ 
     temp[key] = clone(obj[key]); 
    } 
    return temp; 
} 

var MyClass = function(param1, param2) {}; 
var myObj = new MyClass(1,2); 
var myObjClone = clone(myObj); 
alert(myObj instanceof MyClass);  // => true 
alert(myObjClone instanceof MyClass); // => true 
+0

좋은 시작이지만 생성자가 매개 변수를 필요로하는 경우 복제가 실패합니다. var MyClass = function (param1, param2) {alert (param1.테스트)}; – Tom

+0

Tom,이'clone' 함수가 예상되는 인수를 어디에서 얻을 것으로 기대합니까? 어떻게 알 수 있습니까? – James

+0

@ J-P - 그게 내 질문에 관한거야. 복제 할 개체에 대해 알지 못해도 형식 정보를 보존하는 정확한 복제본을 얻을 수있는 방법이 있습니까? 이제는 불가능하다고 생각합니다. – Tom

1
function clone(obj) { 
    var target = new obj.constructor(); 
    for (var key in target) { delete target[key]; } 
    return $.extend(true, target, obj); 
} 

$.extend는 보이지 않는 모든 내부 속성을 복사 할 수 없지만 (일부는 firefox에서 볼 수 있음), obj.constructor이 정확하고 args 없이는 오류가 발생하지 않으면 내부 속성을 new obj.constructor()으로 설정할 수 있습니다. Derived.prototype = new Base()과 같은 상속을 수행하는 경우 생성자를 올바르게 얻으려면 Derived.prototype.constructor = Derived을 따라야합니다.

당신은 $.extend(true, new obj.constructor(), obj)을 할 수 있지만 나중에 생성자가 생성자 인수를 얻을 수 있다고하더라도 생성자가 나중에 삭제 된 속성을 생성 할 수 있습니다. 이것이 확장을 수행하기 전에 속성을 삭제해야하는 이유입니다. 원래 생성자 args의 효과와 그 이후로 객체에 발생한 모든 다른 것들이 우리가 복제하는 객체에 있기 때문에 생성자 args가 잘못되었다는 것은 중요하지 않습니다.

1

문제는 '{}'에 복사 할 새 개체를 전달하는 것입니다. 이것이 당신의 타입을 잃은 이유입니다. 나는 그것을 전달하기 전에 실제 객체를 감싸고 나중에 복사 된 객체의 포장을 풀면 확장이 유형을 예상대로 유지한다는 것을 알았다.

function clone(obj) 
{ 
    var wrappedObj = { inner: obj }; 
    var newObject = jQuery.extend(true, {}, wrappedObj); 
    newObject = newObject.inner; 
    return newObject; 
} 
+0

이게 가장 좋은 대답 인 것 같아 고맙네, 너는 남자 야. –

2

I에 유래에서 발견 한 몇 가지 답변에서 영감을 복용 후, 나는 매우 유연하고, 여전히 객체 또는 하위 객체의 생성자에있을 때 작동하는 기능 왔어요 필수 매개 변수 (Object.create 덕분에) (.이 지금뿐만 아니라 순환 참조를 지원 저스틴 McCandless 덕분에)

//If Object.create isn't already defined, we just do the simple shim, without the second argument, 
//since that's all we need here 
var object_create = Object.create; 
if (typeof object_create !== 'function') { 
    object_create = function(o) { 
     function F() {} 
     F.prototype = o; 
     return new F(); 
    }; 
} 

/** 
* Deep copy an object (make copies of all its object properties, sub-properties, etc.) 
* An improved version of http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone 
* that doesn't break if the constructor has required parameters 
* 
* It also borrows some code from http://stackoverflow.com/a/11621004/560114 
*/ 
function deepCopy(src, /* INTERNAL */ _visited, _copiesVisited) { 
    if(src === null || typeof(src) !== 'object'){ 
     return src; 
    } 

    //Honor native/custom clone methods 
    if(typeof src.clone == 'function'){ 
     return src.clone(true); 
    } 

    //Special cases: 
    //Date 
    if(src instanceof Date){ 
     return new Date(src.getTime()); 
    } 
    //RegExp 
    if(src instanceof RegExp){ 
     return new RegExp(src); 
    } 
    //DOM Element 
    if(src.nodeType && typeof src.cloneNode == 'function'){ 
     return src.cloneNode(true); 
    } 

    // Initialize the visited objects arrays if needed. 
    // This is used to detect cyclic references. 
    if (_visited === undefined){ 
     _visited = []; 
     _copiesVisited = []; 
    } 

    // Check if this object has already been visited 
    var i, len = _visited.length; 
    for (i = 0; i < len; i++) { 
     // If so, get the copy we already made 
     if (src === _visited[i]) { 
      return _copiesVisited[i]; 
     } 
    } 

    //Array 
    if (Object.prototype.toString.call(src) == '[object Array]') { 
     //[].slice() by itself would soft clone 
     var ret = src.slice(); 

     //add it to the visited array 
     _visited.push(src); 
     _copiesVisited.push(ret); 

     var i = ret.length; 
     while (i--) { 
      ret[i] = deepCopy(ret[i], _visited, _copiesVisited); 
     } 
     return ret; 
    } 

    //If we've reached here, we have a regular object 

    //make sure the returned object has the same prototype as the original 
    var proto = (Object.getPrototypeOf ? Object.getPrototypeOf(src): src.__proto__); 
    if (!proto) { 
     proto = src.constructor.prototype; //this line would probably only be reached by very old browsers 
    } 
    var dest = object_create(proto); 

    //add this object to the visited array 
    _visited.push(src); 
    _copiesVisited.push(dest); 

    for (var key in src) { 
     //Note: this does NOT preserve ES5 property attributes like 'writable', 'enumerable', etc. 
     //For an example of how this could be modified to do so, see the singleMixin() function 
     dest[key] = deepCopy(src[key], _visited, _copiesVisited); 
    } 
    return dest; 
} 

이 기능은 내 simpleOO 라이브러리의 일부입니다; 어떤 버그 수정이나 개선이 이루어질 것입니다. (버그를 발견하면 github에서 문제를 자유롭게 열어보십시오.)

0

당신이 쓸 수 :

Object.prototype.clone = function() { 
    return eval(uneval(this)); 
} 

같이 사용할 수 있습니다 :

object1 = object2.clone(); 

대답은 시간을 발견했다 ere : source

하지만 Firefox 마술 일뿐입니다. 다른 브라우저가 여기에서 중단 될 수 있습니다.

+1

그거 악마 야. – Tom

0

정확하게 복사하거나 복제하지는 않지만 원하는 결과를 제공하는 대체 솔루션이 있습니다.

var myObj = new MyClass({a: 1},{a: 2}); 
var myObjCreator = MyClass.bind(this, {a: 1},{a: 2}); 
var myObjClone = new myObjCreator(); 

자동으로 MyClass의 생성자에 지정된 파라미터에 전달 객체를 생성하는 자바 스크립트의 bind 함수를 사용합니다.

나는 OP와 비슷한 요구 사항을 갖고 있었기 때문에이 게시물은 나에게 도움이되었다. 그래서 나는 어떤 사람들은 수정 된 오브젝트에 진정한 깊은 사본이 필요할지도 모른다는 것을 알았지 만 이것은 법안에 맞지 않을 것이다.

관련 문제