比较详细的了解javascript hashtable 的例子 由浅入深的详解 ,希望对各位有所帮助

在Javascript哈希表

Javascript是一种基于原型的面向对象语言。在JavaScript中,所有非标量对象表现为关联数组,即从属性键到值的映射。键和值可以是标量,对象或函数。本教程演示如何在这些本机对象周围提供哈希表封装,避免踩入JavaScript对象的内置属性,从而使其更加一致。

介绍

哈希表是一个的置换关联数组(即名称=>值对)。如果你使用PHP,那么你已经非常熟悉这种类型的数据结构,因为所有的PHP数组都是关联的。(这就是我变得熟悉他们)。

在JavaScript中,所有非标量对象表现为关联数组。对象可以使用其他对象作为键。但是,不跟踪关联数组的长度(如基于索引的数组),并且存在键可能与内置成员(例如添加到对象原型的那些)冲突的可能性以及自定义成员,例如长度属性或方便方法。我们将探索一种可以避免这些缺点的方法。

JavaScript中的关联数组(哈希表)的一个简单示例如下:

var h = new Object(); // or just {}
h ['one'] = 1;
h ['two'] = 2;
h ['three'] = 3;

//显示存储的值
for(var k in h){
    //使用hasOwnProperty过滤掉Object.prototype中的键
    if(h.hasOwnProperty(k)){
        alert('key is:'+ k +',value is:'+ h [k]);
    }}
}}
        

提示: 随意更换的console.log(),如果它是由您的浏览器(Chrome,火狐与萤火虫等)支持的警报()。

正如在PHP中,'foreach'构造用于运行数组,为每个key => value对做一些事情。但是如果我们想知道大小怎么办?

alert('hash table'size + h.length);
        

嗯。对length属性的引用给出了“未定义”的某种意想不到的值。这是因为在Javascript中,当添加新属性(键)时,对象的length属性不会增加(这意味着它的行为不像哈希表)。

基础

在简要讨论基本面之后,我们将开始关注核心问题。在JavaScript中,每个非标量变量都是一个对象。好吧,这是什么意思?基本上,这意味着它有一个构造函数,方法和属性。属性只是一个变量,由对象拥有,因此是该对象的本地变量。使用以下语法访问属性:

h.one
        

其中一个是财产和'。'。符号表示我们正在谈论对象obj的属性。它也可以以相同的方式分配:

h.one = 1;
        

上述示例可以替代地执行为:

for(var k in h){
    if(h.hasOwnProperty(k)){
        alert('key is:'+ k +',value is:'+ eval('h。'+ k));
    }}
}}
        

警告: 我们正在使用的eval()这里只是为了演示表明,直接财产引用(obj.one)产生相同的结果在括号标记(OBJ ['一'])。

还可以向类型为数组的对象添加属性,该对象将分配与数组的索引值和对象的成员混合。

var a = new Array(); // or just []
a [0] = 0
a ['one'] = 1;
a ['two'] = 2;
a ['three'] = 3;

for(var k in a){
    if(a.hasOwnProperty(k)){
        alert('key is:'+ k +',value is:'+ a [k]);
    }}
}}
alert(a.length);
        

奇怪的是,长度报告为1,但是打印了四个键。这是因为我们正在处理数组元素和底层对象。这只是JavaScript闪烁的灵活性(并使我们困惑)。方括号符号(即,[])被重载。当键是数字时,我们将元素分配给数组。否则,我们将向对象分配成员。

由于每个对象都有使用这种非常相同的语法(例如长度和构造函数)访问的默认属性,因此请考虑散列中的键与这些属性之一相同的情况。这种情况突出了JavaScript中关联数组的基本问题。现在应该清楚为什么当我们创建一个关联数组数据结构时,length属性没有设置。通过添加非整数键来处理对象作为关联数组,您将操作底层JavaScript对象,该对象不会被长度跟踪(长度仅适用于索引的键值)。

构造一个HashTable类

在Javascript中,我们可以创建自己的类。因此,我们要做的是创建一个可以维护关联数组的HashTable()类,但是从数据键中分离API方法(没有冲突)。

