使用简单的单词手动声明JavaScript对象

时间:2015-07-26 21:09:47

标签: javascript json bareword

我是JavaScript的初学者,正在尝试重构API,但我遇到了一个奇怪的问题。

我使用node解释器来测试我想在重新设计中加入的一些代码,但是我遇到了一些意想不到的行为。

考虑输入节点解释器的以下内容:

[In0] b

我期待一个ReferenceError,我得到如下所示。

[Out0] 
ReferenceError: b is not defined
at repl:1:1
at REPLServer.defaultEval (repl.js:132:27)
at bound (domain.js:254:14)
at REPLServer.runBound [as eval] (domain.js:267:12)
at REPLServer.<anonymous> (repl.js:279:12)
at REPLServer.emit (events.js:107:17)
at REPLServer.Interface._onLine (readline.js:214:10)
at REPLServer.Interface._line (readline.js:553:8)
at REPLServer.Interface._ttyWrite (readline.js:830:14)
at ReadStream.onkeypress (readline.js:109:10)

下一步

 [In1] 'b'
 [Out1] 'b' // this is OK because it's a string

现在考虑一个JSON风格的对象:

 [In2] var x = { 'b' : 'c' };
 [Out2] { b : 'c' }

奇怪的是,引号被放在x的键上。我很乐意忽视这一点,尽管这很令人困惑。

最后,

 [In3] var y = { b : 'c' };
 [Out3] { b : 'c' }

如果没有引号,解释器不会抱怨并产生与引号相同的结果。

我还注意到这个模式key 总是以字符串形式返回:

for(var key in obj) {
    key;
 }

注意到这种行为,我尝试在我的班级Bar中编写一个方法Foo,定义如下:

function Foo(a) {
    this.a = a;
    this.Bar = function(attr, val) {
        // attr doesn't need to be a string to be a key in object x
        var x = { attr : val };
        for(var key in x) {
            //key DOES need to be a string to be used like this
            this[key] = val;
        }
    }
}

基本上Bar只是将attr:val的键值对添加到Foo类型的对象中。

我尝试使用以下代码测试此代码:

[In4] var foo = new Foo('c');
[Out4] { a : 'c', Bar : [Function] } // this is OK. I expect this.

[In5] foo.Bar(hello, "World");
[Out5]
ReferenceError: hello is not defined
at repl:1:9
at REPLServer.defaultEval (repl.js:132:27)
at bound (domain.js:254:14)
at REPLServer.runBound [as eval] (domain.js:267:12)
at REPLServer.<anonymous> (repl.js:279:12)
at REPLServer.emit (events.js:107:17)
at REPLServer.Interface._onLine (readline.js:214:10)
at REPLServer.Interface._line (readline.js:553:8)
at REPLServer.Interface._ttyWrite (readline.js:830:14)
at ReadStream.onkeypress (readline.js:109:10)

任何人都可以向菜鸟解释这种行为吗?如果可以手动完成,则无法在方法中使用裸字作为密钥,这是非常令人沮丧的。

谢谢, erip

2 个答案:

答案 0 :(得分:3)

上下文在语言分析中非常重要。

在JavaScript中,属性名称始终是字符串或Symbol s(最后是ES6中的新内容)。在object initializer中,您可以指定不带引号的属性名称(使用文字名称,必须是IdentifierName),数字文字(转换为字符串)或字符串文字(例如,带引号):

var a = { b: 'c' };
// is the same as
var a = { 'b': 'c' };
// is the same as
var a = { "b": 'c' };

(在ES6中,您也可以在方括号中使用计算属性名称,但这在此处不相关,并且在任何情况下,它们在计算后最终都是Symbol或字符串。)< / em>的

因此,在对象初始值设定项中, context 告诉解析器它期望一个属性名称,对于该属性名称,允许使用文字或字符串表示法。但是在这里:

foo.Bar(hello, "World");

...上下文告诉它hello标识符,因为语法表示函数调用中的各个参数值​​(括号内容)是表达式,并且表达式中单个单词表示标识符。由于它是您尚未声明的标识符,因此您获得ReferenceError

  

奇怪的是,引号被放在x的键上。我愿意忽视这一点,虽然令人困惑

当属性名称是有效的 IdentifierName 时,这是显示对象属性的标准符号。

重新评论:

  
    

我是JavaScript的初学者,正在尝试重构API

  
     

足够公平。看起来没有比强制用户传递字符串更好的方法了。

是的,传递属性名称的正确方法是字符串,有时是数字(与数组索引一样)或Symbol,然后使用“括号”表示法访问属性:

var a = {foo: "bar"};
var name = "foo";
console.log(foo[name]); // "bar"

(当你使用数字执行该操作时,它会被强制转换为字符串。是的,当你使用JavaScript的标准数组使用数组索引时,理论上会发生这种情况,因为they aren't really arrays at all。)

这并不意味着应该要求您的API用户输入字符串文字。如果有一组允许的属性名称,则可以将它们作为对象的属性使用。

var API = {
    Things: {
        Foo: "Foo",
        Bar: "Bar"
    }
};

...然后使用API​​:

someApiMethod(API.Things.Foo);

您甚至可以将这些只读定义为防止意外覆盖:

var API = {Things: {}};
["Foo", "Bar"].forEach(function(name) {
    Object.defineProperty(API.Things, name, {
        enumerable: true, // Or not, whichever
        value: name
    });
});

默认情况下,使用defineProperty定义的属性是只读的。

在ES6中,它们也可以是常量

const Foo = "Foo";
const Bar = "Bar";

答案 1 :(得分:0)

hello预计会在此处表达,例如参考。

不是,因此失败。

它的语法糖允许形式为{ foo: 'bar'}。解释器知道你在一个对象声明中,假装它是一个字符串。

(JS没有符号。直到ES6,我忽略了。)