如何自动向未定义的对象添加属性?

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

考虑下面的例子:

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

72615 次浏览
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.
var test = {}
if(!test.hasOwnProperty('hello')) {
test.hello = {};
}
test.hello.world = "Hello World!"

You won't be able to do this without some sort of function, as JavaScript doesn't have a generic getter/setter method for objects (Python, for example, has __getattr__). Here's one way to do it:

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

If you really want to, you could add it to the prototype of Object. You can call it like so:

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

I've come up with something, really custom as well, but it works as far as I have tested.

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

Then, a complex variable can be set like this, ensuring that every links will be created if not already:

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

See this working FIDDLE.

This will add a property hello whose value is {world: 'Hello world!'} to the test object, if it doesn't exist. If you have a lot of these objects, you can just iterate over them and apply this function. Note: uses lodash.js

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

Which is actually a convenience method for saying:

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

Note: _.defaults uses loops to achieve the same thing as the second block.

P.S. Checkout https://stackoverflow.com/a/17197858/1218080

Well you could extend the prototype of Object with a function that return a property, but adds it first, if it doesn't exist:

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


var obj = {};


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

New object

myObj = {};

recursive function

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;


}

Call it with a dot notated string

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

or with an array

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

and your object will look like this

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

It works with non-empty objects too!

I use this:

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

You can later do 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}
var test = {}
test.hello.world = "Hello doesn't exist!"

This will throw an error obviously as you didn't defined the test.hello

Firstly you need to need define the hello key then inside you can assign any key. But if you want to create key if not exists then you can do following thing

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

The above statement will create the test.hello object if not defined and if it is defined then it will assign the same value as it is previously

Now you can assign any new key inside the test.hello

test.hello.world = "Everything works perfect";


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

Here's a cool version with proxies:

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

And you use it like this:

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

This will add all the missing properties as empty objects, and leave the existing ones untouched. It's really just a proxied version of the classic test.hello = test.hello || {}, albeit much slower (See benchmark here.) But it's also much nicer to look at, especially if you'll be doing it more than one level deep. I wouldn't pick it for performance-heavy data crunching, but it's probably fast enough for a front-end state update (as in Redux).

Note that there's some implicit assumptions here:

  1. The intervening properties are either objects or non-existent. This will choke if test.hello is a string, for example.
  2. That you always want to be doing this for as long as you're using the Proxy instead of the original object.

These are pretty easily mitigated if you only use it in well-bounded contexts (like a reducer body) where there's little chance of accidentally returning the Proxy, and not much else you would want to do with the object.

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

When using the spread operator, the value can be undefined, it'll automatically create an object.

I've made some changes on columbus's answer to allow create arrays:

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)

I think that is possible to allow use "sub1[].sub2..." to just push into the sub1 array, instead of specify the index, but that's enough for me now.

You can use the Logical nullish assignment (??=):

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

I think the easiest way is to use _.set from Lodash

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

Option 1:

Use Object.assign:

var test = {};
Object.assign(test, { hello: { world: "hello world" }});


console.log(test.hello.world); // "hello world"

Option 2:

Use Destructuring assignment:

var test = {};
test = {...test, ...{ hello: { world: "hello world" }}};


console.log(test.hello.world); // "hello world"