JavaScript----基于hash的面向对象



1.Hash表的结构

首先,允许我们花一点时间来简单介绍hash表。

1.什么是hash表

hash表是一种二维结构。先附上一副图片,我们再开始长篇大论。

如上图所示,左侧是一个一维顺序存储的数组,数组里存储的是指向另一个链式数组的指针。绿色部分即为一对对<Key,Value>,绿色部分右侧的白色部分是指向下一对键值对的指针。

hash表的工作原理:

(1).根据key和散列算法得到散列值,也就是对应的数组下标。

(2).根据数组下标得到此下标里存储的指针,若指针为空,则不存在这样的键值对,否则根据此指针得到此链式数组。(此链式数组里存放的均为一对对<Key,Value>)。

(3).遍历此链式数组,若找到与给定key相等的Key,即在此hash表中存在此<Key,Value>键值对,此后便可以对此键值对进行操作;若找不到,即为不存在此键值对。

所以hash表其实就是管理一对对<Key,Value>这样的结构。

2.不可避免的hash冲突

hash表管理着一组组的<key,Value>.对Key采取散列。散列技术基于散列算法,理想情况下是将相同的key散列为相同的值,不同的key散列为不同的值。但是实际情况下,因为存储空间有限,使得这种算法是不可能被实现的,所以当不同的key被散列为相同的值时,便产生了冲突。这就是我们所说的hash冲突。

下面我们来看看,理想情况下一维的hash表 :如下图:

                  (2)理想情况下的一维hash表

理想情况下的hash表存放的是一对对<key,Value>键值,

1.对hash散列算法的要求:不同的Key必须散列不同的值,相同的Key必须散列为相同的值。

2.对数组的要求,为了保持查找的高效性,必须保持为顺序存储的数组。

OK....那么问题来了,理想情况下的hash表能完美解决以下问题吗:

(1).为了高效,你是顺序存储的数组,那么你知道你每次需要开辟多大的存储给此数组吗?如果太大,势必会造成空间的浪费,而且此空间还必须是连续的,如果过小,需要不断调整。这样你还能维持高效吗?

(2).针对每个<Key,Value>你又准备多大的空间去存储此键值对呢?很遗憾的告诉你...你不知道。过大过小都会面对刚才我们提出的问题?

问题出现后,总会有那么几个天才冒出来?

3.hash表的完美实现(允许冲突的存在)

hash查找是一种高效的查找,在存储空间和查找时间的相互妥协下,有人提出来了一种新的想法,即允许将不同的key散列为相同的值。

具体实现如下:

1.先在空间中开辟一个数组。我们首先根据key和散列算法取得数组的下标,也就是上图部分的左侧数组。

2.数组中的每个单元格维持着一个链式数组,链式数组里存放<Key,Value>.

3.访问的时候,根据key一一比较,若找到,取得到value,若找不到,不同的语言返回着不同的值.

4.存储的时候,先查找此key是否存在,若存在则修改此Value值,若不存在,则在此链式数组的尾部加上一个一对<Key,Value>;

有以上我们可以看出,利用hash表存储对象,查询的时候速度是非常快的。左侧数组可以随机访问,右侧链式数组虽然需要遍历,但是如果散列算法够出色的话,其存储的键值对数量就会很小。查找的速度也足够快。理想情况下查找速度为o(1)。

2.JacaScript中对象的存储形式?

1.两种创建对象的区别

(1).用构造函数创建对象

1 var object=new Object();
2 object.x=1;
3 object.2=1; //出错,因为变量不能以数字开头..

以上的错误相信大家都知道,那么用字面量创建对象呢?

(2).用字面量创建对象

var object = {
  x: 1,
  2: 2
};//不会报错


(3)两种方式比较

两则相比,大家不觉得奇怪吗?------为什么第一种方式报错,第二种反而不会?

当然你也可以理解为JS引擎对语法错误的屏蔽.那么JS引擎干嘛要费那么大的劲去屏蔽违法的变量呢?答案就是:---事出必有因

假设JS允许通过下面这种方式赋值或者访问

object.2=2; //假设不会出错
2==2;console.log(2);//那么出现这种方式的时候,你让JS引擎怎么办?是把2当做全局变量理解还是数字2

当出现这样的情况,解释器也只能干瞪眼了.所以为了语法的统一,才对以变量的形式的访问对象的变量定义了各种要求,

