두 JavaScript 객체의 속성을 동적으로 병합하려면 어떻게해야합니까?


2518

런타임에 두 개의 (매우 간단한) JavaScript 객체를 병합 할 수 있어야합니다. 예를 들어 다음과 같습니다.

var obj1 = { food: 'pizza', car: 'ford' }
var obj2 = { animal: 'dog' }

obj1.merge(obj2);

//obj1 now has three properties: food, car, and animal

누구나 이것에 대한 스크립트를 가지고 있거나 이것을 수행하는 내장 된 방법을 알고 있습니까? 재귀가 필요하지 않으며 함수를 병합 할 필요가 없으며 평평한 객체의 메서드 만 병합하면됩니다.


비슷한 질문에 대해이 답변에 주목할 필요가 있는데, "한 수준 아래로"병합하는 방법을 보여줍니다. 즉, 첫 번째 값을 두 번째 값으로 덮어 쓰지 않고 중복 키 값을 병합하지만 그보다 더 이상 재귀하지는 않습니다. IMHO, 그 작업을위한 깔끔한 코드.
ToolmakerSteve

BTW, 상위 몇 가지 답변은 "얕은"병합을 수행합니다. obj1과 obj2에 동일한 키가 있으면 obj2의 값이 유지되고 obj1의 값이 삭제됩니다. 예를 들어 질문의 예가 var obj2 = { animal: 'dog', food: 'bone' };이면 병합은입니다 { food: 'bone', car: 'ford', animal: 'dog' }. "중첩 된 데이터"로 작업 중이고 "심층 병합"을 원하는 경우 "심층 병합"또는 "재귀"에 대한 답변을 찾으십시오. 값이있는 경우 여기에 설명 된arrays 대로 github "TehShrike / deepmerge"의 "arrayMerge"옵션을 사용 하십시오 .
ToolmakerSteve

아래 링크를 참조하십시오 stackoverflow.com/questions/171251/… ES6 버전의 새로운 기능인 스프레드 연산자
Dev216

답변:


2905

ECMAScript 2018 표준 방법

당신은 객체 확산 을 사용할 것입니다 :

let merged = {...obj1, ...obj2};

merged지금의 조합입니다 obj1obj2. 의 속성이의 속성을 obj2덮어 씁니다 obj1.

/** There's no limit to the number of objects you can merge.
 *  Later properties overwrite earlier properties with the same name. */
const allRules = {...obj1, ...obj2, ...obj3};

이 구문에 대한 MDN 설명서 도 있습니다 . babel을 사용하는 경우 작동하려면 babel-plugin-transform-object-rest-spread 플러그인이 필요합니다.

ECMAScript 2015 (ES6) 표준 방법

/* For the case in question, you would do: */
Object.assign(obj1, obj2);

/** There's no limit to the number of objects you can merge.
 *  All objects get merged into the first object. 
 *  Only the object in the first argument is mutated and returned.
 *  Later properties overwrite earlier properties with the same name. */
const allRules = Object.assign({}, obj1, obj2, obj3, etc);

(보다 MDN JavaScript 참조 참조 )


ES5 및 그 이전 방법

for (var attrname in obj2) { obj1[attrname] = obj2[attrname]; }

이것은 단순히 모든 속성을 추가합니다 obj2obj1여전히 수정되지 않은를 사용하려는 경우 당신이 원하는 것을하지 않을 수있는을obj1 .

프로토 타입 전체를 크 래핑하는 프레임 워크를 사용하는 경우 다음과 같은 검사를 통해 더 좋아질 것입니다. hasOwnProperty 있지만 99 %의 경우에는 해당 코드가 작동합니다.

기능 예 :

/**
 * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1
 * @param obj1
 * @param obj2
 * @returns obj3 a new object based on obj1 and obj2
 */
function merge_options(obj1,obj2){
    var obj3 = {};
    for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; }
    for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; }
    return obj3;
}

90
객체의 이름 속성이 동일하고 속성을 병합하려는 경우에는 작동하지 않습니다.
Xiè Jìléi

69
얕은 복사 / 병합 만 수행합니다. 많은 요소를 방해 할 가능성이 있습니다.
Jay Taylor

45
일부 빈곤 한 영혼은 자신의 프로토 타입을
뒤 흔드는

4
"따라서 속성을 할당하는 것보다 새로운 속성을 복사하거나 정의하는 것만으로는 효과가 없었습니다. 병합 소스에 게터가 포함되어 있으면 새 속성을 프로토 타입에 병합하기에 적합하지 않을 수 있습니다." ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… ). 나는을 사용해야했다 var merged = {...obj1, ...obj2}.
morgler

2
@ Ze'ev{...obj1, ...{animal: 'dog'}}
backslash112

1191

jQuery에는 다음과 같은 유틸리티도 있습니다. http://api.jquery.com/jQuery.extend/ .

jQuery 문서에서 가져 왔습니다.

// Merge options object into settings object
var settings = { validate: false, limit: 5, name: "foo" };
var options  = { validate: true, name: "bar" };
jQuery.extend(settings, options);

// Now the content of settings object is the following:
// { validate: true, limit: 5, name: "bar" }

위의 코드는 라는 기존 객체 를 변경합니다 settings.


인수를 수정하지 않고 새 객체 를 만들려면 다음을 사용하십시오.

var defaults = { validate: false, limit: 5, name: "foo" };
var options = { validate: true, name: "bar" };

/* Merge defaults and options, without modifying defaults */
var settings = $.extend({}, defaults, options);

// The content of settings variable is now the following:
// {validate: true, limit: 5, name: "bar"}
// The 'defaults' and 'options' variables remained the same.

