JavaScript : 값으로 객체를 전달하는 방법?


96
  • 객체를 매개 변수로 전달할 때 JavaScript는 객체를 참조로 전달하므로 객체의 로컬 복사본을 생성하기 어렵습니다.

    var o = {};
    (function(x){
        var obj = x;
        obj.foo = 'foo';
        obj.bar = 'bar';
    })(o)

    o.foo하고 .bar.

  • 복제를 통해이 문제를 해결할 수 있습니다. 간단한 예 :

    var o = {};
    
    function Clone(x) {
       for(p in x)
       this[p] = (typeof(x[p]) == 'object')? new Clone(x[p]) : x[p];
    }
    
    (function(x){
        var obj = new Clone(x);
        obj.foo = 'foo';
        obj.bar = 'bar';
    })(o)

    o필요가 없습니다 .foo.bar.


질문

  1. 로컬 복사본 / 복제를 만드는 것 외에 값으로 개체를 전달하는 더 좋은 방법이 있습니까?

3
이것이 필요한 사용 사례는 무엇입니까?
jondavidjohn 2011 년

2
프로그래밍 재미. 새로운 JS 엔진이이 문제를 해결했는지 (기술적으로는 값으로 참조를 전달하고 있음) 확인하지만 주로 재미를 위해.
vol7ron 2011 년

1
참조 입니다 자바 스크립트 패스 별 참조 또는 패스 값에 의한 언어? -JavaScript는 참조로 전달되지 않습니다. Java와 마찬가지로 객체를 함수에 전달할 때 값으로 전달되지만 값은 참조입니다. 참조 자바는 참조에 의해 전달되어 있습니까? .
Richard JP Le Guen 2011 년

1
내가 아는 한 당신은 가치로 객체를 전달할 수 없습니다. 존재한다고해도 실제로 위에서 언급 한 복제를 수행하는 것이므로 얻을 수있는 것은 아무것도 없습니다. 아마도 3 줄의 코드를 저장하는 것 외에는.
Thor84no

1
@ vol7ron 예, 언어 디자인 특성을 올바르게 구현하여 문제를 해결했습니다.
jondavidjohn 2011 년

답변:


61

별로.

당신이 실제로 필요에 따라 하나의 가능성은 세트에있을 수 있습니다 o새로운 객체의 프로토있다.

var o = {};
(function(x){
    var obj = Object.create( x );
    obj.foo = 'foo';
    obj.bar = 'bar';
})(o);

alert( o.foo ); // undefined

따라서 추가하는 속성 obj은에 추가 되지 않습니다 o. 의 속성 obj과 동일한 속성 이름으로에 추가 된 모든 속성 은 속성을 o섀도 잉합니다 o.

물론에 추가 된 모든 속성 은 섀도우 처리되지 않은 경우 o에서 사용할 수 있으며 프로토 타입 체인에있는 obj모든 개체는 .oo

또한 objArray와 같은 다른 개체를 참조하는 속성이있는 경우 개체에 구성원을 추가하기 전에 해당 개체를 그림자로 처리해야합니다. 그렇지 않으면 해당 구성원이에 추가되고 obj모든 개체간에 공유됩니다. 이 obj프로토 타입 체인에.

var o = {
    baz: []
};
(function(x){
    var obj = Object.create( x );

    obj.baz.push( 'new value' );

})(o);

alert( o.baz[0] );  // 'new_value'

여기에서 당신은 당신의 배열을 그림자하지 않았기 때문에 것을 알 수 있습니다 bazo로모그래퍼 baz에 특성 objo.baz배열이 수정됩니다.

따라서 대신 먼저 그림자를 만들어야합니다.

var o = {
    baz: []
};
(function(x){
    var obj = Object.create( x );

    obj.baz = [];
    obj.baz.push( 'new value' );

})(o);

alert( o.baz[0] );  // undefined

3
+1, 저도 Object.create제 답변에서 제안하려고 했지만 사본의 얕은 부분을 설명하기 위해 확장하고 싶지 않았습니다 ;-)
Andy E

