如果未定义,则自动创建对象

时间:2013-07-14 21:28:28

标签: javascript

如果对象尚未存在,是否有一种简单的方法可以自动添加属性?

考虑以下示例:

var test = {}
test.hello.world = "Hello doesn't exist!"

这不起作用,因为未定义hello

我问这个的原因是因为我有一些现有的对象,我不知道它们是否已经有hello。实际上我在代码的不同部分有很多这些对象。 总是检查hello是否存在以及是否不创建新对象非常烦人:

var test = {}
if(test.hello === undefined) test.hello = {}
test.hello.world = "Hello World!"

在这个例子中,有没有办法自动创建像hello这样的对象?

我在php中的意思是这样的:

$test = array();  
$test['hello']['world'] = "Hello world";   
var_dump($test);

输出:

array(1) {
  ["hello"]=>
  array(1) {
    ["world"]=>
    string(11) "Hello world"
  }
}

好吧,这是一个数组,但在js数组中,它与对象的问题相同。

14 个答案:

答案 0 :(得分:102)

var test = {};
test.hello = test.hello || {};
test.hello.world = "Hello world!";

如果未定义test.hello,则将其设置为空对象。

如果先前已定义test.hello,则保持不变。

var test = {
  hello : {
    foobar : "Hello foobar"
  }
};

test.hello = test.hello || {};
test.hello.world = "Hello World";

console.log(test.hello.foobar); // this is still defined;
console.log(test.hello.world); // as is this.

答案 1 :(得分:11)

新对象

myObj = {};

递归函数

function addProps(obj, arr, val) {

    if (typeof arr == 'string')
        arr = arr.split(".");

    obj[arr[0]] = obj[arr[0]] || {};

    var tmpObj = obj[arr[0]];

    if (arr.length > 1) {
        arr.shift();
        addProps(tmpObj, arr, val);
    }
    else
        obj[arr[0]] = val;

    return obj;

}

用点标记字符串

调用它
addProps(myObj, 'sub1.sub2.propA', 1);

或使用数组

addProps(myObj, ['sub1', 'sub2', 'propA'], 1);

,您的对象将如下所示

myObj = {
  "sub1": {
    "sub2": {
      "propA": 1
    }
  }
};

它也适用于非空对象!

答案 2 :(得分:11)

您可以使用逻辑空赋值 (??=):

var test = {};
(test.hello ??= {}).world ??= "Hello doesn't exist!";

答案 3 :(得分:5)

如果没有某种功能,你将无法做到这一点,因为JavaScript没有对象的通用getter / setter方法(例如,Python有__getattr__)。这是一种方法:

function add_property(object, key, value) {
    var keys = key.split('.');

    while (keys.length > 1) {
        var k = keys.shift();

        if (!object.hasOwnProperty(k)) {
            object[k] = {};
        }

        object = object[k];
    }

    object[keys[0]] = value;
}

如果您真的想要,可以将其添加到Object的原型中。你可以这样称呼它:

> var o = {}
> add_property(o, 'foo.bar.baz', 12)
> o.foo.bar.baz
12

答案 4 :(得分:5)

您可以使用返回属性的函数扩展Object的原型,但如果它不存在则先添加它:

Object.prototype.getOrCreate = function (prop) {
    if (this[prop] === undefined) {
        this[prop] = {};
    }
    return this[prop];
};

var obj = {};

obj.getOrCreate("foo").getOrCreate("bar").val = 1;

答案 5 :(得分:3)

这是一个带有代理的很酷的版本:

const myUpsert = (input) => {
    const handler = {
        get: (obj, prop) => {
            obj[prop] = obj[prop] || {};
            return myUpsert(obj[prop]);
        }
    };
    return new Proxy(input, handler);
};

您可以这样使用它:

myUpsert(test).hello.world = '42';

这会将所有缺少的属性添加为空对象,并保持现有属性不变。它实际上只是经典test.hello = test.hello || {}的代理版本,尽管速度较慢(See benchmark here。)但是它看起来也要好得多,尤其是如果您要进行一个以上级别的深入研究时。我不会选择它来进行性能繁重的数据处理,但是它对于前端状态更新(例如在Redux中)可能足够快。

请注意,这里有一些隐含的假设:

  1. 插入的属性是对象或不存在。例如,如果test.hello是一个字符串,则会阻塞。
  2. 只要使用代理而不是原始对象,您就一直希望这样做。

