问题:声明 JSON/JavaScript Object/Python Dictionary 中的 key(键)可以用什么类型?

JSON、JavaScript Object、Python Dictionary(乱入)syntax 一览

问题:声明 JSON/JavaScript Object/Python Dictionary 中的 key(键)可以用什么类型?

带着这个问题,我们来看看这三种常用数据格式的词法。

JavaScript Object 词法

在 JavaScript 里,如果声明一个 Object,可以这么做:

var obj = {
    "hello": "hello",
    1: 1
}

甚至,可以这么做:

var obj = {
    name: "name",
    age: 1,
    null: null,
    undefined: undefined,
    false: false,
    true: true,
    break: break // keywords
}

也就是说,声明 JavaScript Object 时,key 可以用任何类型。能这么做,得益于 JavaScript 宽松的语法定义——

SourceCharacter :: 
    any Unicode character
...
ReservedWord :: 
    Keyword
    FutureReservedWord
    NullLiteral
    BooleanLiteral
...
Identifier ::
    IdentifierName but not ReservedWord
IdentifierName :: 
    IdentifierStart
    IdentifierName IdentifierPart
...
ObjectLiteral : 
    { }
    { PropertyNameAndValueList }
PropertyNameAndValueList : 
    PropertyName : AssignmentExpression
    PropertyNameAndValueList , PropertyName : AssignmentExpression
PropertyName : 
    Identifier
    StringLiteral
    NumericLiteral

此处参阅 ECMA-262 version 3 1999 年版 附录:语法总结。摘取了与 Object 声明有关的词法描述。Object 声明,由花括号 “{” 括起来,里边可以是 0 或多个 PropertyNameAndValueList。PropertyNameAndValueList 是由 PropertyName 和 AssignmentExpression 中间隔着冒号 “:” 组成。PropertyName 就是我们要找的 key,接着看,PropertyName 是由 Identifier、StringLiteral、NumericLiteral 三者构成。后两个分别是字符串、数字,在上述实例一种有所展示,属于常见做法。而 Identifier,则使得第二个示例得以实现。但仔细看,定义中可是声明了 “IdentifierName but not ReservedWord”,除了保留关键词之外的词。这怎么解释实例二中大量的关键词可以被用做 Key 呢?
原来,在 2009 年版的 ECMA-262 version 5 中,对 PropertyName 的约束做了修正——

PropertyName : 
    IdentifierName
    StringLiteral
    NumericLiteral 

由原来的 Identifier 改为了 IdentifierName,可以使用保留词做 key。
另外我们也可以看到另一个小的修正——

ObjectLiteral : 
    { }
    { PropertyNameAndValueList }
    { PropertyNameAndValueList , }

这个修正,也就从语法层面,使得多加一个逗号这种事情合法化。
当然,值得注意的是,虽然声明可以使用任何类型,但实际上,Object 里的 key,是 string 类型的,解释器帮着做了转化。

var a = {yield: ""}
for (var key in a){
    console.log(typeof(key))
}

> string

JSON (JavaScript Object Notation) 的诞生史

JSON 最早被 Douglas Crockford 用于一个 Cartoon Network 的项目中,那时是 1999 年前后。
JSON 最开始被设计成 JavaScript ( ECMA-262 第三版 1999 年 )的子集。在 2002 年,有了自己的站点 json.org。2005 年,雅虎开始提供 JSON 格式的网络服务,次年,Google 跟进。由此,JSON 成为业界网络通讯标准数据格式。
在被业界认可的同时,JSON 格式的标准规范也在不断地制定和更新。

JSON 词法

虽然 JSON 因为历史的原因被设计为 JavaScript 的子集,但其设计目标却是独立于语言的抽象层,这样才能被很好的用于不同系统之间的通讯中。

JSONText :
    JSONValue
JSONValue :
    JSONNullLiteral
    JSONBooleanLiteral
    JSONObject
    JSONArray
    JSONString
    JSONNumber
JSONObject :
    { }
    { JSONMemberList }
JSONMember :
    JSONString : JSONValue
JSONMemberList :
    JSONMember
    JSONMemberList , JSONMember
JSONArray :
    [ ]
    [ JSONElementList ]
JSONElementList :
    JSONValue
    JSONElementList , JSONValue

(JSON 词法, 完整版 JSON Syntax

JSON 的定义十分简单,我们从顶级元素入手, JSONObject,JSONObject 由一对花括号包着零或多个 JSONMemberList。JSONMemberList 由 JSONMember 构成,JSONMember 就是我们要考察的基础单元,由 JSONString 加冒号(:) 加 JSONValue 构成。其中,JSONString 是由一对引号包起来零或多个字节码构成,也即传统意义上的字符串。
也就是说,JSON 里面,键值对的键,只能是一个字符串。

Python Dictionary 词法

dic = {
    False: False,
    None: None,
    (1,2): (3,4,5),
    1: 1
}

for key in dic:
    print(type(key))

---
output:

<type 'bool'>
<type 'NoneType'>
<type 'tuple'>
<type 'int'>

虽然看起来奇怪,但,是的,Python 的字典类型,用作 key 的,可以是任何可以哈希化(hashable)的值。

A mapping object maps hashable values to arbitrary objects.
A dictionary’s keys are almost arbitrary values. Values that are not hashable, that is, values containing lists, dictionaries or other mutable types (that are compared by value rather than by object identity) may not be used as keys. Numeric types used for keys obey the normal rules for numeric comparison: if two numbers compare equal (such as 1 and 1.0) then they can be used interchangeably to index the same dictionary entry. (Note however, that since computers store floating-point numbers as approximations it is usually unwise to use them as dictionary keys.)

References