+1, 잊어 버렸습니다. 나는 시도했다 var obj = new Object(x). 나에게는 이것이 기본적으로 new Object(x)수행 될 것이라고 생각합니다 Object.create(x)– 흥미 롭습니다
vol7ron 2011-09-27

1
@ vol7ron : 예, new Object(x)동일한 개체를 다시 뱉어냅니다 (아마 눈치 채 셨을 것입니다) . 복제하는 기본 방법이 있으면 좋을 것입니다. 작품에 무엇이 있는지 확실하지 않습니다.
user113716 2011 년

43

이 답변 https://stackoverflow.com/a/5344074/746491을 확인 하십시오 .

즉, JSON.parse(JSON.stringify(obj))객체를 json으로 직렬화 할 수있는 경우 객체를 복사하는 빠른 방법입니다.


2
객체가 항상 직렬화 될 수는없고 IE7 / 8과 같은 중년의 브라우저 JSON에서는 포함되지 않고 정의해야하기 때문에 이것을 피하는 경향이 있습니다 Clone. 그러나, 나는 JSON이 더 많이 데이터베이스와 IDE를 같은 비 브라우저 기반의 소프트웨어에 통합되어 있기 때문에 내 의견은 향후 변경 될 수 있습니다 의심
vol7ron

1
함수와 날짜 개체가 없으면 JSON.parse 솔루션이 가장 좋은 것 같습니다. stackoverflow.com/questions/122102/…
Herr_Hansen

정말?!? 객체 사본을 만들기 위해 JSON으로 직렬화 하시겠습니까? 이 터무니없는 언어를 싫어하는 또 다른 이유입니다.
RyanNerd

1
JSON 직렬화는 정말 빠르게 복사하는 방법입니까? NodeJS 문서에 따르면 JSON 조작은 CPU를 가장 많이 사용하는 작업입니다. nodejs.org/en/docs/guides/dont-block-the-event-loop/…
harshad

38

다음은 개체의 전체 복사를 수행하는 복제 기능입니다.

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;
}

이제 다음과 같이 사용할 수 있습니다.

(function(x){
    var obj = clone(x);
    obj.foo = 'foo';
    obj.bar = 'bar';
})(o)

3
이것은 깊은 대답이었습니다.
Nathan

아름다운! 최고의 답변 IMO.
ganders

23

사용하다 Object.assign()

예:

var a = {some: object};
var b = new Object;
Object.assign(b, a);
// b now equals a, but not by association.

동일한 작업을 수행하는 더 깨끗한 예 :

var a = {some: object};
var b = Object.assign({}, a);
// Once again, b now equals a.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign


4
Better :var b = Object.assign({}, a);
Bergi

동의합니다. 그에 따라 답변을 업데이트합니다. 또한 모든 브라우저가 assign () 메서드를 지원하는 것은 아닙니다. 호환성 요구 사항에 따라 MDN에있는 것과 같은 폴리 필을 사용해야 할 수 있습니다.
브랜든

3
@Brandon, 제공하신 바로 그 링크에서 딥 클로닝에 대한 경고를 표시합니다. "딥 클로닝의 경우 Object.assign ()이 속성 값을 복사하기 때문에 다른 대안을 사용해야합니다. 소스 값이 객체에 대한 참조 인 경우, 참조 값만 복사합니다. "
pwilcox

2
이것은 작동하지만 내부에 포함 된 객체 a는 참조로 복사 될 것입니다.
Stoinov

15

JavaScript에서 객체가 작동하는 방식에 대해 약간 혼란스러워합니다. 개체의 참조 변수의 값입니다. 직렬화되지 않은 값이 없습니다. 객체를 생성 할 때 그 구조는 메모리에 저장되고 할당 된 변수는 해당 구조에 대한 참조를 보유합니다.

당신이 요청하는 것이 어떤 종류의 쉬운 모국어 구조로 제공 되었더라도 기술적으로는 여전히 복제가 될 것입니다.

JavaScript는 실제로 값에 의한 전달입니다. 전달 된 값이 무언가에 대한 참조 일 수 있습니다.