你可能会想,“好吧,所以我们做一个类,但是我们如何摆脱冲突的属性问题?很容易,我们做一个属性,它本身是一个关联数组,称为项目。然后,我们可以使用任何我们想要的键,并将数组的数据存储在其他属性中。诀窍是将关联数组的数据部分移到类的属性内部。以下列表是HashTable()对象定义:

function HashTable(obj)
{
    this.length = 0;
    this.items = {};
    for(var p in obj){
        if(obj.hasOwnProperty(p)){
            this.items [p] = obj [p];
            this.length ++;
        }}
    }}
}}
        

注: 如果您使用的是已经定义了这个功能的JavaScript库,您应该对此类选择另一名称。

让我们分解一下。右击蝙蝠,我们创建一个length属性,它将从0开始。此外,我们在构造函数中接受一个对象。接下来,我们使用传递给构造函数的对象的key => value对来填充内部项,并相应地增加长度。创建HashTable()对象的典型调用将使用以下语法:

var h = new HashTable({one:1,two:2,three:3,“i'm no 4”:4});
        

已经必须很高兴看到一个结构...这么容易添加数据到结构!

现在,正如你可能还记得,我们不能在关联数组中有任何属性或方法(因为它们可能与数据的键冲突),所以除了一个“foreach”结构之外,我们不能做我们的关联数组。现在他们是分开的,我们有能力添加方法和属性,让我们开始吧!

function HashTable(obj)
{
    this.length = 0;
    this.items = {};
    for(var p in obj){
        if(obj.hasOwnProperty(p)){
            this.items [p] = obj [p];
            this.length ++;
        }}
    }}

    this.setItem = function(key,value)
    {
        var previous = undefined;
        if(this.hasItem(key)){
            previous = this.items [key];
        }}
        else {
            this.length ++;
        }}
        this.items [key] = value;
        return previous;
    }}

    this.getItem = function(key){
        return this.hasItem(key)?this.items [key]:undefined;
    }}

    this.hasItem = function(key)
    {
        return this.items.hasOwnProperty(key);
    }}
   
    this.removeItem = function(key)
    {
        if(this.hasItem(key)){
            previous = this.items [key];
            this.length--;
            删除this.items [key];
            return previous;
        }}
        else {
            return undefined;
        }}
    }}

    this.keys = function()
    {
        var keys = [];
        for(var k in this.items){
            if(this.hasItem(k)){
                keys.push(k);
            }}
        }}
        返回键;
    }}

    this.values = function()
    {
        var values = [];
        for(var k in this.items){
            if(this.hasItem(k)){
                values.push(this.items [k]);
            }}
        }}
        返回值;
    }}

    this.each = function(fn){
        for(var k in this.items){
            if(this.hasItem(k)){
                fn(k,this.items [k]);
            }}
        }}
    }}

    this.clear = function()
    {
        this.items = {}
        this.length = 0;
    }}
}}
        

注: 另外,您也可以直接引用构造函数参数,OBJ,而不是将其复制到成员属性,物品。随你便。

让我们通过一些练习来放置HashTable()。

