Javascript 中对象文字的动态键

好的,我正在致力于 Nodes 中的一个项目,我遇到了一个关于对象文字中的键的小问题,我有以下设置:

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

你们中的很多人会看到这个并认为它看起来没问题,但是编译器一直告诉我缺少一个 :(冒号) ,其实不是,看起来好像是 +或者 .都在影响编译器。

现在我相信(不确定) ,对象文字是在编译时创建的,而不是在运行时创建的,这意味着动态变量(如 this.applicationPath和连接)将不可用: (: (

克服这种障碍的最好方法是什么,而不需要重写大量的代码。

53979 次浏览

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

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.

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
}

Prior to ECMAScript 2015 (ed 6), an object literal (ECMAScript calls it an "object initializer") key must be one of:

  1. IdentifierName
  2. StringLiteral
  3. NumericLiteral

So you couldn't use an expression as the key in an initialiser. This was changed as of ECMAScript 2015 (see below). You could use an expression with square bracket notation to access a property, so to set the properties with an expression you had 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 2015 (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 Object Initializer.

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.

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)

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}

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