惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The Last Watchdog
The Last Watchdog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
S
Secure Thoughts
MongoDB | Blog
MongoDB | Blog
博客园 - Franky
T
Tor Project blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Google DeepMind News
Google DeepMind News
L
LINUX DO - 最新话题
博客园_首页
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Vercel News
Vercel News
Last Week in AI
Last Week in AI
月光博客
月光博客
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
P
Proofpoint News Feed
博客园 - 叶小钗
NISL@THU
NISL@THU
C
Check Point Blog
K
Kaspersky official blog
N
News and Events Feed by Topic
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
A
Arctic Wolf
T
Threatpost
GbyAI
GbyAI
L
LINUX DO - 热门话题
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
P
Privacy & Cybersecurity Law Blog
N
News and Events Feed by Topic
Scott Helme
Scott Helme
P
Privacy International News Feed
The Register - Security
The Register - Security
G
GRAHAM CLULEY
Recorded Future
Recorded Future
Apple Machine Learning Research
Apple Machine Learning Research
C
Cybersecurity and Infrastructure Security Agency CISA
B
Blog
Project Zero
Project Zero
Cyberwarzone
Cyberwarzone
Webroot Blog
Webroot Blog
Microsoft Security Blog
Microsoft Security Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
D
DataBreaches.Net
J
Java Code Geeks
AWS News Blog
AWS News Blog
Help Net Security
Help Net Security
Engineering at Meta
Engineering at Meta
M
MIT News - Artificial intelligence
T
Threat Research - Cisco Blogs
Google DeepMind News
Google DeepMind News

博客园 - antony.net

移动版网页自动缩放问题 2011年国内五款值得关注网店系统 Enterprise Architect 7 入门教程大全 Enterprise Architect 业务过程建模 不同浏览器处理后退的一些异同 - antony.net - 博客园 23个.NET开源项目 mysql 存储过程 游标 js代码库 table css 固定表头 - antony.net 在非UI线程中改变UI控件属性的通用方法 RSClientPrint打印 统计sql语句 文档格式批量转换(doc,txt,pdf等) 了解HTTP Headers的方方面面 XmlSerializer 常见问题疑难解答 webservices 水晶报表 - antony.net - 博客园 Build a Custom RadioButton Cell and Column for the DataGridView Control .NET 2.0 - WinForm Control - DataGridView 编程36计(二)
Hash Tables in Javascript
antony.net · 2010-11-15 · via 博客园 - antony.net

转:http://www.mojavelinux.com/articles/javascript_hashes.html

Hash Tables in Javascript

Javascript is a prototype based object-oriented language where an objects is a mapping from property names to values. This tutorial demonstrates how to wrap these native objects with an hash table implementation similar to that found in the Java API.

Introduction

Hash tables are a permutation of associative arrays (i.e. name => value pairs). If you use PHP, then you are very familiar with this type of data structure already since all PHP arrays are associative.

The Javascript language implements very loose and somewhat limited support for associative arrays. Any JavaScript array can use other objects as keys, making it a hash, but there exists no formal constructor for initializing them and it is more or less unweildy to work with. A short example of a hash in JavaScript would be as follows:

var myArray = new Array();
myArray['one'] = 1;
myArray['two'] = 2;
myArray['three'] = 3;

// show the values stored
for (var i in myArray) {
	alert('key is: ' + i + ', value is: ' + myArray[i]);
}
		

Just as in PHP, the 'foreach' contruct is used to run through the array, doing something for each key => value pair. However, notice I did not do:

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

This approach would not work in Javascript because the length property of an associative array in JavaScript is not incremented for arrays with non-numeric keys and must otherwise be explicitly assigned. Part of the reason for this goes back to the missing constructor for created associative arrays.

Fundamentals

After a brief discussion of fundamentals we will begin to focus on the core problem. In JavaScript, every variable is in fact an object. Okay, so what does this mean? Well, essentially, no matter what the variable, it can be used as though it were an instance of an object. This means it has a constructor, methods and properties. A property is just a variable that is owned by the object and thus local to that object. A property is accessed using the syntax:

myArray.one
		

where one is the property and the '.' symbol signifies we are talking about the property of the array (or object) myArray. So the above example could be alternatively executed as:

for (var i in myArray) {
	alert('key is: ' + i + ', value is: ' + eval('myArray.' + i));
}
		

NOTE: In this example we have to use the function 'eval()' because we want to contruct the call myArray.one dynamically.

Since each object has default properties that are accessed using this very same syntax, such as length and constructor, consider the case where the key in the associative array is the same as one of these properties. This situation highlights the fundamental problem with associative arrays in JavaScript. It should be clear now why the length property is not set when we make an associative array data structure. By creating an associative array, you wipe out the original structure of the object because it is no longer possible differentiate between data keys and object properties. They become one in the same.

As a matter of fact, this is not the only conflict. Each object has a property called contructor, which is the function reference which is used to contruct the object. Ahhh, so now we see why hashes in JavaScript have no contructor. By creating an associative array, it is possible to wipe out the constructor in the process. In fact, hashes in JavaScript are somewhat useless for anything more than a very simple, static array. You have to know what it will be used for before you use it. Okay, so enough run on, what are we going to do about it?

Constructing a Hash Class