1
안녕 AndyE! 나는 그것에 대해 혼란스럽지 않지만 JS가 기본적으로 복제를 수행하기를 희망했습니다. 자가 복제에는 큰 비 효율성이 있기 때문에 엔진이 그렇게했다면 더 좋을 것이라고 확신합니다. 하지만 필요 / 수요가 혜택을 초과하는 것 같지는 않습니다.
vol7ron 2011 년

12
JS에서 사람들이 값 대신 참조로 말할 때 값은 중복이 아니라 포인터와 같습니다. 꽤 잘 정립 된 용어입니다.
Erik Reppen 2012-08-22

4
@Erik : 저의 겸손한 의견으로는 언어를 처음 접하는 사람들을 혼동하지 않도록 더 명확하게 정의하는 것이 낫다고 생각합니다. 어떤 것이 잘 확립되었다고해서 그것이 적절하거나 심지어 기술적으로 정확하다는 것을 의미하지는 않습니다 (그렇지 않기 때문에). 사람들은 실제로 어떻게 작동하는지 설명하는 것보다 말하는 것이 더 쉽기 때문에 "참조로"라고 말합니다.
Andy E

6
값은 메모리의 위치에 대한 참조입니다. 궁극적으로 당신은 사본으로 작업하는 대신 메모리의 동일한 항목에 대해 여전히 작업하고 있습니다. 신규 이민자를 돕기 위해 그 구별을 어떻게 모호하게 만드는가? 이것이 현재 다섯 번째 (OT : 매우 약하게 업데이트 된) 판에있는 O'Reilly의 Authoritative Guide를 포함한 많은 기술 서적에서 설명되는 방식입니다.
Erik Reppen 2012-08-23

1
@AndyE 나는 소란이 무엇인지 확인하기 위해 조금 더 읽었습니다. IMO, 이것은 JS가 "진정한"OOP를 가지고 있는지, 아니면 다른 언어에 비해 어떤 것을 "방법"이라고 적절하게 부를 수 있는지에 대한 문제와 비슷합니다. 할당 연산자 동작의 성능 저하 분기를 사용하여 클로저 기반 언어로 복사 된 포인터를 덮어 쓰는 대신 vars를 메모리의 동일한 개체에 직접 쓰는 것으로 처리하는 것이 실제로 가능할지 확신 할 수 없습니다. JS와 다른 언어에서 의미하는 바의 유일한 실제 동작 구별은 할당 연산자를 사용할 때 발생하는 것입니다.
Erik Reppen 2012-08-23

15

이것을 사용하십시오

x = Object.create(x1);

x그리고 x1두 개의 다른 개체가 될 x것입니다.x1


Object.assign ()을 사용하게됩니다. 간단한 테스트 케이스의 경우 .create () 작동하지만 더 복잡한 경우 (아직 패턴을 찾을 수 없음) 여전히 동일한 주소를 참조합니다.
Coisox

객체의 값이 참조로 저장되지 않는 경우에만 변경되지 않습니다. 즉, 숫자 나 부울과 같은 사전 데이터 유형입니다. 예를 들어, 아래 코드를 고려하십시오. a = {x : [12], y : []}; b = Object.create (a); bxpush (12); console.log (ax);
Jjagwe Dennis

8

Javascript는 항상 값으로 전달됩니다. 이 경우 참조 사본을 전달합니다.o 익명 함수로 합니다. 코드는 참조 사본을 사용하고 있지만 단일 객체를 변경하고 있습니다. 가치 이외의 다른 것으로 자바 스크립트를 전달하는 방법은 없습니다.

이 경우 원하는 것은 기본 개체의 복사본을 전달하는 것입니다. 개체를 복제하는 것이 유일한 방법입니다. 복제 방법에는 약간의 업데이트가 필요합니다.

function ShallowCopy(o) {
  var copy = Object.create(o);
  for (prop in o) {
    if (o.hasOwnProperty(prop)) {
      copy[prop] = o[prop];
    }
  }
  return copy;
}