也就是说,js引擎对于违法变量的屏蔽是合理的。

那么问题又来了----


//既然,对变量的形式规定了要求,那么又干嘛允许这种玩意存在呢?-----答案就是:因为它也是合理的/
var object = {
  2: 2
};

为什么上面这种方式合理呢?

  因为在hash表的<Key,Value>键值对中并没有对key提出这样或者那样的要求。所以,如果JS对象是基于Hash表存储变量的,既然在存储时这种形式合法,那么你又为什么不允许我访问      我存储的变量呢? 这不是自相矛盾吗!

当然,这里有一个逻辑性的问题,我凭什么认为JS对象的存储是基于Hash的呢?(这点在第三部分我会给出解释)

以上列举了两种定义对象变量方法以及区别,进而解释了为什么两种定义形式明明相互冲突却又同时存在!



下面开始我们的讨论: JS对象在hash表中Key到底是什么?------答案:字符串

首先来看一下他的输出结果

var object = {
  x: 1,
  2: 2
}
for (var property in object) {
  console.log(‘(‘ + typeof property + ‘)‘ + property + ‘:‘ + object[property]);
}
//结果  (string)2:2 
//     (string)x:1

以上的代码证明了两点: object[2]是真的存在的..并且<Key,Value>中的key是以字符串的形式存在。也就是说,对象的变量和值作为键值对存储在hash表中,key是以字符串的形式存在?那么value是以什么形式存在呢? 这点我们以后再讨论。也很值得讨论。

总结:

用字面量初始化的对象的时候,变量名可以使数字,也可以是字符串,但是不可以是对象,因为如果存在对象,则会造成语义二义性,虽然hash存储结构式允许的,但是在js语义分析时          是不合法的, 但是,以后用[]访问的时候,[]中的内容则可以是对象。

定义变量的两种形式都是合法的,但是在以.号访问时只能访问我们平时所说的合法变量,以[]访问时则比较自由,也没有那么多的要求。

如下所示:


var object={
    {x:1}:2   //这种形式是错误的,因为解释器不能正确识别这种语法,会报错。
}

//以下的方式则不会报错var  object={};object[{x:1}]=1;console.log(object[{x:1}]) ;//1   解释器会尽力量把[]中的内容解释成字符串。

知道看Key是字符创那么我们现在可以解释这个问题了:object[2]和object["2"]有区别吗?-----(当然关于这点也得看你怎么理解)!为了证明这点我们看两个例子

请思考:用[]访问对象变量的时候,解释器帮我们都干了什么?

var object = {
  x: 1,
  2: 2
}

console.log(object[2]);//2
console.log(object["2"]);//2

object[2]=3;
console.log(object["2"]);//3

之所以object[2]和object["2"]等效,那是因为解释器帮我们干了一点活,那么解释器帮我们干了什么呢?

解释器在访问object[2]的时候,先将方括号里面的2转换成字符串。然后再访问,为了证明这点,我写了一点代码证明这点。

var object = {
  x: 1,
  2: 2
}
Object.prototype.toString = function () {
  return ‘2‘;
}
console.log(object[{x: 1}]);  //2console.log(object["2"]);  //2console.log(object[2]);  //2

下面花一点时间来分析上述代码的执行过程:

1.首先定义了一个object对象,对象中与两个变量。

2.重写了Object原型中的toString()方法。

3.第7行输出时对于[]中我们用了一个临时的变量{x:1}。此变量被初始化后,在object[]执行时,先分析方括号中{x:1}是一个对象,此时解释器为了将对象转换为字符串,如果是引用类型会调用原型对象中的toString()函数,如果是基本数据类型是也会将基本数据类型转化为对应字符串,结果即是访问object["2"].输出的结果也就是2了。

所以我们也间接证明了JS对象中,所有的key都是字符串,即使你访问的时候不是字符串的形式,解释器也会尽力先将其转化为字符串。

所以下面两种方式是完全等效的:

1 var object1 = {
2   x: 1,
3   1: 2
4 } //第一种
5 var object2 = {
6   ‘x‘: 1,
7   ‘1‘: 2
8 }  //第二种

以上我们讨论的是 JS对象的存储形式,数据是怎么存放的。



下面我们讨论,为什么JS中对象是基于hash的。

3.JavaScript中的对象是基于Hash的。

(1).证明:

我们可以随时给一个对象增加或删除变量(如果此变量允许删除的话)

