-->

dynamic keys for object literals in Javascript

2019-01-01 11:56发布

问题:

Ok so I\'m working away on a project in Nodes, and I\'ve come across a small problem with the keys in object literals, I have the following set-up:

var required = {
    directories : {
        this.applicationPath                    : \"Application \" + this.application + \" does not exists\",
        this.applicationPath + \"/configs\"       : \"Application config folder does not exists\",
        this.applicationPath + \"/controllers\"   : \"Application controllers folder does not exists\",
        this.applicationPath + \"/public\"        : \"Application public folder does not exists\",
        this.applicationPath + \"/views\"         : \"Application views folder does not exists\"
    },
    files : {
        this.applicationPath + \"/init.js\"               : \"Application init.js file does not exists\",
        this.applicationPath + \"/controllers/index.js\"  : \"Application index.js controller file does not exists\",
        this.applicationPath + \"/configs/application.js\": \"Application configs/application.js file does not exists\",
        this.applicationPath + \"/configs/server.js\"     : \"Application configs/server.js file does not exists\"
    }
}

Ok so many of you will look at this and think it look\'s OK, but the compiler keeps telling me that I am missing a : (colon), which im not, it seems like the + or and the . are both effecting the compiler.

Now i believe (not sure), that object literals are created at compile time, and not run-time, meaning that dynamic variables such as this.applicationPath and concatenation are not going to be available :( :(

What\'s the best way to overcome an obstacle like this without having to rewrite large chunks of code.

回答1:

The only way you can set dynamic keys is with bracket notation:

required.directories[this.applicationPath + \"/configs\"] = \"Application config folder does not exists\";

(of course wherever you do this definition, this.applicationPath must exist)

But do you need this.applicationPath in the keys? How do you access theses values? Maybe you can just remove this.applicationPath from whatever value you use to access the properties.


But in case you need it:

You could use an array to initialize the keys if you want to avoid repeating a lot of code:

var dirs = [\'configs\', \'controllers\', ...];
var files = [\'init.js\', \'controllers/index.js\', ...];

var required = { directories: {}, files: {} };
required.directories[this.applicationPath] = \"Application \" + this.application + \" does not exists\";

for(var i = dirs.length; i--;) {
    required.directories[this.applicationPath + \'/\' + dirs[i]] = \"Application \" + dirs[i] + \" folder does not exists\";
}

for(var i = files.length; i--;) {
    // same here
}


回答2:

In an object literal (ECMA-262 §11.1.5 calls it an \"object initialiser\") the key must be one of:

  1. IdentifierName
  2. StringLiteral
  3. NumericLiteral

So you can\'t use an expression as the key in an initialiser. You can use an expression with square bracket notation to access a property. So to set the properties wtih an expression you have to do:

var required = { directories : {}};
required.directories[this.applicationPath] = \"Application \" + this.application + \" does not exists\";
required.directories[this.applicationPath + \"/configs\"] = \"Application config folder does not exists\";
...

and so on. Since this.applicationPath is reused a lot, better to store a reference to help with performance and cut down the amount of code:

var a = this.applicationPath;
var required = { directories : {}};
var rd = required.directories;
rd[a] = \"Application \" + this.application + \" does not exists\";
rd[a + \"/configs\"] = \"Application config folder does not exists\";
...

Edit

As of ECMAScript ed 6, object initializers can have computed keys using:

[expression]: value

There is also shorthand syntax for property and method names.

See MDN: Object Initializer or ECMAScript §12.2.6.



回答3:

Computed property names are supported in ECMAScript2015:

var name = \'key\';
var value = \'value\';
var o = {
  [name]: value
};
alert(\"o as json : \" + JSON.stringify(o));

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer



回答4:

Inspired by how babel coverts the new ES6 syntax ({[expression]: value}) to old Javascript, I learned that you can do it with a one liner:

var obj = (_obj = {}, _obj[expression] = value, _obj);

Example:

var dynamic_key = \"hello\";
var value = \"world\";
var obj = (_obj = {}, _obj[dynamic_key] = value, _obj);

console.log(obj);
// Object {hello: \"world\"}

(Tested on latest Chrome)



回答5:

For object literals, Javascript/ECMAScript script specifies keys be either a valid IdentifierName, a string literal, or a number credit RobG (even hex) . Not an expression, which is what required.applicationPath + \"/configs\" is.



回答6:

If you have a deep object structure (such as Grunt config), it\'s sometimes convenient to be able to return dynamically-generated object keys using the bracket notation outlined by Felix, but inline within the object structure. This can be achieved by using a function to dynamically return an object within the context of the deep object; in the case for the code in this question, something like this:

var required = {
    directories : function() {
        var o = {};
        o[this.applicationPath] = \"Application \" + this.application + \" does not exists\";
        o[this.applicationPath + \"/configs\"] = \"Application config folder does not exists\";
        o[this.applicationPath + \"/controllers\"] = \"Application controllers folder does not exists\";
        o[this.applicationPath + \"/public\"] = \"Application public folder does not exists\";
        o[this.applicationPath + \"/views\"] = \"Application views folder does not exists\";
        return o;
    }(),
    files : function() {
        var o = {};
        o[this.applicationPath + \"/init.js\"] = \"Application init.js file does not exists\";
        o[this.applicationPath + \"/controllers/index.js\"]  = \"Application index.js controller file does not exists\";
        o[this.applicationPath + \"/configs/application.js\"] =\"Application configs/application.js file does not exists\";
        o[this.applicationPath + \"/configs/server.js\"]     =\"Application configs/server.js file does not exists\";
        return o;
    }()
}

This fiddle validates this approach.



回答7:

An old question, and the answers were correct at the time, but times change. In case someone digs it up in a google search, new javascript versions (ES6) allow using expressions as keys for object literals, if they are surrounded in square brackets: var obj={[\"a\"+Math.PI]:42}



回答8:

the problem is from using \'this\' because it doesn\'t refer to anything smart*. create the static literal with the applicationPath in it.

var required={
    \"applicationPath\":\"someWhereOverTheRainboW\"
};

Then use

required.directories={};
required.directories[required.applicationPath + \"/configs\"]=\"Application config folder does not exists\";
....

to fill it dynamically

Edit; I rushed with my first idea, it didn\'t work. The above works now - sorry for that!

* the keyword \'this\' is very smart :) but it often refers to the window object or the element, the event has been fired on or the called \'active\' object. Thus, creating a lot of confusion ;)