Javascript is very nice language in the sense that we can create our own classes. So what we are going to do is create a Hash() class just like the Array() class, except we are going to get around the conflicts that we are running into with this Array() class.

You might thinking, "okay, so we make a class, but how do we get around the conflicting properties problem?". Easy, we make a property which itself is an array and call it items. Then, we can use any key we want, and store the data about the array in other properties. The trick is to move the data part of the array inside of a property of the class. The following listing is the Hash() object definition:

function Hash()
{
	this.length = 0;
	this.items = new Array();
	for (var i = 0; i < arguments.length; i += 2) {
		if (typeof(arguments[i + 1]) != 'undefined') {
			this.items[arguments[i]] = arguments[i + 1];
			this.length++;
		}
	}
}
		

NOTE: You should select another name for this class if you are using the prototype JavaScript library to avoid a naming conflict.

Let's break this down a bit. Right off the bat, we create a length property, which will just be 0 to start with. Additionally, we create our items array using the Array() contructor. Next we populate that array with the key => value pairs we passed in and continue to increment the length. Ah, but JavaScript doesn't know anything about the special syntax that say PHP uses. So we have to invent our own. What we will do is just alternate key and value arguments to the contructor (similar to Perl). A typical call to create a Hash() object would use the following syntax:

var myHash = new Hash('one', 1, 'two', 2, 'three', 3);
		

Already it must be nice to see a contructor...so much easier to add data to the structure!

Now, as you may recall before, we couldn't have any properties or methods in our associative array, so besides a 'foreach' construct, there was not much we could do with our associative array. Now that we have the ability to add methods and properties, let's get started! So we enhance our Hash() class.

function Hash()
{
	this.length = 0;
	this.items = new Array();
	for (var i = 0; i < arguments.length; i += 2) {
		if (typeof(arguments[i + 1]) != 'undefined') {
			this.items[arguments[i]] = arguments[i + 1];
			this.length++;
		}
	}
   
	this.removeItem = function(in_key)
	{
		var tmp_previous;
		if (typeof(this.items[in_key]) != 'undefined') {
			this.length--;
			var tmp_previous = this.items[in_key];
			delete this.items[in_key];
		}
	   
		return tmp_previous;
	}

	this.getItem = function(in_key) {
		return this.items[in_key];
	}

	this.setItem = function(in_key, in_value)
	{
		var tmp_previous;
		if (typeof(in_value) != 'undefined') {
			if (typeof(this.items[in_key]) == 'undefined') {
				this.length++;
			}
			else {
				tmp_previous = this.items[in_key];
			}

			this.items[in_key] = in_value;
		}
	   
		return tmp_previous;
	}

	this.hasItem = function(in_key)
	{
		return typeof(this.items[in_key]) != 'undefined';
	}

	this.clear = function()
	{
		for (var i in this.items) {
			delete this.items[i];
		}

		this.length = 0;
	}
}
		

Understanding the Implementation

We now have lots of useful methods! In JavaScript, any variable can be a reference to a function, so to add methods to the class, the easiest way to do it is to just write the function and then assign it to a property in the class. Okay, so you may be thinking, "But I can't have the same property name as a method name." That's right, another limitation of JavaScript objects is that methods are properties. However, in most cases, it won't be a problem because method names should be 'behavior' names and properties should be 'state' names.

In order to access the underlying items, we added the methods 'setItem', 'removeItem' and 'hasItem', and a 'clear' method to flush out the data. For now we will refer to each key => value pair as an item. We could create a 'getItem' method as well, but that is a bit slower and it doesn't server any real useful purpose. If you would like, add it for your own class...either way works. Note that if you are using the prototype JavaScript library, the 'hasItem' method will return values for any of the methods that prototype adds to Array. You will also need to ensure the item found is not a function.

The most important role of our methods is to keep the length property up to date. As we can see, it takes a lot of work out of our job and we can use these nice methods to work easily with our hash. Just like a Hash in Java, the return value is a reference to the item in the Hash() that was replaced:

alert("Previous value: " + myHash.setItem('foobar', 'hey'));

If you now want to iterate through the Hash() like we did the array in the very beginning, you may do so using two different approaches:

for (var i in myHash.items) {
	alert('key is: ' + i + ', value is: ' + myHash.items[i]);
}
		

or

for (var i = 0; i < myHash.length; i++) {
	alert('key is: ' + i + ', value is: ' + myHash.items[i]);
}
		

and if you made a method for 'getItem()', you could also do:

for (var i = 0; i < myHash.length; i++) {
	alert('key is: ' + i + ', value is: ' + myHash.getItem(i));
}
		

If you are not concerned with speed down to the millisecond, then it is better to use the more formal approach of making a 'getItem()' method because it helps to prevent exposing the internal Array items.

Summary

Now you should go home and start using this in every JavaScript code you write because it finally makes Arrays in JavaScript useful. It is great for storing configuration data, returning multiple values from function, and the list goes on.

Now, I added a few more properties in some of my JavaScript programs to get more information out of the Hash() and so can you. In one application, I wanted to know how many integer keys the Hash() had rather than just the length of the whole Hash() and I wanted to be able to work with just the integer keys in one instance. You can do this by modifing the contrutor and methods to check for the type of the key using the function 'typeof()' and then setting the indexLength (as I called it) appropriately.