深度分配 JavaScript 对象字面量

8

我想要深度赋值一个对象的值。例如:

const errors = {}
if(errorOnSpecificField) {
  // TypeError: Cannot read property 'subSubCategory' of undefined(…)
  errors.subCategory.subSubCategory.fieldWithError = 'Error Message'
}

现在,没有使用lodash,我可以做到:

const errors = {}
if(errorOnSpecificField) {
    errors.subCategory = errors.SubCategory || {}
    errors.subCategory.subSubCategory = errors.SubCategory.subSubCategory || {}
    errors.subCategory.subSubCategory.fieldWithError = 'Error Message'
}

使用lodash,我可以做到这一点:
const errors = {}
if(errorOnSpecificField) {
    _.set(errors, 'subCategory.subSubCategory.fieldWithError', 'Error Message');
}

我希望避免使用第三方库。现在es2015已经有对象解构,是否有更优雅的解决方案?反向操作很容易:

  let {subCategory : {subSubCategory: {fieldWithError}}} = errors

什么是深层对象赋值的优雅解决方案?谢谢!

2
Object.assign()? - 它会覆盖所有属性 - le_m
正如你所提到的,因为它会覆盖已经设置的其他属性,所以这对我的情况不起作用。 - aherriot
你可以寻找许多 extend 实现中的一些。 - le_m
为什么不直接使用 errors.subCategory = {subSubCategory: {fieldWithError: 'Error Message'}} 呢?这正好是解构的反向操作。 - Bergi
问题在于,如果我已经定义了其他的子子类别,它将覆盖它们。 - aherriot
4个回答

0
这是一种相当易读的方式,可以安全地分配给深层对象:
(((errors||{}).subCategory||{}).subSubCategory||{}).fieldWithError = 'Error Message'

但是如果errors.subCategory.subSubCategory不存在,那么这并不会创建它。


2
这实际上并不适用于赋值操作。对于访问来说是可以的,但不能用于创建属性。 - Bergi
嗯...它不会创建errors.subCategory.subSubCategory,但如果它已经存在,它将安全地分配给它。 - Bo Borgerson

0

简短回答,没有一种干净的方法可以在不编写方法的情况下完成此操作(说实话,您可以使用lodash中的方法而无需导入整个库)

... 然而 ...

警告 仅供娱乐。请勿在生产环境中尝试此操作(需要es6)。

Object.prototype.chainSet = function() {
  let handler = {
    get (target, name) {
      if (!(name in target)) {
        target[name] = new Proxy({}, handler)
      }
      return target[name]
    }
  }

  return new Proxy(this, handler)
}

使用:

let a = {}
a.chainSet().foo.bar.baz = 1
a.foo.bar.baz // => 1

0

Object.assign() 可以完美地满足你的需求。

let errors = { otherField: "value" };
let newobj = {subCategory : {subSubCategory: {fieldWithError: "Error goes here"}}};
Object.assign(errors, newobj);

这将产生:

{
  otherField:'value',
  subCategory: {
    subSubCategory: {
      fieldWithError:'Error goes here'
    }
  }
}

1
如果errors = {subCategory: {x: "y"}}并且您分配了newobj,则属性x将被“删除”(整个subCategory对象将被替换)。操作者正在寻找一种避免这种情况的方法。 - le_m

0
你可以尝试以下代码:
function ErrorRegistry(obj)
{
    this.errors = obj || {};
    this.addError = function(k, msg)
    {
        var keys = k.split('.');
        var o = this.errors;
        for(var i = 0, l = keys.length, last = l-1; i<l; i++)
        {
            if(typeof o[keys[i]] === 'undefined')
                o[keys[i]] = {};
            if(i == last)
                o[keys[i]] = msg;
            else
                o = o[keys[i]];
        }
    };
}
var errors = {'subCategory1':{'fieldWithError1':'Error1'}};
var errorRegistry = new ErrorRegistry(errors);
errorRegistry.addError('subCategory1.fieldWithError2', "Error2");
errorRegistry.addError('subCategory1.subSubCategory1.fieldWithError3', "Error3");
errorRegistry.addError('subCategory1.subSubCategory2.fieldWithError4', "Error4");
errors = errorRegistry.errors;
console.log(errors);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接