166
주의 : 변수 "설정"은 수정됩니다. jQuery는 새 인스턴스를 반환하지 않습니다. 이것과 이름의 이유는 .extend ()가 객체를 하나로 묶지 않고 객체를 확장하기 위해 개발 되었기 때문입니다. 새 객체를 원할 경우 (예 : 설정을 터치하지 않으려는 기본값) 항상 jQuery.extend ({}, 설정, 옵션);
webmat

1
바로! 무리 감사. 이전 의견에서 언급했듯이 이름이 잘못되었습니다. 나는 jQuery 문서를 검색했다가 네 번째로 실행되지 않았다.
Mike Starov 2016 년

28
jQuery.extend에는 깊은 (부울) 설정이 있습니다. jQuery.extend(true,settings,override)설정의 속성에 개체가 있고 재정의에 해당 개체의 일부만있는 경우 중요합니다. 일치하지 않는 속성을 제거하는 대신 딥 설정은 존재하는 위치 만 업데이트합니다. 기본값은 false입니다.
vol7ron

19
Gie willikers, 그들은 이것을 정말로 명명하는 좋은 일을했다. Javascript 객체를 확장하는 방법을 검색하면 바로 나타납니다! Javascript 객체를 융합하거나 연결하려고하면 충돌하지 않을 수 있습니다.
Derek Greer

2
몇 줄의 바닐라 JS가 원하는 것을 할 때 라이브러리 방법 사용을 고려하도록 사람들에게 말하지 마십시오. jQuery 전에 시간이 있었다!
CrazyMerlin

347

하모니는 2015 년 (ES6) ECMA 스크립트 지정 Object.assign이 작업을 수행 할 것입니다.

Object.assign(obj1, obj2);

현재 브라우저 지원이 향상 되고 있지만 지원되지 않는 브라우저를 개발하는 경우 polyfill을 사용할 수 있습니다 .



나는 그 사이에 생각 Object.assign꽤 괜찮은 범위를 가지고 : kangax.github.io/compat-table/es6/...
LazerBass에게

13
이것은 이제 정답입니다. 요즘 사람들은 이런 종류의 것을 지원하지 않는 희귀 브라우저 (IE11)와 호환되도록 코드를 컴파일합니다. 참고 사항 : obj2를 obj1에 추가하지 않으려면 다음을 사용하여 새 객체를 반환 할 수 있습니다.Object.assign({}, obj1, obj2)
Duvrai

6
@Duvrai 내가 본 보고서에 따르면, IE11은 2016 년 7 월 현재 시장 점유율의 약 18 %에서 드물지 않습니다 .
NanoWizard

3
@Kermani OP는 특히 재귀가 필요하지 않다고 지적합니다. 따라서이 솔루션이 얕은 병합을 수행한다는 것을 아는 것이 유용하지만이 대답만으로도 충분합니다. JoachimLou의 의견은 가치있는 공감대를 받았으며 필요할 때 추가 정보를 제공합니다. 깊고 얕은 병합과 비슷한 주제의 차이는이 논의의 범위를 벗어나고 다른 SO 질문에 더 적합하다고 생각합니다.
NanoWizard

264

객체 속성을 병합하는 코드를 검색하여 여기에 올렸습니다. 그러나 재귀 병합을위한 코드가 없기 때문에 직접 작성했습니다. (아마도 jQuery extend는 재귀 BTW입니까?) 어쨌든 다른 누군가도 유용 할 것입니다.

(이제 코드는 사용하지 않습니다 Object.prototype:)

암호

/*
* Recursively merge properties of two objects 
*/
function MergeRecursive(obj1, obj2) {

  for (var p in obj2) {
    try {
      // Property in destination object set; update its value.
      if ( obj2[p].constructor==Object ) {
        obj1[p] = MergeRecursive(obj1[p], obj2[p]);

      } else {
        obj1[p] = obj2[p];

      }

    } catch(e) {
      // Property in destination object not set; create it and set its value.
      obj1[p] = obj2[p];

    }
  }

  return obj1;
}

o1 = {  a : 1,
        b : 2,
        c : {
          ca : 1,
          cb : 2,
          cc : {
            cca : 100,
            ccb : 200 } } };

o2 = {  a : 10,
        c : {
          ca : 10,
          cb : 20, 
          cc : {
            cca : 101,
            ccb : 202 } } };

o3 = MergeRecursive(o1, o2);

객체 o3을 다음과 같이 생성합니다

o3 = {  a : 10,
        b : 2,
        c : {
          ca : 10,
          cb : 20,
          cc : { 
            cca : 101,
            ccb : 202 } } };

11
좋았지 만 먼저 물체의 딥 카피를 만들 것입니다. 이 방법으로 객체가 참조로 전달되므로 o1도 수정됩니다.
skerit

1
이것이 내가 찾던 것입니다. try catch 문으로 이동하기 전에 obj2.hasOwnProperty (p)를 확인하십시오. 그렇지 않으면 프로토 타입 체인에서 다른 크랩이 병합 될 수 있습니다.
Adam

3
감사합니다 Markus. o1도 MergeRecursive에 의해 수정되므로 끝에 o1과 o3은 동일합니다. 아무것도 반환하지 않으면 더 직관적 일 수 있으므로 사용자는 공급하는 (o1)이 수정되어 결과가된다는 것을 알고 있습니다. 또한 귀하의 예에서, o2에 o2 속성이 o1에 삽입되었음을 보여주기 위해 o2에 원래없는 o2에 무언가를 추가하는 것이 좋습니다. o1에서-그러나이 점에서 작동합니다).
팬케이크

7
이것은 좋은 대답이지만 몇 가지 문제가 있습니다. 1) 예외를 기반으로하는 논리는 없습니다. 이 경우 예외가 발생하면 예기치 않은 결과가 발생합니다. 2) 원래 객체가 수정되었으므로 아무것도 반환하지 않는 것에 대한 @pancake의 의견에 동의합니다. 여기에 예외 논리없이 jsFiddle 예는 다음과 같습니다 jsfiddle.net/jlowery2663/z8at6knn/4 - 제프 로워
제프 로워

3
또한 객체 배열이있는 경우 obj2 [p] .constructor == Array가 필요합니다.
작곡가

175

주의 underscore.jsextend-method이 한 라이너에서이 작업을 수행합니다 :

_.extend({name : 'moe'}, {age : 50});
=> {name : 'moe', age : 50}

36
이 예제는 익명 객체를 다루는 데는 좋지만, 그렇지 않은 경우 jQuery 응답에 대한 webmat의 주석이 변형에 대한 경고가 여기에 적용됩니다. 밑줄도 대상 객체를 변형합니다 . jQuery 답변과 마찬가지로이 무 돌연변이를 수행하려면 빈 객체로 병합하십시오._({}).extend(obj1, obj2);
Abe Voelker

8
달성하려는 목표에 따라 관련이있을 수있는 또 다른 방법이 있습니다. _.defaults(object, *defaults)"기본 개체의 값으로 개체의 null 속성과 정의되지 않은 속성을 채우고 개체를 반환합니다."
conny

많은 사람들이 대상 객체를 변경하는 것에 대해 언급했지만 메소드가 새로운 것을 작성하는 대신 일반적인 패턴 (예 : dojo)은 dojo.mixin (dojo.clone (myobj), {newpropertys : " to myobj "})
콜린 D

7
밑줄은 임의의 수의 객체를 사용할 수 있으므로을 수행하여 원래 객체의 변형을 피할 수 있습니다 var mergedObj = _.extend({}, obj1, obj2).
lobati

84

jQuery extend ()와 유사하게 AngularJS 에서 동일한 함수를 갖습니다 .

// Merge the 'options' object into the 'settings' object
var settings = {validate: false, limit: 5, name: "foo"};
var options  = {validate: true, name: "bar"};
angular.extend(settings, options);

1
@naXa이 fn에 대해서만 lib를 추가하지 않으려면 옵션이 될 것이라고 생각합니다.
juanmf

67

오늘 객체를 병합해야 하며이 질문 (및 답변)이 많은 도움이되었습니다. 나는 대답 중 일부를 시도했지만 그중 어느 것도 내 요구에 맞지 않기 때문에 대답 중 일부를 결합하고 직접 무언가를 추가하고 새로운 병합 기능을 고안했습니다. 여기있어:

var merge = function() {
    var obj = {},
        i = 0,
        il = arguments.length,
        key;
    for (; i < il; i++) {
        for (key in arguments[i]) {
            if (arguments[i].hasOwnProperty(key)) {
                obj[key] = arguments[i][key];
            }
        }
    }
    return obj;
};

사용법 예 :

var t1 = {
    key1: 1,
    key2: "test",
    key3: [5, 2, 76, 21]
};
var t2 = {
    key1: {
        ik1: "hello",
        ik2: "world",
        ik3: 3
    }
};
var t3 = {
    key2: 3,
    key3: {
        t1: 1,
        t2: 2,
        t3: {
            a1: 1,
            a2: 3,
            a4: [21, 3, 42, "asd"]
        }
    }
};

console.log(merge(t1, t2));
console.log(merge(t1, t3));
console.log(merge(t2, t3));
console.log(merge(t1, t2, t3));
console.log(merge({}, t1, { key1: 1 }));

3
좋은 대답하지만 난 속성이 두 번째 첫 번째에 존재하고있는 경우 생각하고 당신은 손실됩니다 다음, 제 1 및 제 2 첫 번째 개체에 존재하는 독특한 사람을 병합하여 새로운 객체를 만들려고 노력하고 있습니다
파업

2
그러나 그것은 행동이 예상되지 않습니까? 이 함수의 목적은 인수 순서대로 값을 대체하는 것입니다. 다음은 전류를 무시합니다. 고유 한 가치를 어떻게 보존 할 수 있습니까? 내 예에서, 당신은 무엇을 기대 merge({}, t1, { key1: 1 })합니까?
Emre Erkan

내 기대는 객체 유형이 아닌 값만 병합하는 것입니다.
AGamePlayer

예상 된 결과가 아니라 다음 경우 실패 gist.github.com/ceremcem/a54f90923c6e6ec42c7daf24cf2768bd
ceremcem

이것은 node.js 프로젝트의 엄격 모드에서 나를 위해 일했습니다. 지금 까지이 게시물에 대한 가장 좋은 답변입니다.
klewis

48

객체 확산 속성을 사용할 수 있습니다 ( 현재 3 단계 ECMAScript 제안).

const obj1 = { food: 'pizza', car: 'ford' };
const obj2 = { animal: 'dog' };

const obj3 = { ...obj1, ...obj2 };
console.log(obj3);


브라우저 지원?
Alph.Dev

1
@ Alph.Dev caniuse.com을 알고 있습니까?
connexo

node.js에서 3 도트 구문을 사용할 수 없습니다. 노드가 그것에 대해 불평합니다.
klewis

41

주어진 솔루션은 확인하기 위해 수정해야 source.hasOwnProperty(property)for..in할당하기 전에 루프 - 그렇지 않으면, 당신은 거의 필요없는 경우 전체 프로토 타입 체인의 속성을 복사 결국 ...


40

한 줄의 코드로 N 개의 객체 속성 병합

Object.assign방법은 ECMAScript를 2015 년 (ES6) 표준의 일부이며 정확하게 당신이 필요로한다. ( IE지원되지 않음)

var clone = Object.assign({}, obj);