1 var object={};
2 object.x=1;//增加一个变量
3 delete object.x;//删除一个变量 success
4 console.log(Object.keys(object).length); //0

(1) 既然变量的对象类型和个数是可变的,我们也就不能像java,c++那样,先将一个对象分配固定的空间。JS的引用指向的对象所占用的空间必须支持随时调整。基于此,顺序存储的数组已经被淘汰。

(2)链式数组查询较慢的弊端已经先天决定其不可能作为对象中变量的存储结构。

(3)当然你可以说,我可以用树的存储结构,效率较高的可能就是平衡树了。平衡树在查询的时候时间复杂度为log(n),也不算太高,但是当删除属性的时候,平衡树在调整的时候代价相比          于hash表也是很大。或许,你还有其他的选择,但是我敢说,肯定没有任何一种有hash表存储数据那么方便和高效。

(4).只有JavaScript的对象是基于hash表存储的,那么所有的在c++,java,c#中存在的不合理才会变得合理。

其实说白一点,物理存储并非非hash表不可,只是没有比hash表更好的了,所以在JS语言设计的时候就是当做hash表结构进行设计的。

为了证明对象是基于hash表以键值对存储的,我们来简单看一下数组和函数:

函数:

var person=function y(){};

person.x=1;
person.y=2;

for(var property in person){
  console.log(property+" : "+ person[property])
}//x:1// y:2

数组:

var array=[1,2,4]
for(var property in array){
  console.log(property+" : "+ array[property])
}// 0: 1 // 1: 2 // 2:4

(2).理解了这些有什么用:

1.你还会对数组数可以拥有属性,函数也可以拥有属性奇怪吗? 因为他们本身管理着属于自己的hash表,所以他们随时都可以给自己添加或则删除一些属性。

2.我们知道数组中的下表是可以随意添加的,无论你设置为多大,你也可以越界访问(严格来说,根本就没有所谓的越界),只是返回的结果是undefined...因为没有找到对应的key。

你将其理解为下标,倒不如理解为Key,如此,JS数组还有那么神秘吗? 说白了,也就是一种hash结构。如果你想,你也可以把object当成数组,然后自定义一整套函数,只是可能没有那么方便。

注:当然,函数作为一种对象肯定有其的特殊性。在这里我们就不过多的讨论了。

4.JS对象是Hash表的典型应用

数组去重

 1 var array=[‘true‘,true,false,‘1‘,1,‘‘,‘sss‘," ",1,34,,,{x:1},{x:2}]
 2
 3 Array.prototype.unique=function(){
 4
 5  //利用对象的hash存储特性去重
 6   var object={},result=[];
 7
 8   for(var i=0,length=this.length;i<length;i++){
 9
10     var temp=this[i],key;
11
12     if((typeof temp)==‘object‘){
13           key=JSON.stringify(temp); //若为对象类型,将对象序列化为字符串
14     }else{
15           key=typeof temp+temp;
16     }
17
18     if(!object[key]){
19       object[key]=true;  //若object中已经存在此键值,则证明此元素在数组中已经存在
20       result.push(temp);
21
22     }
23
24   }
25
26   return result;
27
28 }
29
30 console.log(array.unique()); 

//["true", true, false, "1", 1, "", "sss", " ", 34, undefined, Object { x=1}, Object { x=2}]

//此算法的缺点,因为另外建立一个object对象和result数组,所以比较占用空间,但是速度非常快。这是对网上一些算法的改进,网络上有好多针对对象hash的算法并不能完美的去重。

  比如数组[1,"1",{x:1},{x:2}]。

文章中存在的疑点: Number,Boolean 等基本类型在转化为字符串的时候到底调用的是什么方法?(不是原型链中的toString()方法,关于这点未能叙述,欢迎补充);

时间: 2024-10-22 09:00:22

JavaScript----基于hash的面向对象的相关文章

深刻理解JavaScript基于原型的面向对象

主题一.原型 一.基于原型的语言的特点 1 只有对象,没有类;对象继承对象,而不是类继承类. 2  "原型对象"是基于原型语言的核心概念.原型对象是新对象的模板,它将自身的属性共享给新对象.一个对象不但可以享有自己创建时和运行时定义的属性,而且可以享有原型对象的属性. 3 除了语言原生的顶级对象,每一个对象都有自己的原型对象,所有对象构成一个树状的层级系统.root节点的顶层对象是一个语言原生的对象,其他所有对象都直接或间接继承它的属性. 显然,基于原型的语言比基于类的语言简单得多,我

