JavaScript中是否有任何散列码函数?
基本上,我试图创build一个独特的对象,一套的对象。 我有一个聪明的想法,就是使用JavaScript对象和属性名称的对象。 如,
set[obj] = true;
这工作,直到一点。 它对string和数字非常有用,但是对于其他对象,它们似乎都“散列”到相同的值并访问相同的属性。 有没有什么方法可以为一个对象生成一个独特的散列值? string和数字如何做,我可以重写相同的行为?
JavaScript对象只能使用string作为键(其他任何东西都被转换成string)。
或者,您可以维护一个索引有问题的对象的数组,并使用索引string作为对象的引用。 像这样的东西:
var ObjectReference = []; ObjectReference.push(obj); set['ObjectReference.' + ObjectReference.indexOf(obj)] = true;
显然它有点冗长,但是你可以写一些处理它的方法,并且设置所有的方法。
编辑:
你的猜测是事实 – 这是在JavaScript中定义的行为 – 特别是toString转换意味着你可以定义你自己的toString函数作为属性名称的对象。 – olliej
这引出了另一个有趣的观点。 你可以在你想要散列的对象上定义一个toString方法,并且可以形成它们的散列标识符。
如果你想在JavaScript中使用Java的hashCode()函数,那就是你的:
String.prototype.hashCode = function(){ var hash = 0; for (var i = 0; i < this.length; i++) { var character = this.charCodeAt(i); hash = ((hash<<5)-hash)+character; hash = hash & hash; // Convert to 32bit integer } return hash; }
这是Java中的实现方式(按位运算符)。
最简单的方法是给每个对象自己的唯一的toString
方法:
(function() { var id = 0; /*global MyObject */ MyObject = function() { this.objectId = '<#MyObject:' + (id++) + '>'; this.toString= function() { return this.objectId; }; }; })();
我有同样的问题,这对我来说完全解决了这个问题,而且很容易重新实现一些脂肪Java风格的Hashtable
,并将equals()
和hashCode()
到对象类。 只要确保你不把一个string'<#MyObject:12>粘在你的散列中,或者它将会用你的id来清除你退出的对象的条目。
现在我所有的哈希都完全冷了。 前几天我刚刚也发布了一个关于这个确切主题的博客文章。
我select的解决scheme与Daniel的类似,但是不是使用对象工厂并重写toString,而是通过getHashCode函数首次请求对象时,显式地将哈希添加到对象。 有点凌乱,但更好的为我的需要:)
Function.prototype.getHashCode = (function(id) { return function() { if (!this.hashCode) { this.hashCode = '<hash|#' + (id++) + '>'; } return this.hashCode; } }(0));
Harmony WeakMaps是ECMAScript 6规范(JavaScript的下一个版本)的一部分。 也就是说:一个键集可以是任何东西(包括未定义的)并且是不可枚举的。
这意味着除非您直接引用链接到它的键(任何对象!),否则不可能获得对某个值的引用。 对于一系列与效率和垃圾收集有关的引擎实现原因来说,这一点非常重要,但是它也是非常酷的,因为它允许新的语义,如可更新的访问权限和传递数据而不暴露数据发送者。
来自MDN :
var wm1 = new WeakMap(), wm2 = new WeakMap(); var o1 = {}, o2 = function(){}, o3 = window; wm1.set(o1, 37); wm1.set(o2, "azerty"); wm2.set(o1, o2); // A value can be anything, including an object or a function. wm2.set(o3, undefined); wm2.set(wm1, wm2); // Keys and values can be any objects. Even WeakMaps! wm1.get(o2); // "azerty" wm2.get(o2); // Undefined, because there is no value for o2 on wm2. wm2.get(o3); // Undefined, because that is the set value. wm1.has(o2); // True wm2.has(o2); // False wm2.has(o3); // True (even if the value itself is 'undefined'). wm1.has(o1); // True wm1.delete(o1); wm1.has(o1); // False
WeakMaps在当前的Firefox,Chrome和Edge中可用。 它们也在Node v7中支持,在v6中也使用--harmony-weak-maps
标志。
我前一段时间编写了一个小的JavaScript模块来为string,对象,数组等生成哈希码(我只是将它提交给GitHub :))
用法:
Hashcode.value("stackoverflow") // -2559914341 Hashcode.value({ 'site' : "stackoverflow" }) // -3579752159
就我的具体情况而言,我只关心关键字和原始值相等的对象。 为我工作的解决scheme是将对象转换为其JSON表示并将其用作哈希。 密钥定义的顺序可能有不一致的局限; 但就像我说的那样对我有效,因为这些对象都是在一个地方生成的。
var hashtable = {}; var myObject = {a:0,b:1,c:2}; var hash = JSON.stringify(myObject); // '{"a":0,"b":1,"c":2}' hashtable[hash] = myObject; // { // '{"a":0,"b":1,"c":2}': myObject // }
JavaScript规范将索引属性访问定义为对索引名执行toString转换。 例如,
myObject[myProperty] = ...;
是相同的
myObject[myProperty.toString()] = ...;
这在JavaScript中是必需的
myObject["someProperty"]
是相同的
myObject.someProperty
是的,这让我很难过:-(
在ECMAScript 6中,现在有一个你想要的方法: https : //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
它已经在最新的Chrome,FF和IE11中可用。
参考: https : //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol
您可以使用Es6符号来创build唯一的密钥和访问对象。 从Symbol()返回的每个符号值都是唯一的。 符号值可以被用作对象属性的标识符; 这是数据types的唯一目的。
var obj = {}; obj[Symbol('a')] = 'a'; obj[Symbol.for('b')] = 'b'; obj['c'] = 'c'; obj.d = 'd';
这是我简单的解决scheme,返回一个唯一的整数。
function hashcode(obj) { var hc = 0; var chars = JSON.stringify(obj).replace(/\{|\"|\}|\:|,/g, ''); var len = chars.length; for (var i = 0; i < len; i++) { // Bump 7 to larger prime number to increase uniqueness hc += (chars.charCodeAt(i) * 7); } return hc; }
我的解决scheme为全局Object
对象引入了一个静态函数。
(function() { var lastStorageId = 0; this.Object.hash = function(object) { var hash = object.__id; if (!hash) hash = object.__id = lastStorageId++; return '#' + hash; }; }());
我认为在JavaScript中使用其他对象操作函数更方便。
如果你真的想要设置行为(我要通过Java知识),那么你将很难find一个在JavaScript中的解决scheme。 大多数开发人员会推荐一个唯一的键来表示每个对象,但这不同于set,因为您可以获得两个相同的对象,每个对象都有唯一的键。 Java API通过比较哈希码值而不是密钥来检查重复值,并且由于在JavaScript中没有对象的哈希码值表示,所以几乎不可能做到这一点。 即使Prototype JS库也承认这个缺点,当它说:
“哈希可以被认为是一个关联数组,绑定唯一键值(这不一定是唯一的)…”
除了无眼睑的回答,这里是一个函数,返回一个可重复的,唯一的ID为任何对象:
var uniqueIdList = []; function getConstantUniqueIdFor(element) { // HACK, using a list results in O(n), but how do we hash eg a DOM node? if (uniqueIdList.indexOf(element) < 0) { uniqueIdList.push(element); } return uniqueIdList.indexOf(element); }
正如你所看到的,它使用了一个非常低效的查找列表,但这是目前我能find的最好的列表。
如果你想使用对象作为键,你需要覆盖它们的toString方法,就像这里已经提到的一样。 所使用的散列函数都很好,但是它们只能用于相同的对象,而不能用于相同的对象。
我已经写了一个小型库,可以从对象中创build哈希值,您可以轻松地使用它来实现此目的。 对象甚至可以有不同的顺序,哈希将是相同的。 在内部你可以使用不同types的散列(djb2,md5,sha1,sha256,sha512,ripemd160)。
以下是文档中的一个小例子:
var hash = require('es-hash'); // Save data in an object with an object as a key Object.prototype.toString = function () { return '[object Object #'+hash(this)+']'; } var foo = {}; foo[{bar: 'foo'}] = 'foo'; /* * Output: * foo * undefined */ console.log(foo[{bar: 'foo'}]); console.log(foo[{}]);
该软件包可以在浏览器和Node-J中使用。
存储库: https : //bitbucket.org/tehrengruber/es-js-hash
如果你想在查找对象中有唯一的值,你可以这样做:
创build一个查找对象
var lookup = {};
设置哈希码function
function getHashCode(obj) { var hashCode = ''; if (typeof obj !== 'object') return hashCode + obj; for (var prop in obj) // No hasOwnProperty needed hashCode += prop + getHashCode(obj[prop]); // Add key + value to the result string return hashCode; }
目的
var key = getHashCode({ 1: 3, 3: 7 }); // key = '1337' lookup[key] = true;
排列
var key = getHashCode([1, 3, 3, 7]); // key = '01132337' lookup[key] = true;
其他types
var key = getHashCode('StackOverflow'); // key = 'StackOverflow' lookup[key] = true;
最后结果
{ 1337: true, 01132337: true, StackOverflow: true }
请注意,当对象或数组为空时, getHashCode
不会返回任何值
getHashCode([{},{},{}]); // '012' getHashCode([[],[],[]]); // '012'
这与@ijmacd解决scheme类似,只有getHashCode
不具有JSON
依赖性。