javascript - 将一个对象合并到另一个对象的最佳方法(不覆盖)

标签 javascript oop object properties iteration

<分区>

我想将一个对象 (parent) 合并到另一个对象 (window) 中而不覆盖现有值。
两个对象都不知道键、值和长度,但我可以放心地假设会有嵌套对象。
由于某些原因,我无法重新创建目标对象,它需要是一个实际的合并。

在 javascript 中执行此操作的最佳方法是什么?

示例:

var target = {
    prop1: {
        prop1stuff1: 42,
        prop3:       15.5
    },
    42:    'stuff'
};

var source = {
    prop1: {
        prop1stuff1: 42,
        prop4:       17,
        prop3:       18
    },
    '42':  'test'
};

function merge(t, s){
    //code to merge source (s) into target (t)
    //without overwriting existing values
    //and without overwriting t with a new object
}

merge(target, source); //alter target by reference, does not return anything

console.log(target);
// ^ this outputs:
{
    prop1: {
        prop1stuff1: 42,
        prop3:       15.5,
        prop4:       17
    },
    42:    'stuff'
}

编辑:

我不能为目标分配一个新对象,我必须一个一个地添加属性。
我也不知道嵌套对象会有多深

*第二次编辑:***

TJ Crowder 的回答有效,但我尝试合并的对象包含大量循环引用,导致无限循环。
我添加了一个循环引用检测器,我现在要更新 TJ Crowder 的答案。

最佳答案

您将执行从源到目标的属性递归副本,并检查以确保该属性不存在:

function merge(t, s){
  // Do nothing if they're the same object
  if (t === s) {
      return;
  }

  // Loop through source's own enumerable properties
  Object.keys(s).forEach(function(key) {
    // Get the value
    var val = s[key];

    // Is it a non-null object reference?
    if (val !== null && typeof val === "object") {
      // Yes, if it doesn't exist yet on target, create it
      if (!t.hasOwnProperty(key)) {
        t[key] = {};
      }

      // Recurse into that object
      merge(t[key], s[key]);

    // Not a non-null object ref, copy if target doesn't have it
    } else if (!t.hasOwnProperty(key)) {
      t[key] = s[key];
    }
  });
}

注意事项:

  • 以上假定源中的任何对象都是普通对象,因此如果它不存在于目标中,我们将使用 {} 创建它。这不是很复杂,我们可能想更进一步,比如检查它是否是一个数组,或者其他内置类型,并做一些更广泛的事情。但以上内容应该可以帮助您入门。

  • 我们在上面做“自己的”属性;您可以使用 for-in 循环而不是 Object.keys 来执行属性,包括从原型(prototype)继承的属性;那么您将使用 if (!(key in t)) 而不是 !t.hasOwnProperty(key)

例子:

var common = {
    commonProp: "I'm a prop on an object both target and source have"
};
var target = {
    prop1: {
        prop1stuff1: 42,
        prop3:       15.5
    },
    42:    'stuff',
    common: common
};

var source = {
    prop1: {
        prop1stuff1: 42,
        prop4:       17,
        prop3:       18
    },
    '42':  'test',
    common: common
};

function merge(t, s){
  // Do nothing if they're the same object
  if (t === s) {
      return;
  }

  // Loop through source's own enumerable properties
  Object.keys(s).forEach(function(key) {
    // Get the value
    var val = s[key];

    // Is it a non-null object reference?
    if (val !== null && typeof val === "object") {
      // Yes, if it doesn't exist yet on target, create it
      if (!t.hasOwnProperty(key)) {
        t[key] = {};
      }

      // Recurse into that object
      merge(t[key], s[key]);

    // Not a non-null object ref, copy if target doesn't have it
    } else if (!t.hasOwnProperty(key)) {
      t[key] = s[key];
    }
  });
}

merge(target, source);
document.body.innerHTML =
  "<pre>" + JSON.stringify(target) + "</pre>";


OP 扩展了上述内容以充分处理循环引用以达到他们的目的(可能不是通用的):

function merge(t, s){
// Do nothing if they're the same object
if (t === s) return;

// Loop through source's own enumerable properties
Object.keys(s).forEach(function(key) {

    // Get the value
    var val = s[key];

    // Is it a non-null object reference?
    if (val !== null && typeof val === "object") {

        // Yes, if it doesn't exist yet on target, create it
        if (!t.hasOwnProperty(key)) t[key] = {};

        // Recurse into that object IF IT DOES NOT CONTAIN CIRCULAR REFERENCES
        if ( !isCyclic( t[ key ] ) && !isCyclic( s[ key ] ) ) merge( t[ key ], s[ key ] );

        // Not a non-null object ref, copy if target doesn't have it
    } else if (!t.hasOwnProperty(key)) t[key] = s[key];
});

function isCyclic( obj ) {
    var seenObjects = [];
    function detect( obj ) {
        if ( obj && typeof obj === 'object' ) {
            if ( seenObjects.indexOf( obj ) !== -1 ) return true;
            seenObjects.push( obj );
            for ( var key in obj ) if ( obj.hasOwnProperty( key ) && detect( obj[ key ] ) ) return true;
        }
        return false;
    }
    return detect( obj );
}

//and now... Merge!
merge( window, parent );
//window now has all properties of parent
//that it didn't have before

关于javascript - 将一个对象合并到另一个对象的最佳方法(不覆盖),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34063998/

相关文章:

javascript - 数组位于另一个数组中

java - 为什么编码应该是 "to the interface"特别是对于原始数据类型?

带有 jQ​​uery 的 JavaScript OOP

python - 理解 Django 的模型类

带有对象键的 JavaScript setter 和 getter

c++ - 返回一个空 vector c++

javascript - Angular 2 : How to set default value of field from parent component back to child component?

javascript - 如何在整个 Angular 应用程序中引用 Youtube Iframe API 播放器

javascript - Jquery Select2 4 - 使用自定义数据时的 Ajax "no results found"

java - 如何使用多个类的序列化来实现备份和恢复?