JavaScript 关联数组的键必须是字符串,还是可以是任何对象?
Posted
技术标签:
【中文标题】JavaScript 关联数组的键必须是字符串,还是可以是任何对象?【英文标题】:Do the keys of JavaScript associative arrays need to be strings, or can they be any object? 【发布时间】:2010-10-05 11:56:55 【问题描述】:【问题讨论】:
【参考方案1】:您是在谈论 javascript 对象 (JSON) 吗?
The specification 表示键应该是字符串。
但是 JavaScript 解释器允许"key": "val"
和key: "val"
。
【讨论】:
我想知道我是否可以有一个像 java 中的映射一样工作的结构,其中键可以是任何对象(甚至是另一个数组)。它不必在 json 中声明,它可以声明为:myarray[myObject] = value. key怎么可能是一个对象,它是一个标识符……也许应该是myarray[value] = myObject; @sktrdie 想象一下: [ 'some', 'object', 'as', 'key' ]: 'value' JSON 无效,但问题是它在JS.【参考方案2】:JavaScript 中没有原生关联数组,只有对象。对象具有属性。属性的名称始终是字符串:即使是数组的数字索引 will be converted to strings 在“数组魔法”发生之前。
如果您正在寻找具有任意键的关联数组,look here。
【讨论】:
还有……这到底是什么意思?属性名称通常比字符串有更严格的限制:它们必须是有效的标识符。这个限制是否代表关联数组键?【参考方案3】:我已经实现了 JavaScript HashMap 代码可以从http://github.com/lambder/HashMapJS/tree/master获取
键和值可以是任意的 JavaScript 对象。 对用作键或值的对象没有任何要求。
机制是微不足道的。对于每个键,都会生成一个唯一的 id(每个 HashMap 实例)。 该 id 以极不可能发生冲突的字段名称注入关键对象;)
该 id 然后用于键入底层烘焙标准 JavaScript 关联对象。
代码如下:
/*
=====================================================================
@license MIT
@author Lambder
@copyright 2009 Lambder.
@end
=====================================================================
*/
var HashMap = function()
this.initialize();
HashMap.prototype =
hashkey_prefix: "<#HashMapHashkeyPerfix>",
hashcode_field: "<#HashMapHashkeyPerfix>",
initialize: function()
this.backing_hash = ;
this.code = 0;
,
/*
Maps value to key, returning the previous association
*/
put: function(key, value)
var prev;
if (key && value)
var hashCode = key[this.hashcode_field];
if (hashCode)
prev = this.backing_hash[hashCode];
else
this.code += 1;
hashCode = this.hashkey_prefix + this.code;
key[this.hashcode_field] = hashCode;
this.backing_hash[hashCode] = value;
return prev;
,
/*
Returns value associated with given key
*/
get: function(key)
var value;
if (key)
var hashCode = key[this.hashcode_field];
if (hashCode)
value = this.backing_hash[hashCode];
return value;
,
/*
Deletes association by given key.
Returns true if the association existed, false otherwise
*/
del: function(key)
var success = false;
if (key)
var hashCode = key[this.hashcode_field];
if (hashCode)
var prev = this.backing_hash[hashCode];
this.backing_hash[hashCode] = undefined;
if(prev !== undefined)
success = true;
return success;
//// Usage
// Creation
var my_map = new HashMap();
// Insertion
var a_key = ;
var a_value = struct: "structA";
var b_key = ;
var b_value = struct: "structB";
var c_key = ;
var c_value = struct: "structC";
my_map.put(a_key, a_value);
my_map.put(b_key, b_value);
var prev_b = my_map.put(b_key, c_value);
// Retrieval
if(my_map.get(a_key) !== a_value)
throw("fail1")
if(my_map.get(b_key) !== c_value)
throw("fail2")
if(prev_b !== b_value)
throw("fail3")
// Deletion
var a_existed = my_map.del(a_key);
var c_existed = my_map.del(c_key);
var a2_existed = my_map.del(a_key);
if(a_existed !== true)
throw("fail4")
if(c_existed !== false)
throw("fail5")
if(a2_existed !== false)
throw("fail6")
【讨论】:
【参考方案4】:在 Lambder's idea 的基础上,我实现了一个小型 DataStructures 库。
我已经对其进行了一些测试,似乎一切正常。
它还会自动为每个 HashTable/HashSet 分配一个唯一的 id,用于唯一标识对象的关键属性。
var DataStructure = ;
DataStructure.init = function()
DataStructure.initHashables();
delete DataStructure.initHashables;
DataStructure.initHashables = function()
var objectHashableIndexer = new DataStructure.Indexer();
DataStructure.Hashable = function()
var self = this;
// Constant
//
//
const ERROR_KEY_DOES_NOT_EXIST = "Key doesn't exists in Hashable when trying to pop. Associated Key Object and Hashable logged to console.";
const HASH_MAP_KEY_PROPERTY_BASE = "DATA_STRUCTURE_HASH_MAP_KEY_PROPERTY_";
// Attributes
//
//
var tableNumber = objectHashableIndexer.getIndex();
var tableKeyProperty = HASH_MAP_KEY_PROPERTY_BASE + tableNumber.toString();
self.tableKeyProperty = tableKeyProperty;
var indexer = new DataStructure.Indexer();
var data = ;
self.data = data;
// Methods
//
//
self.getObjectKey = function()
return indexer.getIndex().toString();
self.putBackObjectKey = function(index)
indexer.putBackIndex(parseInt(index));
var getObjectKey = self.getObjectKey;
var putBackObjectKey = self.putBackObjectKey;
self.exists = function(key)
if (!(tableKeyProperty in key))
return false;
var realKey = key[tableKeyProperty];
if (!(realKey in data))
return false;
return true;
self.pop = function(key)
if (!self.exists(key))
console.log(key);
console.log(self);
throw ERROR_KEY_DOES_NOT_EXIST;
else
var realKey = key[tableKeyProperty];
delete key[tableKeyProperty];
delete data[realKey];
putBackObjectKey(realKey);
self.destroy = function()
objectHashableIndexer.putBackIndex(tableNumber);
delete self;
/*
Class DataStructure.ObjectHashMap
Purpose: Provides a way to hash arbitrary objects to values.
Prototype Arguments:
Attributes:
Methods:
Notes:
Should call inherited method destroy() when done with table to preserve indexes
*/
DataStructure.ObjectHashMap = function()
DataStructure.Hashable.call(this);
var self = this;
// Constant
//
//
const ERROR_KEY_EXISTS = "Key already exists in ObjectHashMap when trying to push. Associated Key Object and ObjectHashMap logged to console.";
const ERROR_KEY_DOES_NOT_EXIST = "Key doesn't exists in ObjectHashMap when trying to getValue. Associated Key Object and ObjectHashMap logged to console.";
// Attributes
//
//
var tableKeyProperty;
var data;
// Initialization
//
//
self.init = function()
self.privatize();
delete self.privatize;
self.privatize = function()
tableKeyProperty = self.tableKeyProperty;
delete self.tableKeyProperty;
getObjectKey = self.getObjectKey;
delete self.getObjectKey;
putBackObjectKey = self.putBackObjectKey;
delete self.putBackObjectKey;
data = self.data;
delete self.data;
// Methods
//
//
var getObjectKey;
var putBackObjectKey;
self.push = function(key, value)
if (self.exists(key))
console.log(key);
console.log(self);
throw ERROR_KEY_EXISTS;
else
var realKey = getObjectKey();
key[tableKeyProperty] = realKey;
data[realKey] = value;
self.getValue = function(key)
if(!self.exists(key))
console.log(key);
console.log(self);
throw ERROR_KEY_DOES_NOT_EXIST;
else
var realKey = key[tableKeyProperty];
return data[realKey];
self.init();
delete self.init;
/*
Class DataStructure.ObjectHashSet
Purpose: Provides a way to store arbitrary objects and check that they exist.
Prototype Arguments:
Attributes:
Methods:
Notes:
Should call inherited method destroy() when done with table to preserve indexes
*/
DataStructure.ObjectHashSet = function()
DataStructure.Hashable.call(this);
var self = this;
// Constant
//
//
const ERROR_KEY_EXISTS = "Key already exists in ObjectHashSet when trying to push. Associated Key Object and ObjectHashSet logged to console.";
const ERROR_KEY_DOES_NOT_EXIST = "Key doesn't exists in ObjectHashSet when trying to getValue. Associated Key Object and ObjectHashSet logged to console.";
// Attributes
//
//
var tableKeyProperty;
var data;
// Initialization
//
//
self.init = function()
self.privatize();
delete self.privatize;
self.privatize = function()
tableKeyProperty = self.tableKeyProperty;
delete self.tableKeyProperty;
getObjectKey = self.getObjectKey;
delete self.getObjectKey;
putBackObjectKey = self.putBackObjectKey;
delete self.putBackObjectKey;
data = self.data;
delete self.data;
// Methods
//
//
var getObjectKey;
var putBackObjectKey;
self.push = function(key)
if (self.exists(key))
console.log(key);
console.log(self);
throw ERROR_KEY_EXISTS;
else
var realKey = getObjectKey();
key[tableKeyProperty] = realKey;
data[realKey] = "";
self.init();
delete self.init;
DataStructure.Indexer = function()
var self = this;
// Constant
//
//
const DEFAULT_SIZE = 1000;
// Attributes
//
//
var nextIndex = 0;
var availableIndicies = 0;
var freeIndicies = [];
// Initialization
//
//
self.init = function()
freeIndicies.length = DEFAULT_SIZE;
// Methods
//
//
self.getIndex = function()
var index = 0;
if (availableIndicies === 0)
index = nextIndex;
++nextIndex;
else
--availableIndicies;
index = freeIndicies[availableIndicies];
return index;
self.putBackIndex = function(index)
if (availableIndicies === freeIndicies.length)
freeIndicies.push(index);
else
freeIndicies[availableIndicies] = index;
++availableIndicies;
self.init();
delete self.init;
DataStructure.init();
delete DataStructure.init;
【讨论】:
【参考方案5】:这取决于您所说的“关联数组”是什么意思。 JavaScript 中没有所谓的“关联数组”,有对象,有地图。
对象是可以使用[]
表示法访问的对象,例如foo["bar"]
,其中的键必须是字符串,正如Christoph's answer 解释的那样。
还有maps,可以有任何对象作为键。但是,要访问它们,您不能使用[]
作为对象,您必须使用get
和set
方法。以下是如何使用它们的示例:
let myMap = new Map(); // Create the map
myMap.set("key", "value"); // To set a value, use the set method.
// The first argument is the key, the second one is the value.
myMap.set(Math, "bar"); // You can really use any object as key
myMap.set(console.log, "hello"); // Including functions
myMap.set(document.body, "world"); // And even DOM elements
// To get the value associated to a key, use the get method
console.log(myMap.get(Math)); // "bar"
console.log(myMap.get(document.body)); // "world"
在这个示例中,我使用内置对象作为键,以避免在定义新对象作为键时混淆示例,但当然也可以使用您自己的对象作为键。
但是请注意,不要使用[]
访问地图的元素。执行myMap[whatever]
是有效代码,因此不会抛出错误,但不会按预期工作:
// Don't do this
myMap[Math] = 3;
myMap["[object Math]"] = 4;
console.log(myMap[Math]); //4
console.log(myMap.get(Math)); // 'undefined'
// Do this instead
myMap.set(Math, 3);
myMap.set("[object Math]", 4);
console.log(myMap.get(Math)); //3
要了解有关地图的更多信息,请参阅 Map。
【讨论】:
以上是关于JavaScript 关联数组的键必须是字符串,还是可以是任何对象?的主要内容,如果未能解决你的问题,请参考以下文章