javascript - 합치기 - object.assign polyfill




두 JavaScript 객체의 속성을 어떻게 동적으로 병합 할 수 있습니까? (20)

런타임시 두 개의 JavaScript 객체를 병합 할 수 있어야합니다. 예를 들면 다음과 같습니다.

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

obj1.merge(obj2);

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

누구든지이 스크립트를 가지고 있거나이 작업을 수행하는 방법을 알고 있습니까? 재귀가 필요 없으며 함수를 병합 할 필요가 없습니다.


한 줄의 코드에서 N 개 개체의 속성 병합

Object.assign 메서드는 ECMAScript 2015 (ES6) 표준의 일부이며 필요한 작업을 정확하게 수행합니다. ( IE 는 지원되지 않음)

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

Object.assign () 메서드는 열거 가능한 모든 속성의 값을 하나 이상의 소스 객체에서 대상 객체로 복사하는 데 사용됩니다.

더 읽기 ...

이전 브라우저를 지원하는 polyfill :

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

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

  • 여러 객체를 병합 할 수있는 가능성 추가
  • 깊은 물체를 지원합니다.
  • override 매개 변수 (마지막 매개 변수가 부울 인 경우 감지 됨)

override가 false 인 경우, 프로퍼티는 오버라이드 (override)되지 않고 새로운 프로퍼티가 추가됩니다.

사용법 : obj.merge (병합 ... [, 재정의]);

여기 내 코드가 있습니다 :

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의 개체 비교


jQuery extends ()와 마찬가지로 AngularJS 에서도 같은 기능을 수행 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);

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.

개체 속성을 병합하는 코드를 찾아 봤는데 여기서 끝났어. 그러나 재귀 적 병합을위한 코드가 없었기 때문에 나는 그것을 직접 작성했다. (어쩌면 jQuery 확장은 재귀 적 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 } } };

너무 복잡하지 않은 객체의 경우 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_/

이 예제에서 "} {" 문자열 내에 있으면 안됩니다 .


바로 그 과정에서 병합 작업이 아닌 속성을 덮어 쓰는 것뿐입니다.

이것은 JavaScript 객체 영역이 실제로 병합되는 방법 from . 객체 자체가 아닌 객체의 키만이 from 의해 겹쳐 쓰여집니다. 다른 모든 것은 실제로 병합 됩니다. 물론 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);


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

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

주어진 솔루션은 할당 전에 for..in 루프에서 source.hasOwnProperty(property) 를 확인하도록 수정해야합니다. 그렇지 않으면 거의 프로토 타입 체인의 속성을 복사하지 않게됩니다.


Prototype 은 다음과 같습니다.

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

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



객체 전개 속성 - 현재 스테이지 3 ECMAScript 제안을 사용할 수 있습니다.

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

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


ECMAScript 2018 표준 방법

객체 스프레드를 사용 합니다 .

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

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

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

obj1obj2 모든 속성을 추가하기 만하면 수정되지 않은 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;
}

객체 병합은 간단합니다.

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


var mergedObj = Object.assign(obj1,obj2,obj3)

console.log(mergedObj);

오브젝트는 오른쪽에서 왼쪽으로 병합됩니다. 즉, 오른쪽 오브젝트와 동일한 특성을 갖는 오브젝트가 대체됩니다.

이 예제에서 obj2.carobj1.car 대체 obj1.car


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

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

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

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


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}

lodash의 defaultsDeep 사용해야합니다.defaultsDeep

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

를 기반으로 Markus'VSYNC'대답 ,이 확장 된 버전입니다. 이 함수는 여러 가지 인수를 취합니다. DOM 노드에서 속성을 설정하는 데 사용할 수 있으며 값의 전체 복사본을 만듭니다. 그러나 첫 번째 인수는 참조로 제공됩니다.

DOM 노드를 감지하려면 isDOMNode () 함수가 사용됩니다 ( 질문 JavaScript isDOM - JavaScript 객체가 DOM 객체인지 어떻게 확인합니까? ).

Opera 11, Firefox 6, Internet Explorer 8 및 Google 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 객체를 포함하지 않는 인수 (null 포함)는 무시됩니다. 첫 번째 인수를 제외하고는 DOM 노드도 삭제됩니다. 새 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;
}





javascript-objects