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,可以有任何对象作为键。但是,要访问它们,您不能使用[] 作为对象,您必须使用getset 方法。以下是如何使用它们的示例:

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 关联数组的键必须是字符串,还是可以是任何对象?的主要内容,如果未能解决你的问题,请参考以下文章

如何通过值获取 JavaScript 对象中的键?

Javascript 语句

使用整数作为关联数组的键在 iOS safari 上不起作用

如何在 Bash 中间接获取关联数组的键和值?

更新另一列时,使用列名作为 PL/SQL 关联数组的键

JavaScript--数组--关联(hash)数组