Object.assign () 메소드는 열거 가능한 모든 고유 특성의 값을 하나 이상의 소스 오브젝트에서 대상 오브젝트로 복사하는 데 사용됩니다.

더 읽어보기 ...

구형 브라우저를 지원 하는 폴리 필 :

if (!Object.assign) {
  Object.defineProperty(Object, 'assign', {
    enumerable: false,
    configurable: true,
    writable: true,
    value: function(target) {
      'use strict';
      if (target === undefined || target === null) {
        throw new TypeError('Cannot convert first argument to object');
      }

      var to = Object(target);
      for (var i = 1; i < arguments.length; i++) {
        var nextSource = arguments[i];
        if (nextSource === undefined || nextSource === null) {
          continue;
        }
        nextSource = Object(nextSource);

        var keysArray = Object.keys(nextSource);
        for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
          var nextKey = keysArray[nextIndex];
          var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
          if (desc !== undefined && desc.enumerable) {
            to[nextKey] = nextSource[nextKey];
          }
        }
      }
      return to;
    }
  });
}

오래된 브라우저에서 '엄격한 사용'이하는 일 또는 왜 존재 하는가? 객체를 지원하는 브라우저 [defineProperty; 키; getOwnPropertyDescriptor; 등], 정확히 오래되지 않았습니다. 5 세대 UA의 초기 버전입니다.
Bekim Bacaj

Objects.assign다른 답변이하지 않는 병합 된 객체 를 반환 한다는 것을 명확히 해 주셔서 감사합니다 . 더 기능적인 프로그래밍 스타일입니다.
Sridhar Sarnobat

36

다음 두 가지가 좋은 출발점이 될 것입니다. lodash에는 이러한 특수 요구 사항에 맞는 사용자 정의 기능도 있습니다!

