94

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

考虑以下示例:

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 数组中,它与对象的问题相同。

4

14 回答 14

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

If test.hello is undefined, it gets set to an empty object.

If test.hello was previously defined, it stays unchanged.

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.
于 2013-07-14T21:31:08.953 回答
40

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

var test = {};
(test.hello ??= {}).world ??= "Hello doesn't exist!";
于 2021-03-20T22:38:31.407 回答
18

新对象

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

它也适用于非空对象!

于 2015-12-10T14:56:46.373 回答
7

如果没有某种函数,您将无法做到这一点,因为 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
于 2013-07-14T21:42:13.780 回答
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;
于 2013-07-14T22:07:27.417 回答
5

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

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 || {},虽然速度要慢得多(请参阅此处的基准。)但它看起来也更好看,特别是如果你要做的不仅仅是一个级别的深度。我不会选择它来处理需要大量性能的数据,但它对于前端状态更新(如在 Redux 中)来说可能已经足够快了。

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

  1. 中间属性要么是对象,要么是不存在的。test.hello例如,如果是一个字符串,这将阻塞。
  2. 只要您使用代理而不是原始对象,您总是希望这样做。

如果您只在边界良好的上下文(如减速器主体)中使用它,这些情况很容易缓解,在这种情况下,意外返回代理的可能性很小,而且您不想对对象做太多其他事情。

于 2018-09-26T22:26:02.560 回答
2
var test = {}
if(!test.hasOwnProperty('hello')) {
    test.hello = {};
}
test.hello.world = "Hello World!"
于 2013-07-14T21:31:27.323 回答
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使用循环来实现与第二个块相同的事情。

PS结帐https://stackoverflow.com/a/17197858/1218080

于 2013-07-14T21:55:39.850 回答
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

于 2013-07-14T21:42:40.803 回答
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';
于 2017-09-28T06:24:44.207 回答
1

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

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

于 2019-01-17T02:11:24.100 回答
1

我用这个:

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

你可以稍后做fe:

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}
于 2017-03-21T08:29:24.360 回答
1

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

 _.set({}, 'a[0].b.c', 4);
// => { a: [{ b: { c: 4 } }] }
于 2021-05-15T19:24:25.653 回答
0

我对哥伦布的回答进行了一些更改以允许创建数组:

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数组,而不是指定索引,但这对我来说已经足够了。

于 2020-06-19T15:57:38.317 回答