Javascript 如果键是字符串,如何部分更新嵌套对象?

Javascript 如果键是字符串,如何部分更新嵌套对象?,javascript,underscore.js,Javascript,Underscore.js,我有下划线 我有: var person = { personal: { fname: 'Victor', lname: 'Lee', address: { street: '1234 Main', state: { abbrName: 'CA', fullName: 'California', timezone: 'PST' }, zip: '94043' }

我有下划线

我有:

var person = {
  personal: {
    fname: 'Victor',
    lname: 'Lee',
    address: {
      street: '1234 Main',
      state: {
        abbrName: 'CA',
        fullName: 'California',
        timezone: 'PST'
      },
      zip: '94043'
    }
  }
};
我想更新多个属性,但保留其他属性不变

不要写三行分开的字:

  person.personal.address.state.abbrName = 'OR';
  person.personal.address.state.fullName = 'Oregon';
  person.personal.address.zip = '97062';
我希望能够在一行代码中设置所有道具,但同时保持其他属性不变

如果我这样做:

_.extend(person.personal.address, {
  state: {
    abbrName: 'OR',
    fullName: 'Oregon'
  },
  zip: '97032'
});
结果对象的时区已被砍掉:

{
  personal: {
    fname: 'Victor',
    lname: 'Lee',
    address: {
      street: '1234 Main',
      state: {
        abbrName: 'CA',
        fullName: 'California',
      },
      zip: '94043'
    }
  }
};
像这样的东西很理想:

var updateObj = function(obj, key, value){
  // stuff
  return obj;
};
然后像这样跑:

updateObj(person, 'personal.address', {
  state: {
    abbrName: 'OR',
    fullName: 'Oregon'
  },
  zip: '97032'
});
到目前为止,我已经得到了这个,但它一次只完全覆盖一个属性

var updateObjectWithStringKey = function(obj, key, value) {
  if (typeof key === "string"){
    key = key.split(".");
  };

  if (prop.length > 1) {
    var e = key.shift();
    updateObjectWithStringKey(obj[e] =
      typeof obj[e] == 'object' ? obj[e] : {},
      key,
      value);
  } else {
    obj[key[0]] = value;
  };

  return obj;

};
编辑

好吧,我想我已经接近了:

var MergeRecursive = function(destination, source) {

  for (var p in source) {

    if ( typeof source[p] == 'object' ) {
      destination[p] = MergeRecursive(destination[p], source[p]);
    } else {
      destination[p] = source[p];
    };

  };

  return destination;

};
这会合并信息,即使信息被楔入对象中的各个级别:

var person = {
  personal: {
    fname: 'Victor',
    lname: 'Lee',
    address: {
      street: '1234 Main',
      state: {
        abbrName: 'CA',
        fullName: 'California',
        timezone: 'PST'
      },
      zip: '94043'
    }
  }
};

var updatedInfo = {
  personal: {
    address: {
      state: {
        abbrName: 'OR',
        fullName: 'Oregon',
        capital: 'Salem'
      },
      zip: '97062'
    },
  }
};

MergeRecursive(person, updatedInfo);
返回

{
  personal: {
    fname: 'Victor',
    lname: 'Lee',
    address: {
      street: '1234 Main',
      state: {
        abbrName: 'OR',
        fullName: 'Oregon',
        timezone: 'PST',
        capital: 'Salem'
      },
      zip: '97062'
    }
  }
}
但正如我所说,我想提供一个字符串路径,指向我想更新的对象部分:

updateObj(person, 'personal.address', {
  state: {
    abbrName: 'OR',
    fullName: 'Oregon',
    capital: 'Salem'
  },
  zip: '97062'
});
此函数执行此操作,但不使用上述合并行为:

var updateObjectWithStringProp = function(obj, prop, value) {

  if (typeof prop === "string") {
    var prop = prop.split('.');
  }

  if (prop.length > 1) {
    var p = prop.shift();
    if (obj[p] == null || typeof obj[p] !== 'object') {
      obj[p] = {};
    }
    updateObjectWithStringProp(obj[p], prop, value);
  } else {
    obj[prop[0]] = value;
  }

  return obj;

};

如何编辑此函数以同时包含合并行为?

是否可以将库从下划线更改为

然后,您将拥有相同(以及更多)的可用功能,包括
\uuu.defaultsDeep

您可以在1行中执行合并,在中间保留您的时区。

var person = {
  personal: {
    fname: 'Victor',
    lname: 'Lee',
    address: {
      street: '1234 Main',
      state: {
        abbrName: 'CA',
        fullName: 'California',
        timezone: 'PST'
      },
      zip: '94043'
    }
  }
};
var update = {
  personal: { 
    address: { 
      state: { 
        abbrName: 'OR', 
        fullName: 'Oregon' 
      }, 
      zip: '97032'
    }
  }
}
var result = _.defaultsDeep(update, person);
结果将决定:

result = {
  "personal":{
    "address":{
      "state":{
        "abbrName":"OR",
        "fullName":"Oregon",
        "timezone":"PST"
      },
      "zip":"97032",
      "street":"1234 Main"
    },
    "fname":"Victor",
    "lname":"Lee"
  }
}

您可以使用递归,但请记住,它可能会遇到具有高嵌套的大型对象的问题

注意:在做这样的函数

之前,你应该考虑一些事情。
  • 这两个值都可以是简单对象
  • 这两个值都可以是对象的数组
  • 这两个值都可以是数组
  • 新对象可以有额外的参数。是否允许向原始对象添加值
  • 源对象可以为空吗?如果是,您应该返回
    未定义的
    还是
    新值
也可以有其他参数

函数mergeObject(源、newValue、allowNewProps){
if(typeof newValue==“object”&&typeof source==“object”){
for(新值中的var k){
if(Array.isArray(newValue[k])&&Array.isArray(source[k])){
对于(var i=0;i
在上面概述的特定情况下,您是否有理由不尝试
.extend(person.personal.address,{state:'MO',zip:44444})
?您使用
Object.prototype.toString.call(obj[e])====“[Object Object]”而不是
obj[e]的类型==“object”
?使用问题中的mergerecursive函数。好的,我已经编辑了原始问题。最大的问题是更新夹在对象两个级别之间的信息。@Jorg这是一个非常普通的答案。try..catch完全没有必要,而且
obj2[p]。构造函数==Object
在许多情况下都会失败。