詳解JavaScript中Hash Map映射結(jié)構(gòu)的實現(xiàn)

字號:


    Hash Map通常在JavaScript中作為一個簡單的來存儲鍵值對的地方。然而,Object并不是一個真正的哈希映射,如果使用不當(dāng)可能會帶來潛在的問題。而且JavaScript可能不提供本地哈希映射(至少不是跨瀏覽器兼容的),有一個更好的聲明對象屬性的方法。
    Hash Map的簡單實現(xiàn):
    var hashMap = { 
      Set : function(key,value){this[key] = value}, 
      Get : function(key){return this[key]}, 
      Contains : function(key){return this.Get(key) == null?false:true}, 
      Remove : function(key){delete this[key]} 
    } 
    使用方法示例:
    hashMap.Set("name","John Smith"); 
    hashMap.Set("age",24); 
    hashMap.Get("name");//John Smith 
    hashMap.Contains("title");//false 
    hashMap.Contains("name");//true 
    hashMap.Remove("age"); 
    在Object聲明成員的問題
    該問題可能緣于對象原型鏈的繼承機制。就拿toString方法來說,如果使用in操作符來判斷對象是否存在的話:
    var map = {};
    'toString' in map; // true
    因為in操作符會從所有原型繼續(xù)對象查找該對象是否存在。要解決這個問題,可使用hasOwnProperty方法檢測該對象是否存在:
    var map = {};
    map.hasOwnProperty('toString'); // false
    這個方法可以工作地很正常,不過如果你定義了一個hasOwnProperty屬性那可能就麻煩了:
    var map = {};
    map.hasOwnProperty = 'foo';
    map.hasOwnProperty('hasOwnproperty'); // TypeError
    快速修復(fù)這個的方法是使用原生對象的方法。
    var map = {};
    map.hasOwnProperty = 'foo';
    {}.hasOwnProperty.call(map, 'hasOwnproperty'); // true
    這種方法不會引起任何問題,每次你判斷對象中的屬性是否存在時都要過濾掉原型鏈中的方法:
    var map = {};
    var has = {}.hasOwnProperty;
    for(var key in map){
     if(has.call(map, key)){
      // do something
     }
    }
    裸對象
    創(chuàng)建一個真正的Hash Map的訣竅是解藕所有的原型對象。我們可以通過 Object.create 來實現(xiàn)這個效果
    var obj = {};
    // is equivalent to:
    var obj = Object.create(Object.prototype);
    另外,這種方法可以讓你完全放棄原型,直接使用 null 來繼承。
    var map = Object.create(null);
    map instanceof Object; // false
    Object.prototype.isPrototypeOf(map); // false
    Object.getPrototypeOf(map); // null
    這些裸對象(或字典)是作為Hasp Map的理想選擇。因為不會有任何沖突,它會抵制任何類型轉(zhuǎn)換,比如這樣就會產(chǎn)生錯誤。
    var map = Object.create(null);
    map + ""; // TypeError: Cannot convert object to primitive value
    這里沒有任何保留字,它就是為Hash Map設(shè)計的,比如。
    var map = Object.create(null);
    'toString' in map; // false
    更進一步,for ... in 循環(huán)變得更加簡單了,我們只需要把循環(huán)寫成這樣。
    var map = Object.create(null);
    for(var key in map){
     // do something
    }
    除了這些區(qū)別,它使用起來跟一般的Object鍵值存儲沒有任何區(qū)別。該對象可以被序列化,可以聲明原型和被繼承,上下文變量的使用也是一樣的。
    var map = Object.create(null);
    Object.defineProperties(map, {
     'foo': {
      value: 1,
      enumerable: true
     },
     'bar': {
      value: 2,
      enumerable: false
     }
    });
    map.foo; // 1
    map['bar']; // 2
    JSON.stringify(map); // {"foo":1}
    {}.hasOwnProperty.call(map, 'foo'); // true
    {}.propertyIsEnumerable.call(map, 'bar'); // false
    甚至上面提到的那些變量檢測方法同樣適用。
    var map = Object.create(null);
    typeof map; // object
    {}.toString.call(map); // [object Object]
    {}.valueOf.call(map); // Object {}