【问题标题】:Do the keys of JavaScript associative arrays need to be strings, or can they be any object?JavaScript 关联数组的键必须是字符串,还是可以是任何对象?
【发布时间】:2010-10-05 11:56:55
【问题描述】:

JavaScript 关联数组的键必须是字符串,还是可以是任何对象?

【问题讨论】:

    标签: javascript arrays


    【解决方案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

        【讨论】:

          猜你喜欢
          • 2022-08-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-08-22
          • 2017-07-29
          • 2019-07-07
          • 1970-01-01
          • 2018-05-16
          相关资源
          最近更新 更多