如果仅在边界有限的环境(如化简器主体)中使用它,这些情况就很容易缓解,在这种情况下,意外返回代理的可能性很小,并且您不想对该对象做很多其他事情。

答案 6 :(得分:2)

这将向测试对象添加值hello的属性{world: 'Hello world!'}(如果它不存在)。如果你有很多这些对象,你可以迭代它们并应用这个函数。注意:使用lodash.js

var test = {};
_.defaults(test, { hello: {world: 'Hello world!'} });    

这实际上是一种方便的说法:

var defaults = _.partialRight(_.assign, function(a, b) {
  return typeof a == 'undefined' ? b : a;
});        
defaults(test, { hello: {world: 'Hello world!'} });

注意:_.defaults使用循环来实现与第二个块相同的东西。

P.S。结帐https://stackoverflow.com/a/17197858/1218080

答案 7 :(得分:2)

我认为最简单的方法是使用来自 Lodash 的 _.set

 _.set({}, 'a[0].b.c', 4);
// => { a: [{ b: { c: 4 } }] }

答案 8 :(得分:1)

var test = {}
if(!test.hasOwnProperty('hello')) {
    test.hello = {};
}
test.hello.world = "Hello World!"

答案 9 :(得分:1)

我想出了一些东西,也是真正习惯的东西,但是就我测试而言它起作用了。

function dotted_put_var(str,val) {
    var oper=str.split('.');
    var p=window;
    for (var i=0;i<oper.length-1;i++) {
        var x=oper[i];
        p[x]=p[x]||{};
        p=p[x];
    }
    p[oper.pop()]=val;
}

然后,可以像这样设置一个复杂的变量,确保每个链接都已创建,如果还没有:

dotter_put_var('test.hello.world', 'testvalue'); // test.hello.world="testvalue";

请参见此工作FIDDLE

答案 10 :(得分:1)

var test = {}
test.hello.world = "Hello doesn't exist!"

由于您没有定义test.hello

,因此显然会抛出错误

首先你需要定义hello键然后在里面你可以分配任何键。 但是如果你想创建密钥,如果不存在,那么你可以做以下事情

test.hello = test.hello || {};

如果没有定义,上面的语句将创建test.hello对象,如果定义了它,那么它将分配与之前相同的值

现在您可以在test.hello

中分配任何新密钥
test.hello.world = "Everything works perfect";

test.hello.world2 = 'With another key too, it works perfect';

答案 11 :(得分:0)

我用这个:

Object.prototype.initProperty = function(name, defaultValue) {
  if (!(name in this)) this[name] = defaultValue;
};

你以后可以做f.e。:

var x = {a: 1};
x.initProperty("a", 2); // will not change property a
x.initProperty("b", 3); // will define property b
console.log(x); // => {a: 1, b: 3}

答案 12 :(得分:0)

let test = {};
test = {...test, hello: {...test.hello, world: 'Hello does exist!'}};
console.log(test);

使用散布运算符时,该值可以是未定义的,它将自动创建一个对象。

答案 13 :(得分:0)

我对columbus's answer进行了一些更改,以允许创建数组:

function addProps(obj, arr, val) {

  if (typeof arr == 'string')
    arr = arr.split(".");

  var tmpObj, isArray = /^(.*)\[(\d+)\]$/.exec(arr[0])
  if (isArray && !Number.isNaN(isArray[2])) {
    obj[isArray[1]] = obj[isArray[1]] || [];
    obj[isArray[1]][isArray[2]] = obj[isArray[1]][isArray[2]] || {}
    tmpObj = obj[isArray[1]][isArray[2]];
  } else {
    obj[arr[0]] = obj[arr[0]] || {};
    tmpObj = obj[arr[0]];
  }

  if (arr.length > 1) {
    arr.shift();
    addProps(tmpObj, arr, val);
  } else
    obj[arr[0]] = val;

  return obj;

}


var myObj = {}
addProps(myObj, 'sub1[0].sub2.propA', 1)
addProps(myObj, 'sub1[1].sub2.propA', 2)

console.log(myObj)

我认为可以允许使用“ sub1 []。sub2 ...”仅推入sub1数组,而不是指定索引,但这对我来说已经足够了。