javascript基于原型的面向对象的理论基础

面向对象,即按照人类的思维方式来编写程序,这是人类与生俱来的思维方式而不是新兴的一种方法. 1.对象(object):在内存中真实存在的: 2.对象即看待事物时就是一个一个物体构成而是物体就有属性和方法. 3.类:具有相同属性和方法的一组对象的抽象:(不是真实存在的)在设计时抽象出来的: ps:对象是类的实列,类是对象的抽象 ps:javascript是一个基于原形的面向对象的语言,即每个对象有一个原形,对象从原形种继承属性和方法.当访问对象的属性或调用对象的方法时,解析器首先检查对象是否有一个

javascript: 基于原型的面向对象编程

Douglas Crockford指出javascript是世界上最被误解的编程语言.由于javascript缺少常见的面向对象概念,许多程序猿认为javascript不是一个合适的语言.我在做第一个javascript项目时候也发现不能将代码放在一个类中.其实大部分程序猿不知道javascript可以面向对象. 浏览器大战时代,Netscape的执行官招来了一个叫Brendan Eich的聪明人,发明了livescript(就是现在的javascript)语言,用来运行在浏览器端.它不像c++

轻松学习JavaScript十三:JavaScript基于面向对象之继承(包含面向对象继承机制)

一面相对象继承机制 今天算是什么都没干,尽在了解面向对象三大特性之一的继承了,过去的学习的C++和C#都是正统的面向对象语 言,学习的时候也没有怎么深入了解过,只是简单的学习最基础的继承.下午在看继承机制的时候,看到一个很经典 的继承机制实例.这个实例使用UML很好的解释了继承机制. 说明继承机制最简单的方式是,利用一个经典的例子就是几何形状.实际上,几何形状只有两种,即椭圆形(是圆 形的)和多边形(具有一定数量的边).圆是椭圆的一种,它只有一个焦点.三角形.矩形和五边形都是多边形的一种, 具有

JavaScript初探系列之面向对象

面向对象的语言有一个标志,即拥有类的概念,抽象实例对象的公共属性与方法,基于类可以创建任意多个实例对象,一般具有封装.继承.多态的特性!但JS中对象与纯面向对象语言中的对象是不同的,ECMA标准定义JS中对象:无序属性的集合,其属性可以包含基本值.对象或者函数.可以简单理解为JS的对象是一组无序的值,其中的属性或方法都有一个名字,根据这个名字可以访问相映射的. 一   重新认识面向对象 为了说明 JavaScript 是一门彻底的面向对象的语言,首先有必要从面向对象的概念着手 , 探讨一下面向对

Java基于对象基础 基于对象和面向对象的区别(转)

Java基于对象基础 基于对象和面向对象的区别 JavaScript设计者想把javascript语言设计成基于对象(object-based)的语言,他想把这个与面向对象(object-oriented)语言区分开来.但是实际上,可以将基于对象看作是面向对象. 原型对象和类的区别 在JavaScript中没有类这一可以使用关键字,当然保留字不算.所以它有自己对类这种封装结构的实现,这和Java,c++还是有很大区别的.但是我们还是能将原型对象(prototype object)看做是类,它们的

R语言基于S4的面向对象编程

前言 本文接上一篇文章 R语言基于S3的面向对象编程,本文继续介绍R语言基于S4的面向对象编程. S4对象系统具有明显的结构化特征,更适合面向对象的程序设计.Bioconductor社区,以S4对象系统做为基础架构,只接受符合S4定义的R包. 目录 S4对象介绍 创建S4对象 访问对象的属性 S4的泛型函数 查看S4对象的函数 S4对象的使用 1 S4对象介绍 S4对象系统是一种标准的R语言面向对象实现方式,S4对象有明确的类定义,参数定义,参数检查,继承关系,实例化等的面向对象系统的特征. 2

JavaScript强化教程-JS面向对象编程

本文为H5EDU机构官方HTML5培训教程,主要介绍:JavaScript强化教程--JS面向对象编程  对事物的抽象描述  描述这类事物的特征和行为  对象是类的实例代码实现:创建一个类  function peple(){        this.hp=0;         this.act = 30;         this.name = "";         this.x=0;         this.y=0;         this.move =function(x,

JavaScript对象及初识面向对象

JavaScript对象及初识面向对象: