JavaScript对象字面量的动态键

119

好的,我正在使用Node.js开发一个项目,但在对象字面量中的键方面遇到了一些小问题。我有以下设置:

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之类的动态变量和连接将不可用:( :(

如何在不必重新编写大块代码的情况下克服这种障碍最好的方法是什么。


在这个背景下,您可能会对我有点相关的http://stackoverflow.com/questions/17841915/angularjs-ng-model-form-driven-by-ng-repeat-over-ui-model-description-data-how-t/17844354#17844354感兴趣。 - vorburger
现在可能是重复的问题吗?https://dev59.com/OWIj5IYBdhLWcg3w04bd#19837961 - OneHoopyFrood
8个回答

221

26
随着时间的推移,这已成为最正确的答案! - Gershom Maes
3
当然!@RobertPitt,请将接受的答案更改为这个? - orion elenzil
哇,这对我来说是一个新事物。太棒了,“计算属性名”,感谢这个概念。 - yuceel
1
这是一个更正确的答案,因为它比被接受的答案更好地回答了所提出的问题:被接受的答案处理绑定对象,而不是对象字面量/“内联对象”。 - Daniel Brady
我记得几年前看到这个时觉得它是不必要的。但实际上,对于使用TypeScript的人来说非常有用,因为它隐式地在对象创建时推断类型,并节省了很多丑陋、显式的样板代码。 - Preom

111

在 ECMAScript 2015(ed6)之前,对象字面量(ECMAScript 称其为“对象初始化器”)的键必须是以下之一:

  1. IdentifierName
  2. StringLiteral
  3. NumericLiteral

因此,在初始化器中无法使用表达式作为键。这已经从 ECMAScript 2015 开始发生了改变(请参见下文)。您可以使用方括号表示法和表达式访问属性,因此要使用表达式设置属性,您必须这样做:

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

等等, 因为this.applicationPath被多次重复使用,最好存储一个引用以提高性能并减少代码量:

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";
...

编辑

从ECMAScript 2015(第6版)开始,对象初始化器可以使用计算键:

[expression]: value

还有属性和方法名称的速记语法。

请参见 MDN:对象初始值设定项ECMAScript 对象初始化器


9
因为你引用了标准并恰当地列出了可用作索引的关键内容,所以我点赞了。 - Ciro Santilli OurBigBook.com
ECMA 变成 USCMA 了吗? - Lee Goddard
"initializer"不是以美国为中心的术语;请参见https://www.quora.com/What-is-the-difference-between-realize-and-realise-Can-it-be-used-interchangeably - Antonis Christofides
@AntonisChristofides—这是美国英语变体采用的拼写方式。 - RobG
@AntonisChristofides——已删除段落,反正它是多余的。;-) - RobG
显示剩余4条评论

46
你可以使用方括号表示法设置动态键:
required.directories[this.applicationPath + "/configs"] = "Application config folder does not exists";

当然,无论你在哪个定义中使用这个方法,都必须存在this.applicationPath

但是你需要在键中使用this.applicationPath吗?如何访问这些值?也许你可以从用于访问属性的任何值中删除this.applicationPath


但是如果你需要它:

如果你想避免重复大量代码,你可以使用数组来初始化键:

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
}

我并不需要它们出现在键值中,不过这只是个人喜好,我会改变它的位置。你的方法似乎是最合理的。 - RobertPitt
谢谢Felix,我采用了使用"/config"作为键的方法,并在for循环中进行了连接,我没有必要使用索引中的变量,但现在时间有点晚了,再次感谢你。 - RobertPitt
2
我认为这个答案需要更新,以提及计算属性名 - JLRishe
虽然冗长,但您可以使用:{ ...Object.defineProperty({}, key, { value, enumerable: true }) },其中key和value是通过定义具有该键和值的对象在文字对象符号中展开的变量。如果支持,{ [key]: value }则更为简洁。 - bucabay

8

受到babel将新的ES6语法({[expression]: value})转换为旧Javascript的启发,我学会了可以用一行代码来完成它:

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

例子:

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

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

(在最新版Chrome上测试)


3
如果你有一个深层次的对象结构(比如Grunt配置),有时候可以使用Felix所述的方括号符号来动态生成对象键,但是在对象结构内部进行内联操作会更加方便。这可以通过使用函数在深层对象上下文中动态返回对象来实现;对于本问题中的代码,可以像这样实现:
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;
    }()
}

这个示例验证了这种方法。


3

这是一个老问题,之前的回答在当时是正确的,但是时代在变化。如果有人在谷歌搜索中找到了它,新的JavaScript版本(ES6)允许使用表达式作为对象字面量的键,只要它们被放在方括号中:

var obj={["a"+Math.PI]:42}

2
对于对象字面量,Javascript/ECMAScript脚本规定键必须是有效的IdentifierName、字符串文字或数字(甚至十六进制) RobG原话。而不是一个表达式,这就是required.applicationPath + "/configs"的情况。

this.applicationPath 不算作有效的标识符吗? - RobertPitt
同样是一个表达式,因为 this 的值直到运行时才能确定。 - MooGoo
RobertPitt - 不是,这是一个表达式。 - RobG
请注意,键可以是IdentifierName,这与标识符不同。如果使用与标识符相同名称的键(例如变量名),它将创建一个具有该名称的属性,而不是解析标识符并创建具有标识符值的属性(否则它将被视为表达式,这是不可能的)。 - RobG

0

问题出在使用'this',因为它并没有指向任何有意义的东西。 创建带有applicationPath的静态文字。

var required={
    "applicationPath":"彩虹之外的某个地方"
};

然后使用:

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

来动态填充它。

编辑; 我匆忙提出了我的第一个想法,但它没有起作用。上面的代码现在可以工作了 - 对此感到抱歉!

*关键词'this'非常聪明 :),但它经常指向窗口对象,或事件被触发的元素,或所调用的“活动”对象。因此,会造成很多混乱 ;)


你能提供一个你所说的结构的例子吗?我刚刚尝试的方法仍然会出错。 - RobertPitt
添加了你想要的缺失行。 - japrescott
谢谢更新,我猜你的意思是这样,但是当涉及到键时,我仍然遇到错误,抛出的错误是SyntaxError: Unexpected token . - RobertPitt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接