_.extend( http://underscorejs.org/#extend )
_.merge( https://lodash.com/docs#merge )


4
위의 방법은 원본 객체를 변경합니다.
Wtower

lodash 방법은 두 객체를 병합하고 단순히 객체를 바꾸거나 닦는 것이 아니라 하나 또는 두 레벨 이상의 중첩 속성을 유지하려고했기 때문에 저에게 효과적이었습니다.
SamBrick

사실 @Wtower. 이 때문에 버그가 발생했습니다. 항상 _.merge ({}, ...처럼 시작해야합니다.
Martin Cremer

29

그건 그렇고, 당신이하는 모든 일은 병합하지 않고 속성을 덮어 쓰는 것입니다 ...

이것은 JavaScript 객체 영역이 실제로 병합 to되는 방식 from입니다. 객체 자체가 아닌 객체 의 키만 로 덮어 씁니다 . 다른 모든 것들은 실제로 합쳐질 것 입니다. 물론이 동작을 변경하여 if 등과 같은 존재하는 것을 덮어 쓰지 않을 수 있습니다 to[n] is undefined.

var realMerge = function (to, from) {

    for (n in from) {

        if (typeof to[n] != 'object') {
            to[n] = from[n];
        } else if (typeof from[n] == 'object') {
            to[n] = realMerge(to[n], from[n]);
        }
    }
    return to;
};

용법:

var merged = realMerge(obj1, obj2);

3
나에게 많은 도움이, 밑줄 JS 대신 병합의, 덮어 쓰기 실제로 한을 확장합니다
데이비드 Cumps

1
해당 typeof 배열과 typeof null을 주목할 필요가있을 때 'object'를 반환하고이를 중단합니다.
Salakar

@ u01jmg3 여기에 내 대답을보십시오 : stackoverflow.com/questions/27936772/…-isObject 함수
Salakar

node.js 프로젝트에 엄격 모드를 사용하는 경우 중단됩니다.
klewis

28

여기 내 찌르다

  1. 깊은 병합 지원
  2. 인수를 변경하지 않습니다
  3. 여러 가지 인수를 취합니다
  4. 오브젝트 프로토 타입을 확장하지 않습니다
  5. 다른 라이브러리 ( jQuery , MooTools , Underscore.js 등)에 의존하지 않습니다
  6. hasOwnProperty에 대한 점검 포함
  7. 짧은 :)

    /*
        Recursively merge properties and return new object
        obj1 &lt;- obj2 [ &lt;- ... ]
    */
    function merge () {
        var dst = {}
            ,src
            ,p
            ,args = [].splice.call(arguments, 0)
        ;
    
        while (args.length > 0) {
            src = args.splice(0, 1)[0];
            if (toString.call(src) == '[object Object]') {
                for (p in src) {
                    if (src.hasOwnProperty(p)) {
                        if (toString.call(src[p]) == '[object Object]') {
                            dst[p] = merge(dst[p] || {}, src[p]);
                        } else {
                            dst[p] = src[p];
                        }
                    }
                }
            }
        }
    
       return dst;
    }

예:

a = {
    "p1": "p1a",
    "p2": [
        "a",
        "b",
        "c"
    ],
    "p3": true,
    "p5": null,
    "p6": {
        "p61": "p61a",
        "p62": "p62a",
        "p63": [
            "aa",
            "bb",
            "cc"
        ],
        "p64": {
            "p641": "p641a"
        }
    }
};

b = {
    "p1": "p1b",
    "p2": [
        "d",
        "e",
        "f"
    ],
    "p3": false,
    "p4": true,
    "p6": {
        "p61": "p61b",
        "p64": {
            "p642": "p642b"
        }
    }
};

c = {
    "p1": "p1c",
    "p3": null,
    "p6": {
        "p62": "p62c",
        "p64": {
            "p643": "p641c"
        }
    }
};

d = merge(a, b, c);


/*
    d = {
        "p1": "p1c",
        "p2": [
            "d",
            "e",
            "f"
        ],
        "p3": null,
        "p5": null,
        "p6": {
            "p61": "p61b",
            "p62": "p62c",
            "p63": [
                "aa",
                "bb",
                "cc"
            ],
            "p64": {
                "p641": "p641a",
                "p642": "p642b",
                "p643": "p641c"
            }
        },
        "p4": true
    };
*/

이 페이지에서 테스트 한 다른 함수와 비교할 때이 함수는 진정 재귀 적이며 (깊은 병합을 수행함) jQuery.extend ()가 실제로 잘하는 것과 유사합니다. 그러나 첫 번째 객체 / 인수를 수정하는 것이 더 좋으므로 사용자는 빈 객체 {}를 첫 번째 매개 변수로 전달할지 또는 함수가 원래 객체를 수정하도록 할지 결정할 수 있습니다 . 변경 따라서 dst = {}dst = arguments[0]이를 병합 된 개체에 전달 최초의 객체 변경됩니다
Jasdeep Khalsa

3
이제 크롬에서 작동이 중지되었습니다. 나는 toString.call(src) == '[object Object]'물건이 있는지 여부를 확인 하기 위해 그러한 구성을 사용하는 것이 좋지 않다고 생각합니다 . typeof src훨씬 낫다. 또한 Arrays를 객체로 변환합니다 (최근 크롬에서는 이러한 동작이 발생합니다).
m03geek

욕심 많은 루프 내 에서이 작업을 수행해야했습니다. 지금까지 lodash (일부 밑줄 lib)에서 _.merge (object, [sources]) 사용하는 기능과 비교 하여이 기능을 벤치 마크했습니다. 함수가 거의 두 배 빠릅니다. 고마워 친구.
Arnaud Bouchot

20

Object.assign ()

ECMAScript 2015 (ES6)

이것은 ECMAScript 2015 (ES6) 표준의 일부인 새로운 기술입니다. 이 기술의 사양은 확정되었지만 다양한 브라우저에서 사용 및 구현 상태에 대한 호환성 표를 확인하십시오.

Object.assign () 메소드는 열거 가능한 모든 고유 특성의 값을 하나 이상의 소스 오브젝트에서 대상 오브젝트로 복사하는 데 사용됩니다. 대상 객체를 반환합니다.

var o1 = { a: 1 };
var o2 = { b: 2 };
var o3 = { c: 3 };

var obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1);  // { a: 1, b: 2, c: 3 }, target object itself is changed.

19

너무 복잡하지 않은 객체의 경우 JSON을 사용할 수 있습니다 .

var obj1 = { food: 'pizza', car: 'ford' }
var obj2 = { animal: 'dog', car: 'chevy'}
var objMerge;

objMerge = JSON.stringify(obj1) + JSON.stringify(obj2);

// {"food": "pizza","car":"ford"}{"animal":"dog","car":"chevy"}

objMerge = objMerge.replace(/\}\{/, ","); //  \_ replace with comma for valid JSON

objMerge = JSON.parse(objMerge); // { food: 'pizza', animal: 'dog', car: 'chevy'}
// Of same keys in both objects, the last object's value is retained_/

이 예에서 "} {" 은 ( 는) 문자열 안에 있으면 안됩니다 !


4
var so1 = JSON.stringify(obj1); var so2 = JSON.stringify(obj1); objMerge = so1.substr(0, so1.length-1)+","+so2.substr(1); console.info(objMerge);
Quamis

function merge(obj1, obj2) { return JSON.parse((JSON.stringify(obj1) + JSON.stringify(obj2)) .replace(/\}\{/g, ',').replace(/,\}/g, '}').replace(/\{,/g, '{')); }
Alex Ivasyuv

또는 하나의 라이너로 :objMerge = JSON.parse((JSON.stringify(obj1) + JSON.stringify(obj2)).replace(/\}\{/, ", "));
Rabbi Shuki Gur

1
@Charles, 죄송하지만 "이 방법은 매우 위험합니다"는 물론 재미도 있습니다. 이는 가장 안전한 방법입니다. 첫 번째는 JSON을 사용하여 개체 내용을 많이 읽어야하는 작업입니다. 두 번째로, 가장 안전한 JSON 개체를 사용합니다. JSON 호환 객체 문자열 리터럴에서 JSO를 재분석하는 데 최적화 된 머신. 또한 표준을 구현 한 최초의 브라우저 인 IE8과 역 호환됩니다. "왜 어리석은 말을하고 왜 도망쳐 야합니까?"
Bekim Bacaj

stringify를 사용할 수 없으므로 객체의 함수가 여기에서 분해 될 것이라고 생각하지만 다른 단점을 명확히 할 수 있습니까?
yeahdixon

18

라는 라이브러리 거기 deepmergeGitHub의이 : 그건 일부 견인을 받고있는 것으로 보인다. npm 및 bower 패키지 관리자를 통해 사용할 수있는 독립형 입니다.

답변의 코드를 복사하여 붙여 넣는 대신 이것을 사용하거나 개선하는 경향이 있습니다.


17

이 작업을 수행하는 가장 좋은 방법은 Object.defineProperty를 사용하여 열거 할 수없는 적절한 속성을 추가하는 것입니다.

이런 식으로 Object.prototype.extend를 사용하여 속성을 만들 경우 얻을 수있는 새로 만들어진 "extend"를 사용하지 않고도 개체 속성을 계속 반복 할 수 있습니다.

희망적으로 이것은 도움이됩니다 :

Object.defineProperty (Object.prototype, "extend", {
    열거 가능 : 거짓,
    값 : function (from) {
        var props = Object.getOwnPropertyNames (from);
        var dest = 이것;
        props.forEach (함수 (이름) {
            if (이름이 dest) {
                var destination = Object.getOwnPropertyDescriptor (시작, 이름);
                Object.defineProperty (대상, 이름, 목적지);
            }
        });
        이것을 돌려줍니다;
    }
});

작업이 완료되면 다음을 수행 할 수 있습니다.

var obj = {
    이름 : '스택',
    마무리 : '오버플로'
}
var replacement = {
    이름 : '주식'
};

obj.extend (교체);

방금 블로그 게시물을 작성했습니다 : http://onemoredigit.com/post/1527191998/extending-objects-in-node-js


잠깐만 --- 코드가 작동하지 않습니다! :( 다른 병합 알고리즘과 비교하기 위해 jsperf에 삽입했으며 코드가 실패했습니다-함수 확장이 존재하지 않습니다.
Jens Roland

16

당신은 단순히 jQuery를 사용할 수 있습니다 extend

var obj1 = { val1: false, limit: 5, name: "foo" };
var obj2 = { val2: true, name: "bar" };

jQuery.extend(obj1, obj2);

이제 및의 obj1모든 값을 포함합니다obj1obj2


2
obj1은 obj2가 아닌 모든 값을 포함합니다. 첫 번째 개체를 확장하고 있습니다. jQuery.extend( target [, object1 ] [, objectN ] )
ruuter

5
이 질문은 jQuery에 관한 것이 아닙니다. JavaScript! = jQuery
Jorge Riv

1
자바 스크립트를 검색했지만이 방법이 더 간단합니다
Ehsan

13

프로토 타입 은 다음과 같습니다.

Object.extend = function(destination,source) {
    for (var property in source)
        destination[property] = source[property];
    return destination;
}

obj1.extend(obj2) 당신이 원하는 것을 할 것입니다.


6
Object.prototype.extend을 의미 했습니까? 어쨌든 Object.prototype을 확장하는 것은 좋지 않습니다. -1.
SolutionYogi

그것을 생각해 보면, 아마해야 Object.prototype하지 Object... 좋은 생각이나하지, 이것은 무엇인가 prototype.js프레임 워크가하는, 당신이 그것을 또는에 따라 다른 프레임 워크를 사용하는 경우 그래서, Object.prototype이미 확장 될 것입니다 extend.
ephemient

2
나는 당신을 괴롭 히려는 것이 아니지만 prototype.js가 나쁜 주장이기 때문에 괜찮다는 말입니다. Prototype.js는 인기가 있지만 JavaScript를 수행하는 방법에 대한 역할 모델이라는 의미는 아닙니다. JS pro의 프로토 타입 라이브러리에 대한 자세한 검토를 확인하십시오. dhtmlkitchen.com/?category=/JavaScript/&date=2008/06/17/…
솔루션

7
옳고 그름은 누가 신경 쓰나요? 작동하면 작동합니다. 완벽한 솔루션을 기다리는 것은 계약 유지, 신규 고객 확보 또는 마감일 준수를 제외하고는 훌륭합니다. 무료 돈을 프로그래밍하는 것에 열정을 가진 사람이라면 누구나 "올바른"방식으로 모든 것을 할 수 있습니다.
David

1
너희 모두 어디서 찾았 어 Object.prototype? Object.extend객체를 방해하지 않고 단지 함수를 Object객체에 연결 합니다. 그리고 obj1.extend(obj2)화재 함수에 올바른 방법이 아니다, 사용 Object.extend(obj1, obj2)insted
artnikpro

13

** Object.assign또는 병합 ...연산자를 사용하여 객체를 병합하는 것이 간단 합니다 **

var obj1 = { food: 'pizza', car: 'ford' }
var obj2 = { animal: 'dog', car: 'BMW' }
var obj3 = {a: "A"}


var mergedObj = Object.assign(obj1,obj2,obj3)
 // or using the Spread operator (...)
var mergedObj = {...obj1,...obj2,...obj3}

console.log(mergedObj);

개체는 오른쪽에서 왼쪽으로 병합되므로 오른쪽 개체와 동일한 속성을 가진 개체가 재정의됩니다.

이 예에서 obj2.car재정의obj1.car



10

David Coallier의 방법을 확장했습니다.

  • 여러 객체를 병합 할 수있는 기능 추가
  • 깊은 물체 지원
  • 재정의 매개 변수 (마지막 매개 변수가 부울 인 경우 감지 됨)

override가 false이면 속성이 재정의되지 않지만 새 속성이 추가됩니다.

사용법 : obj.merge (merges ... [, override]);

내 코드는 다음과 같습니다.

Object.defineProperty(Object.prototype, "merge", {
    enumerable: false,
    value: function () {
        var override = true,
            dest = this,
            len = arguments.length,
            props, merge, i, from;

        if (typeof(arguments[arguments.length - 1]) === "boolean") {
            override = arguments[arguments.length - 1];
            len = arguments.length - 1;
        }

        for (i = 0; i < len; i++) {
            from = arguments[i];
            if (from != null) {
                Object.getOwnPropertyNames(from).forEach(function (name) {
                    var descriptor;

                    // nesting
                    if ((typeof(dest[name]) === "object" || typeof(dest[name]) === "undefined")
                            && typeof(from[name]) === "object") {

                        // ensure proper types (Array rsp Object)
                        if (typeof(dest[name]) === "undefined") {
                            dest[name] = Array.isArray(from[name]) ? [] : {};
                        }
                        if (override) {
                            if (!Array.isArray(dest[name]) && Array.isArray(from[name])) {
                                dest[name] = [];
                            }
                            else if (Array.isArray(dest[name]) && !Array.isArray(from[name])) {
                                dest[name] = {};
                            }
                        }
                        dest[name].merge(from[name], override);
                    } 

                    // flat properties
                    else if ((name in dest && override) || !(name in dest)) {
                        descriptor = Object.getOwnPropertyDescriptor(from, name);
                        if (descriptor.configurable) {
                            Object.defineProperty(dest, name, descriptor);
                        }
                    }
                });
            }
        }
        return this;
    }
});

예 및 테스트 사례 :

function clone (obj) {
    return JSON.parse(JSON.stringify(obj));
}
var obj = {
    name : "trick",
    value : "value"
};

var mergeObj = {
    name : "truck",
    value2 : "value2"
};

var mergeObj2 = {
    name : "track",
    value : "mergeObj2",
    value2 : "value2-mergeObj2",
    value3 : "value3"
};

assertTrue("Standard", clone(obj).merge(mergeObj).equals({
    name : "truck",
    value : "value",
    value2 : "value2"
}));

assertTrue("Standard no Override", clone(obj).merge(mergeObj, false).equals({
    name : "trick",
    value : "value",
    value2 : "value2"
}));

assertTrue("Multiple", clone(obj).merge(mergeObj, mergeObj2).equals({
    name : "track",
    value : "mergeObj2",
    value2 : "value2-mergeObj2",
    value3 : "value3"
}));

assertTrue("Multiple no Override", clone(obj).merge(mergeObj, mergeObj2, false).equals({
    name : "trick",
    value : "value",
    value2 : "value2",
    value3 : "value3"
}));

var deep = {
    first : {
        name : "trick",
        val : "value"
    },
    second : {
        foo : "bar"
    }
};

var deepMerge = {
    first : {
        name : "track",
        anotherVal : "wohoo"
    },
    second : {
        foo : "baz",
        bar : "bam"
    },
    v : "on first layer"
};

assertTrue("Deep merges", clone(deep).merge(deepMerge).equals({
    first : {
        name : "track",
        val : "value",
        anotherVal : "wohoo"
    },
    second : {
        foo : "baz",
        bar : "bam"
    },
    v : "on first layer"
}));

assertTrue("Deep merges no override", clone(deep).merge(deepMerge, false).equals({
    first : {
        name : "trick",
        val : "value",
        anotherVal : "wohoo"
    },
    second : {
        foo : "bar",
        bar : "bam"
    },
    v : "on first layer"
}));

var obj1 = {a: 1, b: "hello"};
obj1.merge({c: 3});
assertTrue(obj1.equals({a: 1, b: "hello", c: 3}));

obj1.merge({a: 2, b: "mom", d: "new property"}, false);
assertTrue(obj1.equals({a: 1, b: "hello", c: 3, d: "new property"}));

var obj2 = {};
obj2.merge({a: 1}, {b: 2}, {a: 3});
assertTrue(obj2.equals({a: 3, b: 2}));

var a = [];
var b = [1, [2, 3], 4];
a.merge(b);
assertEquals(1, a[0]);
assertEquals([2, 3], a[1]);
assertEquals(4, a[2]);


var o1 = {};
var o2 = {a: 1, b: {c: 2}};
var o3 = {d: 3};
o1.merge(o2, o3);
assertTrue(o1.equals({a: 1, b: {c: 2}, d: 3}));
o1.b.c = 99;
assertTrue(o2.equals({a: 1, b: {c: 2}}));

// checking types with arrays and objects
var bo;
a = [];
bo = [1, {0:2, 1:3}, 4];
b = [1, [2, 3], 4];

a.merge(b);
assertTrue("Array stays Array?", Array.isArray(a[1]));

a = [];
a.merge(bo);
assertTrue("Object stays Object?", !Array.isArray(a[1]));

a = [];
a.merge(b);
a.merge(bo);
assertTrue("Object overrides Array", !Array.isArray(a[1]));

a = [];
a.merge(b);
a.merge(bo, false);
assertTrue("Object does not override Array", Array.isArray(a[1]));

a = [];
a.merge(bo);
a.merge(b);
assertTrue("Array overrides Object", Array.isArray(a[1]));

a = [];
a.merge(bo);
a.merge(b, false);
assertTrue("Array does not override Object", !Array.isArray(a[1]));

내 equals 메소드는 여기에서 찾을 수 있습니다 : JavaScript의 객체 비교



9

Ext JS 4 에서는 다음과 같이 수행 할 수 있습니다.

var mergedObject = Ext.Object.merge(object1, object2)

// Or shorter:
var mergedObject2 = Ext.merge(object1, object2)

merge (object) : Object를 참조하십시오 .


1
+2 년이 지나도 여전히 EXT.Object.merge의 버그 EXTJS-9173에주의하십시오
Chris

9

와우 .. 이것은 여러 페이지에서 본 첫 번째 StackOverflow 게시물입니다. 다른 "답변"추가에 대한 사과

이 방법은 ES5 이하 입니다. ES6를 다루는 다른 답변이 많이 있습니다.

속성을 사용하는 "깊은" 개체 병합을 보지 못했습니다 arguments. 내 대답은 컴팩트 하고 재귀 적이므로 무제한 객체 인수를 전달할 수 있습니다.

function extend() {
    for (var o = {}, i = 0; i < arguments.length; i++) {
        // if (arguments[i].constructor !== Object) continue;
        for (var k in arguments[i]) {
            if (arguments[i].hasOwnProperty(k)) {
                o[k] = arguments[i][k].constructor === Object ? extend(o[k] || {}, arguments[i][k]) : arguments[i][k];
            }
        }
    }
    return o;
}

주석 처리 된 부분은 선택 사항입니다. 개체가 아닌 전달 된 인수를 건너 뛰기 만하면됩니다 (오류 방지).

예:

extend({
    api: 1,
    params: {
        query: 'hello'
    }
}, {
    params: {
        query: 'there'
    }
});

// outputs {api: 1, params: {query: 'there'}}

이 대답은 이제 바다에 떨어졌지만 ...


가장 효과적인 짧은 답변! 더 많은 투표를받을 가치가 있습니다!
Jens Törnell

8
var obj1 = { food: 'pizza', car: 'ford' }
var obj2 = { animal: 'dog' }

// result
result: {food: "pizza", car: "ford", animal: "dog"}

jQuery.extend () 사용하기 - 링크

// Merge obj1 & obj2 to result
var result1 = $.extend( {}, obj1, obj2 );

_.merge () 사용 - 링크

// Merge obj1 & obj2 to result
var result2 = _.merge( {}, obj1, obj2 );

_.extend () 사용 - 링크

// Merge obj1 & obj2 to result
var result3 = _.extend( {}, obj1, obj2 );

Object.assign () 사용 ECMAScript 2015 (ES6) - 링크

// Merge obj1 & obj2 to result
var result4 = Object.assign( {}, obj1, obj2 );

모두의 출력

obj1: { animal: 'dog' }
obj2: { food: 'pizza', car: 'ford' }
result1: {food: "pizza", car: "ford", animal: "dog"}
result2: {food: "pizza", car: "ford", animal: "dog"}
result3: {food: "pizza", car: "ford", animal: "dog"}
result4: {food: "pizza", car: "ford", animal: "dog"}

7

Markus 'vsync'answer를 기반으로 하는 확장 버전입니다. 이 함수는 여러 인수를 취합니다. DOM에서 속성을 설정하는 데 사용할 수 있습니다 노드에서 하고 값의 깊은 복사본을 만드는 . 그러나 첫 번째 주장은 참고로 제공됩니다.

DOM 노드를 감지하기 위해 isDOMNode () 함수가 사용됩니다 ( JavaScript isDOM — JavaScript 오브젝트가 DOM 오브젝트인지 어떻게 확인합니까? )

Opera 11, Firefox 6, Internet Explorer 8 및 Chrome 16 에서 테스트되었습니다 .

암호

function mergeRecursive() {

  // _mergeRecursive does the actual job with two arguments.
  var _mergeRecursive = function (dst, src) {
    if (isDOMNode(src) || typeof src !== 'object' || src === null) {
      return dst;
    }

    for (var p in src) {
      if (!src.hasOwnProperty(p))
        continue;
      if (src[p] === undefined)
        continue;
      if ( typeof src[p] !== 'object' || src[p] === null) {
        dst[p] = src[p];
      } else if (typeof dst[p]!=='object' || dst[p] === null) {
        dst[p] = _mergeRecursive(src[p].constructor===Array ? [] : {}, src[p]);
      } else {
        _mergeRecursive(dst[p], src[p]);
      }
    }
    return dst;
  }

  // Loop through arguments and merge them into the first argument.
  var out = arguments[0];
  if (typeof out !== 'object' || out === null)
    return out;
  for (var i = 1, il = arguments.length; i < il; i++) {
    _mergeRecursive(out, arguments[i]);
  }
  return out;
}

몇 가지 예

innerHTML 및 HTML 요소 스타일 설정

mergeRecursive(
  document.getElementById('mydiv'),
  {style: {border: '5px solid green', color: 'red'}},
  {innerHTML: 'Hello world!'});

배열과 객체를 병합합니다. undefined를 사용하면 왼쪽 배열 / 객체의 값을 보존 할 수 있습니다.

o = mergeRecursive({a:'a'}, [1,2,3], [undefined, null, [30,31]], {a:undefined, b:'b'});
// o = {0:1, 1:null, 2:[30,31], a:'a', b:'b'}

JavaScript 객체가 아닌 인수 (널 포함)는 무시됩니다. 첫 번째 인수를 제외하고 DOM 노드도 삭제됩니다. new String ()처럼 생성 된 문자열은 사실 객체라는 것을주의하십시오.

o = mergeRecursive({a:'a'}, 1, true, null, undefined, [1,2,3], 'bc', new String('de'));
// o = {0:'d', 1:'e', 2:3, a:'a'}

두 객체를 새로운 객체로 병합하려면 (두 가지에 영향을 미치지 않고) 첫 번째 인수로 {}를 제공하십시오

var a={}, b={b:'abc'}, c={c:'cde'}, o;
o = mergeRecursive(a, b, c);
// o===a is true, o===b is false, o===c is false

편집 (ReaperSoon) :

배열을 병합하려면

function mergeRecursive(obj1, obj2) {
  if (Array.isArray(obj2)) { return obj1.concat(obj2); }
  for (var p in obj2) {
    try {
      // Property in destination object set; update its value.
      if ( obj2[p].constructor==Object ) {
        obj1[p] = mergeRecursive(obj1[p], obj2[p]);
      } else if (Array.isArray(obj2[p])) {
        obj1[p] = obj1[p].concat(obj2[p]);
      } else {
        obj1[p] = obj2[p];
      }
    } catch(e) {
      // Property in destination object not set; create it and set its value.
      obj1[p] = obj2[p];
    }
  }
  return obj1;
}


5

lodash의 기본값을 사용해야합니다.

_.defaultsDeep({ 'user': { 'name': 'barney' } }, { 'user': { 'name': 'fred', 'age': 36 } });
// → { 'user': { 'name': 'barney', 'age': 36 } }

5

Underscore.js를 사용하면 객체 배열을 병합하려면 다음을 수행하십시오.

var arrayOfObjects = [ {a:1}, {b:2, c:3}, {d:4} ];
_(arrayOfObjects).reduce(function(memo, o) { return _(memo).extend(o); });

결과는 다음과 같습니다.

Object {a: 1, b: 2, c: 3, d: 4}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.