1
+1 "항상 가치로 전달"의 경우 " [참조]의 값으로 호출"과는 반대로 객체 공유로 호출을 사용 하고 "값"을 "객체 자체"로 승격합니다 (참고로 참조는 구현 세부 사항 일 뿐이며 JavaScript에 노출되지 않기 때문에 (또는 사양에 직접 언급 된!) 객체는 복사 / 복제 / 복제되지 않습니다.

원격 연결에서받는 객체에 속성을 추가하는 데 문제가 있었기 때문에 (해당 객체에 속성을 추가 할 수없는 문제라고 생각했습니다) 그래서이 얕은 복사 기능을 시도하고이 오류가 발생했습니다-객체 프로토 타입 오직 Object이거나 null 일 수 있습니다. var copy = Object.create (o); 내 문제를 해결하는 방법을 어떤 생각
Harshit Laddha

딥 카피가 아닙니까?
River Tam

@RiverTam 아니요, 개체 내에서 개체를 복제하지 않기 때문에 전체 복사본이 아닙니다. 그러나 각 하위 개체에서 자신을 재귀 적으로 호출하는 것만으로도 쉽게 딥 카피로 만들 수 있습니다 .
ABPerson

7

jQuery 사용자를 고려하여 프레임 워크를 사용하여 간단한 방법으로이를 수행하는 방법도 있습니다. jQuery가 우리 삶을 좀 더 쉽게 만드는 또 다른 방법입니다.

var oShallowCopy = jQuery.extend({}, o);
var oDeepCopy    = jQuery.extend(true, {}, o); 

참조 :


아-이 오래된 질문을 발견하셨습니다. :) 원래 질문은 JS 언어에 대한 탐구에 더 가깝다고 생각합니다. 내 예제가 복제가 아닌 깊은 사본 이었기 때문에 당시 내 용어조차 약한 것 같습니다 . 그러나 객체 / 해시는 참조로만 전달 될 수있는 것 같습니다. 이는 스크립팅 언어에서 일반적입니다
vol7ron

1
:) 정확한 기능을 찾고 발견했습니다. 원본 개체를 유지하면서 제출하기 전에 편집을 위해 복사해야하는 데이터 개체가 있습니다. 나는 네이티브 JS 솔루션을 선호하고 내 기본 라이브러리에서 사용할 예정이지만 jQuery 솔루션은 나의 임시 수정입니다. 받아 들여지는 대답은 환상적입니다. : D
Brett Weber

6

실제로 Javascript는 항상 value로 전달됩니다 . 그러나 객체 참조는 이기 때문에 객체는 참조 로 전달되는 것처럼 작동합니다 .

따라서이 문제를 살펴 보려면 JSON을 사용하여 객체를 문자열 화 하고 다시 구문 분석 하십시오. 아래 코드 예를 참조하십시오.

var person = { Name: 'John', Age: '21', Gender: 'Male' };

var holder = JSON.stringify(person);
// value of holder is "{"Name":"John","Age":"21","Gender":"Male"}"
// note that holder is a new string object

var person_copy = JSON.parse(holder);
// value of person_copy is { Name: 'John', Age: '21', Gender: 'Male' };
// person and person_copy now have the same properties and data
// but are referencing two different objects

2
예, 이것은 간단한 변환 / 재 변환을위한 옵션입니다. 일반적으로 문자열 작업은 매우 비효율적입니다. 하지만 한동안 JSON 구문 분석의 성능을 평가하지 않았습니다. 이것은 단순한 객체에 대해서는 작동하지만 함수를 값으로 포함하는 객체는 내용을 잃게됩니다.
vol7ron

4

use obj2 = {... obj1} 이제 두 객체 모두 동일한 값을 가지며 다른 참조를가집니다.


2
여기에 자바 스크립트가 처음입니다. 스프레드 연산자는 참조로 복사하는 대신 새로운 값을 생성합니까? 그렇다면 이것이 왜 반대표를 받았습니까?
jo3birdtalk apr

