【发布时间】:2010-10-05 11:56:55
【问题描述】:
JavaScript 关联数组的键必须是字符串,还是可以是任何对象?
【问题讨论】:
标签: javascript arrays
JavaScript 关联数组的键必须是字符串,还是可以是任何对象?
【问题讨论】:
标签: javascript arrays
您是在谈论 JavaScript 对象 (JSON) 吗?
The specification 表示键应该是字符串。
但是 JavaScript 解释器允许{"key": "val"} 和{key: "val"}。
【讨论】:
JavaScript 中没有原生关联数组,只有对象。对象具有属性。属性的名称始终是字符串:即使是数组的数字索引 will be converted to strings 在“数组魔法”发生之前。
如果您正在寻找具有任意键的关联数组,look here。
【讨论】:
我已经实现了 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")
}
【讨论】:
在 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;
【讨论】:
这取决于您所说的“关联数组”是什么意思。 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。
【讨论】: