Есть ли какая-либо функция хэш-кода в JavaScript? - PullRequest
133 голосов
/ 12 октября 2008

По сути, я пытаюсь создать объект из уникальных объектов, набор. У меня была блестящая идея просто использовать объект JavaScript с объектами для имен свойств. Например,

set[obj] = true;

Это работает, до определенного момента. Он отлично работает со строками и числами, но с другими объектами все они, похоже, "хэшируют" одно и то же значение и имеют доступ к одному и тому же свойству. Есть ли какой-то способ, которым я могу сгенерировать уникальное хеш-значение для объекта? Как строки и числа делают это, я могу переопределить то же самое поведение?

Ответы [ 18 ]

52 голосов
/ 10 ноября 2011

Если вам нужна такая функция hashCode (), как Java в JavaScript, она ваша:

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 (побитовый оператор).

32 голосов
/ 12 октября 2008

Объекты JavaScript могут использовать в качестве ключей только строки (все остальное преобразуется в строку).

В качестве альтернативы вы можете поддерживать массив, который индексирует рассматриваемые объекты, и использовать его индексную строку в качестве ссылки на объект. Примерно так:

var ObjectReference = [];
ObjectReference.push(obj);

set['ObjectReference.' + ObjectReference.indexOf(obj)] = true;

Очевидно, что это немного многословно, но вы могли бы написать пару методов, которые справятся с этим, получат и установят все волей и неволей.

Edit:

Ваше предположение является фактом - это определенное поведение в JavaScript - в частности, происходит преобразование toString, означающее, что вы можете определить свою собственную функцию toString для объекта, который будет использоваться в качестве имени свойства. - olliej

Это поднимает еще один интересный момент; Вы можете определить метод toString для объектов, которые хотите хэшировать, и это может сформировать их хеш-идентификатор.

31 голосов
/ 20 мая 2009

Самый простой способ сделать это - дать каждому из ваших объектов свой уникальный toString метод:

(function() {
    var id = 0;

    /*global MyObject */
    MyObject = function() {
        this.objectId = '<#MyObject:' + (id++) + '>';
        this.toString= function() {
            return this.objectId;
        };
    };
})();

У меня была та же проблема, и это решило ее идеально для меня с минимальными усилиями, и было намного проще, чем заново реализовать какой-то жирный стиль Java Hashtable и добавить equals() и hashCode() в ваши классы объектов. Просто убедитесь, что вы не вставляете строку '<#MyObject: 12> в ваш хеш, иначе она уничтожит запись для вашего выходящего объекта с этим идентификатором.

Теперь все мои хэши совершенно холодные. Я также только что опубликовал запись в блоге несколько дней назад о этой точной теме .

18 голосов
/ 10 ноября 2011

На то, что вы описали, распространяется 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

Слабые карты доступны в текущих версиях Firefox, Chrome и Edge. Они также поддерживаются в Node v7 и v6 с флагом --harmony-weak-maps.

18 голосов
/ 26 апреля 2011

Решение, которое я выбрал, похоже на решение Дэниела, но вместо того, чтобы использовать фабрику объектов и переопределить toString, я явно добавляю хэш к объекту, когда он сначала запрашивается через функцию getHashCode. Немного грязно, но лучше для моих нужд:)

Function.prototype.getHashCode = (function(id) {
    return function() {
        if (!this.hashCode) {
            this.hashCode = '<hash|#' + (id++) + '>';
        }
        return this.hashCode;
    }
}(0));
13 голосов
/ 11 марта 2014

Для моей конкретной ситуации меня интересует только равенство объекта в отношении ключей и примитивных значений. Решением, которое работало для меня, было преобразование объекта в его 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
// }
8 голосов
/ 08 апреля 2013

Я собрал небольшой модуль JavaScript некоторое время назад, чтобы создавать хеш-коды для строк, объектов, массивов и т. Д. (Я просто передал его GitHub :))

Использование:

Hashcode.value("stackoverflow")
// -2559914341
Hashcode.value({ 'site' : "stackoverflow" })
// -3579752159
8 голосов
/ 12 октября 2008

Спецификация JavaScript определяет доступ к индексированным свойствам как выполнение преобразования toString для имени индекса. Например,

myObject[myProperty] = ...;

совпадает с

myObject[myProperty.toString()] = ...;

Это необходимо как в JavaScript

myObject["someProperty"]

совпадает с

myObject.someProperty

И да, мне тоже грустно: - (

7 голосов
/ 13 января 2015

В ECMAScript 6 теперь есть Set, который работает так, как вы хотите: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set

Он уже доступен в последних версиях Chrome, FF и IE11.

4 голосов
/ 29 января 2017

Ссылка: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol

Вы можете использовать символ Es6 для создания уникального ключа и доступа к объекту. Каждое значение символа, возвращаемое из Symbol (), является уникальным. Значение символа может использоваться в качестве идентификатора для свойств объекта; это единственная цель типа данных.

var obj = {};

obj[Symbol('a')] = 'a';
obj[Symbol.for('b')] = 'b';
obj['c'] = 'c';
obj.d = 'd';
...