2
언뜻보기에 그것은 나를 위해 잘 작동합니다. 그러나 나는 그것이 변수의 첫 번째 계층에만 있다는 것을 발견했습니다. 예를 들어,, a가 될 let a = {value: "old"} let b = [...a] b.value = "new"{value: "old"}, b가 될 때 작동 합니다 {value: "new"}. 그러나 , a가 될 let a = [{value: "old"}] let b = [...a] b[0].value = "new"[{value: "new"}], b가 될 때 작동하지 않습니다 [{value: "new"}].
Brady Huang

예, 얕은 복사를 수행합니다. 중첩 된 개체가있는 경우 깊은 복사를 수행해야합니다. 단순성을 위해 lodash의 복제 및 deepClone 기능을 사용할 수 있습니다
Geetanshu Gulati

3

값 (참조가 아님)으로 개체를 복사해야했는데이 페이지가 유용하다는 것을 알았습니다.

JavaScript에서 객체를 딥 복제하는 가장 효율적인 방법은 무엇입니까? . 특히 John Resig의 다음 코드를 사용하여 개체를 복제합니다.

//Shallow copy
var newObject = jQuery.extend({}, oldObject);
// Deep copy
var newObject = jQuery.extend(true, {}, oldObject);

문제는 jQuery 라이브러리가 아니라 JavaScript에 관한 것입니다.
j08691 2013-08-12

1
어떤 이유로 든 Jquery를 사용할 수없는 사람들에게는 이것이 도움이되지 않습니다. 그러나 Jquery는 자바 스크립트 라이브러리이므로 대부분의 자바 스크립트 사용자에게 여전히 도움이 될 것이라고 생각합니다.
Anthony Haffey

서버에서이를 수행하려는 프로그래머에게는 도움이되지 않습니다. 그래서 안돼. 유효한 대답이 아닙니다.
Tomasz Gałkowski

3

ES6 구문 사용 :

let obj = Object.assign({}, o);


3
@galkowskit를 아주 잘 언급합니다. 의 가장 큰 문제 (더 나은 : 주목할 점 ) Object.assign는 깊은 복사를 수행하지 않는다는 것입니다.
vol7ron

1

요약하면 지나치게 복잡한 프록시 일 뿐이지 만 Catch-All Proxies 가 가능할까요?

var o = {
    a: 'a',
    b: 'b',
    func: function() { return 'func'; }
};

var proxy = Proxy.create(handlerMaker(o), o);

(function(x){
    var obj = x;
    console.log(x.a);
    console.log(x.b);
    obj.foo = 'foo';
    obj.bar = 'bar';
})(proxy);

console.log(o.foo);

function handlerMaker(obj) {
  return {
   getOwnPropertyDescriptor: function(name) {
     var desc = Object.getOwnPropertyDescriptor(obj, name);
     // a trapping proxy's properties must always be configurable
     if (desc !== undefined) { desc.configurable = true; }
     return desc;
   },
   getPropertyDescriptor:  function(name) {
     var desc = Object.getOwnPropertyDescriptor(obj, name); // not in ES5
     // a trapping proxy's properties must always be configurable
     if (desc !== undefined) { desc.configurable = true; }
     return desc;
   },
   getOwnPropertyNames: function() {
     return Object.getOwnPropertyNames(obj);
   },
   getPropertyNames: function() {
     return Object.getPropertyNames(obj);                // not in ES5
   },
   defineProperty: function(name, desc) {

   },
   delete:       function(name) { return delete obj[name]; },   
   fix:          function() {}
  };
}

Richard, 나는 응답하는데 조금 늦었습니다. :)하지만 몇 가지 이유로 한 번도 해본 적이 없다고 생각합니다. JS에게 다양한 방식으로 객체를 전달할 수있는 능력을 부여하는 구문 설탕을 찾고있었습니다. 당신의 대답은 길어 보였고 나는 당신의 "프록시"사용에 매달렸다. 3 년이 지난 지금도 여전히 사용이 끊깁니다. 수업 시간에 충분히주의를 기울이지 않았을 수도 있지만, 네트워킹처럼 CS의 프록시가 중개자 역할을해야한다고 생각했습니다. 아마도 여기에 있고 그것이 무엇 사이의 중재인지에 대해 올바르게 생각하고 있지 않습니다.
vol7ron

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.