var h = new HashTable({one:1,two:2,three:3,“i'm no 4”:4});

alert('original length:'+ h.length);
alert('key of key“one”:'+ h.getItem('one'));
alert('has key“foo”?'+ h.hasItem('foo'));
alert('key'foo':'+ h.setItem('foo','bar')的上一个值);
alert('length after setItem:'+ h.length);
alert('key of key“foo”:'+ h.getItem('foo'));
alert('key of key“i'm no 4”:'+ h.getItem(“i'm no 4”));
h.clear();
alert('length after clear:'+ h.length);
        

这些调用应该产生以下输出:

  • 原长:4
  • 键“1”的值:1
  • 有键“foo”?假
  • 键的前一个值“foo”:未定义
  • setItem之后的长度:5
  • 键值“foo”:bar
  • 键“i'm no 4”的值:4
  • 清除后长度:0

让我们来看看这一切是如何工作的。

了解实现

我们现在有很多有用的方法!在JavaScript中,任何变量都可以是对函数的引用,所以要向类中添加方法,最简单的方法是只需编写函数,然后将其分配给类中的属性。好吧,所以你可能会想,“但我不能有相同的属性名称作为方法名称。没错,JavaScript对象的另一个限制是方法是属性。但是,在大多数情况下,它不会是一个问题,因为方法名称应该是'行为'名称,属性应该是'状态'名称。

为了访问底层项目,我们添加了方法'setItem','getItem','hasItem','keys','values'来设置和检索数据以及'remoteItem'和'clear'方法来刷新数据。现在我们将每个键=>值对作为一个项目。不是为了完整性添加'getItem'。你可以直接访问items属性直接通过键检索值(这将稍快)。'hasItem'方法使用'hasOwnProperty'方法来检查一个键是否属于items对象,而不是已经添加到Object原型(这是由诸如prototype.js这样的库完成的)的函数。

我们的方法的最重要的作用是保持length属性是最新的。我们可以看到,它需要大量的工作,我们的工作,我们可以使用这些好的方法,以轻松工作与我们的哈希。就像Java中的HashTable一样,返回值是对被替换的HashTable()中的项的引用:

alert(“Previous value:”+ h.setItem('foobar','hey'));
        

如果你现在想像HashTable()一样重复遍历对象,你可以使用几种不同的方法:

迭代项,过滤掉从Object.prototype继承的成员:

for(var k in h.items){
    if(h.hasItem(k)){
        alert('key is:'+ k +',value is:'+ h.items [k]);
    }}
}}
        

使用每个迭代条目:(注意,在这种情况下我们不必使用hasOwnProperty)

h.each(function(k,v){
    alert('key is:'+ k +',value is:'+ v);
});
        

迭代键集合:

for(var i = 0,keys = h.keys(),len = keys.length; i <len; i ++){
    alert('key is:'+ keys [i] +',value is:'+ h.getItem(keys [i]));
}}
        

迭代值的集合:

for(var i = 0,v = h.values(),len = v.length; i <len; i ++){
    alert('value is:'+ v [i]);
}}
        

你还可以找到哈希表的大小:

alert('hash table的大小:'+ h.length);
        

摘要

现在你应该回​​家,开始在你写的每个JavaScript代码中使用它,因为它最终使得JavaScript中的关联数组很有用。它非常适合存储配置数据,从函数返回多个值,列表继续。

虽然这篇文章仍然作为背景材料是有用的,我强烈建议采用更加成熟的JavaScript库,如jQuery的,它提供了更多的良好测试方便的API,包括jQuery.each() ,这是一个通用的迭代函数,可用于以无缝地迭代对象和数组。

归因

感谢彼得·贝利这个原来的文章中指出了错误,并建议如何改进它。我已经包括他的反馈下面逐字。我已经尽了最大努力将他推荐的更改整合到文章中。他是正确的,但是,这篇文章实际上只是在JavaScript中向关联数组添加一个length属性,并允许您添加不与键名称冲突的其他方法,简单和简单。

  • 你在第一个例子中误导了读者,从一个新的Array()开始,好像对下一步有什么意义。第一行也可以读取“var myArray = new Boolean();” 关于多少不相关的数组有例子。< - 我改变了使用Object()的例子。
  • 你得到的对象迭代都错了。for..in循环需要检查obj.hasOwnProperty()。有关摘要http://javascript.crockford.com/survey.html,请参阅这里的“对象”部分。< - 我已经包括使用hasOwnProperty()和解释为什么它是必要的。
  • 此外,eval()的用法是完全不合理的,因为javascript对象支持括号符号。myArray [i]会正常工作。我发现这是特别麻烦,因为你在页面上的注释建议读者,他们“有”使用eval(),可能是最危险的功能在javascript核心。更何况你在前面的例子中使用这个确切的语法!(myArray ['one'] = 1与myArray.one = 1相同)。< - 我添加了一个注意,这纯粹是为了演示的目的。
  • 不是所有的变量都是对象!有实际的标量类型。它只是觉得他们都是对象,因为它们基本上实现为泛型。< - 我澄清,我在谈论非标量对象。
  • 也许我最大的问题是,文章几乎不需要存在。JavaScript对象*是*哈希表!Hash类提供的唯一真正的实用程序是大小确定。其他(设置,获取,有,删除)是本机可用。

感谢彼得!


猜你喜欢

转载自blog.csdn.net/qq_34665877/article/details/54340626