Есть ли какая-либо функция хэш-кода в JavaScript?

150

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

set[obj] = true;

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

Буг
источник
32
Причина, по которой все объекты хешируются с одним и тем же значением, заключается в том, что вы не переопределили их методы toString. Поскольку ключи должны быть строками, метод toString автоматически вызывается для получения действительного ключа, поэтому все ваши объекты преобразуются в одну и ту же строку по умолчанию: «[объект объекта]».
alanning
4
JSON.stringify(obj)или obj.toSource()может работать для вас в зависимости от проблемы и целевой платформы.
AnnanFay
4
@Annan JSON.stringify (obj) буквально просто преобразует (весь) объект в строку. Таким образом, вы будете просто копировать объект на себя. Это бессмысленно, трата пространства и не оптимально.
Metalstorm
1
@Metalstorm Верно, поэтому все зависит от вашей проблемы. Когда я нашел этот вопрос через Google, моим окончательным решением был вызов toSource () для объектов. Другим способом было бы просто использовать обычный хеш для источника.
AnnanFay
@Annan, toSourceне работай в Chrome, кстати
Pacerier

Ответы:

35

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

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

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

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

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

Редактировать:

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

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

eyelidlessness
источник
другой вариант - дать каждому объекту случайное значение в виде его хеша - может быть, случайное число + общее количество тиков - и затем иметь набор функций для добавления / удаления объекта из массива.
Sugendran
4
Это не удастся, если вы добавите один и тот же объект дважды. Будет думать, что это другое.
Даниэль Х Мур
«Это не удастся, если вы добавите один и тот же объект дважды. Он будет думать, что он другой». Хорошая точка зрения. Решением может быть создание подкласса Array для ObjectReference с подключением повторной проверки к push (). У меня нет времени, чтобы отредактировать это решение сейчас, но я надеюсь, что вспомню позже.
век
8
Мне нравится это решение, потому что оно не нуждается в дополнительных свойствах объекта. Но это становится проблематичным, если вы пытаетесь иметь чистый сборщик мусора. В вашем подходе он сохранит объект, хотя другие ссылки уже были удалены. Это может привести к проблемам в больших приложениях.
Джонни
35
Какой смысл хэшировать объекты, если каждый раз, когда вы обращаетесь к ним, вам требуется линейное сканирование массива?
Бордайгорл,
57

Если вам нужна функция 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 (побитовый оператор).

Обратите внимание, что hashCode может быть положительным и отрицательным, и это нормально, см. HashCode с отрицательными значениями . Итак, вы можете рассмотреть возможность использования Math.abs()вместе с этой функцией.

KimKha
источник
5
это создает -hash, не идеально
qodeninja
2
@KimKha char- зарезервированное слово в JS и может вызвать некоторые проблемы. Какое-то другое имя было бы лучше.
szeryf
16
@qodeninja говорит кто? Я впервые услышал такое заявление. Можете ли вы дать ссылку на какой-нибудь источник? Хэши обычно рассчитываются с использованием целочисленной арифметики фиксированного размера и битовых операций, поэтому ожидать положительных или отрицательных результатов можно только в этом случае.
szeryf
7
Придирчиво, но ... "если (this.length == 0) вернуть хеш;" избыточно :) И лично поменял бы «символ» на «код».
Metalstorm
10
@qodeninja и @szeryf: вы просто должны быть осторожны, используя его. Например, я попытался сделать pickOne["helloo".hashCode() % 20]массив pickOneс 20 элементами. Я получил, undefinedпотому что хеш-код отрицательный, так что это пример, в котором кто-то (я) неявно предполагал положительные хеш-коды.
Джим Пиварски
31

Самый простой способ сделать это - дать каждому из ваших объектов свой уникальный 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> в ваш хеш, иначе это сотрет запись для вашего выходящего объекта с этим идентификатором.

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

Даниэль Х Мур
источник
28
Но это пропускает весь смысл. У Java есть equals()и hashCode()так, чтобы два эквивалентных объекта имели одинаковое хеш-значение. Использование описанного выше метода означает, что каждый экземпляр MyObjectбудет иметь уникальную строку, что означает, что вам нужно будет сохранить ссылку на этот объект, чтобы когда-либо получить правильное значение с карты. Наличие ключа не имеет смысла, потому что оно не имеет ничего общего с уникальностью объекта. Полезная toString()функция должна быть реализована для конкретного типа объекта, который вы используете в качестве ключа.
Sethro
@sethro вы можете реализовать toStringдля объектов так, чтобы он напрямую отображал отношение эквивалентности, чтобы два объекта создавали одинаковую строку, если они считаются «равными».
Даниэль Икс Мур
3
Правильно, и это единственный правильный способ использования, toString()чтобы позволить вам использовать Objectкак Set. Я думаю, что я неправильно понял ваш ответ как попытку предоставить общее решение, чтобы избежать написания toString()эквивалента equals()или hashCode()в каждом конкретном случае.
Sethro
3
Dowvoted. Это не хеш-код, смотрите мои ответы на: stackoverflow.com/a/14953738/524126 И настоящая реализация
хеш-кода
5
@Metalstorm вопрос не в «истинном» хеш-коде, а в том, как успешно использовать объект в качестве набора в JavaScript.
Даниэль Икс Мур
20

То, что вы описали, покрыто 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флагом.

slezica
источник
1
В чем разница между этим и Map?
smac89
@ smac89 WeakMap имеет ограничения: 1) Принимает только объекты в качестве ключей 2) Нет свойства размера 3) Нет итератора или метода forEach 4) Нет четкого метода. Ключ - это объект, поэтому, когда объект будет удален из памяти, данные из WeakMap, связанные с этим объектом, также будут удалены. Это очень полезно, когда мы хотим сохранить информацию, которая должна существовать только тогда, когда объект существует. Так что WeakMap имеет только методы: установить, удалить для записи и получить, имеет для чтения
Екатерина Токарева
Это не совсем правильно работает ... var m = new Map();m.set({},"abc"); console.log(m.get({}) //=>undefinedЭто работает, только если у вас есть та же переменная, на которую вы изначально ссылались в команде set. EGvar m = new Map();a={};m.set(a,"abc"); console.log(m.get(a) //=>undefined
Sancarn
1
@Sancarn Это не обязательно должна быть одна и та же переменная, но они должны указывать на один и тот же объект. В первом примере у вас есть два разных объекта, они выглядят одинаково, но у них другой адрес.
Svish
1
@ Свист хорошее место! Хотя я знаю это сейчас, я, возможно, не сделал бы тогда :)
Sancarn
19

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

Function.prototype.getHashCode = (function(id) {
    return function() {
        if (!this.hashCode) {
            this.hashCode = '<hash|#' + (id++) + '>';
        }
        return this.hashCode;
    }
}(0));
theGecko
источник
7
Если вы хотите идти по этому пути, это гораздо лучше , чтобы установить с помощью хэш - код Object.definePropertyс enumerableустановленным на false, так что вы не врезаться никаких for .. inпетель.
Себастьян Новак
14

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

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

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

Hashcode.value("stackoverflow")
// -2559914341
Hashcode.value({ 'site' : "stackoverflow" })
// -3579752159
MetalStorm
источник
GC javascript не задыхается от циклических ссылок тоже?
Клейтон Рабенда
@ Райан Лонг: Я бы даже сказал, что если у вас есть циклические ссылки, вам нужно реорганизовать код;)
Metalstorm
11
@Metalstorm "тогда вам нужно реорганизовать свой код" Вы шутите? Каждый родительский и дочерний пары DOM-элемента составляют циклическую ссылку.
Крис Миддлтон
8
Он плохо справляется с хэшированием объектов, которые имеют числовые свойства, во многих случаях возвращая одно и то же значение, т.е. var hash1 = Hashcode.value({ a: 1, b: 2 }); var hash2 = Hashcode.value({ a: 2, b: 1 }); console.log(hash1, hash2);записывают в журнал2867874173 2867874173
Julien Bérubé
9

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

myObject[myProperty] = ...;

такой же как

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

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

myObject["someProperty"]

такой же как

myObject.someProperty

И да, это меня тоже огорчает :-(

olliej
источник
9

В ECMAScript 6 теперь есть функция, Setкоторая работает так, как вам хочется: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set

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

Даниэль Х Мур
источник
1
Это должен быть лучший ответ в 2016 году. Если вы используете Babel, вы можете использовать Set в соответствии со спецификацией ES6, и он будет автоматически заполнен в выводе ES5. babeljs.io/docs/learn-es2015/#map-set-weak-map-weak-set
atroberts20
5

Ссылка: 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';
Халид Азам
источник
2
За исключением того, что на самом деле нет способа восстановить Символ, пусть x = Symbol ('a'); let y = Symbol ('a'); console.log (x === y); // возвращает false, поэтому Symbol не работает как хеш.
Ричард Коллетт
3

Вот мое простое решение, которое возвращает уникальное целое число.

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;
}
Тимоти Перес
источник
2
Сложность этого подрывает всю идею позади hashCode ()
tuxSlayer
Я не считаю это излишне сложным. Мне было любопытно, хотя: почему заменить фазу? В противном случае исключения должны были бы вернуться в charCodeAt, не так ли?
Грег Петтит
Жаль из-за hashcode({a:1, b:2}) === hashcode({a:2, b:1})и многих других конфликтов.
Маартин
3

Основываясь на заголовке, мы можем генерировать сильные хэши с помощью js, его можно использовать для генерации уникального хеша из объекта, массива параметров, строки или чего-либо еще.

Позже для индексации это позволит избежать возможных ошибок сопоставления, позволяя при этом извлекать индекс из параметров (избегать поиска / зацикливания объекта и т. Д.):

async function H(m) {
  const msgUint8 = new TextEncoder().encode(m)                       
  const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8)          
  const hashArray = Array.from(new Uint8Array(hashBuffer))                    
  const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('')
  console.log(hashHex)
}

/* Examples ----------------------- */
H("An obscure ....")
H(JSON.stringify( {"hello" : "world"} ))
H(JSON.stringify( [54,51,54,47] ))

Выше приведен вывод в моем браузере, он должен быть одинаковым и для вас ( это правда? ):

bf1cf3fe6975fe382ab392ec1dd42009380614be03d489f23601c11413cfca2b
93a23971a914e5eacbf0a8d25154cda309c3c1c72fbb9914d47c60f3cb681588
d2f209e194045604a3b15bdfd7502898a0e848e4603c5a818bd01da69c00ad19

https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest#Converting_a_digest_to_a_hex_string

NVRM
источник
1

Мое решение вводит статическую функцию для глобального Objectобъекта.

(function() {
    var lastStorageId = 0;

    this.Object.hash = function(object) {
        var hash = object.__id;

        if (!hash)
             hash = object.__id = lastStorageId++;

        return '#' + hash;
    };
}());

Я думаю, что это удобнее с другими функциями управления объектами в JavaScript.

Джонни
источник
1
Объекты с одинаковыми внутренними значениями будут хэшировать разные хэши, это не то, что делает хэш (код).
Metalstorm
В JavaScript (и я думаю, что почти во всех других языках) два объекта, созданные с одинаковыми внутренними значениями, по-прежнему являются разными объектами, потому что лежащий в основе тип данных представлен каждым новым экземпляром объекта. jsfiddle.net/h4G9f
Джонни
4
Да, но это не то, для чего нужен хеш-код, хеш-коды используются для проверки равенства состояния объектов. Так же, как хеш, одни и те же входные данные (переменные значения) входят в тот же самый хэш. То, что вы ищете, это UUID (именно это обеспечивает ваша функция).
Metalstorm
1
Ты прав. Я неправильно понимаю вопрос. Очень плохо, что принятый ответ также не дает хорошего решения.
Джонни
Что касается вашей функции, я бы склонялся к тому, чтобы она выглядела примерно так: jsfiddle.net/xVSsd Тот же результат, более короткий (LoC + chars) и, возможно, чуть-чуть быстрее :)
Metalstorm
1

Я постараюсь пойти немного глубже, чем другие ответы.

Даже если бы у JS была лучшая поддержка хеширования, он не мог бы магически все хешировать, во многих случаях вам придется определять свою собственную хеш-функцию. Например, Java имеет хорошую поддержку хеширования, но вам все еще нужно подумать и поработать.

Одна проблема связана с термином хэш / хэш-код ... существует криптографическое хеширование и некриптографическое хеширование. Другая проблема заключается в том, что вы должны понимать, почему хеширование полезно и как оно работает.

Когда мы говорим о хешировании в JavaScript или Java, большую часть времени мы говорим о некриптографическом хешировании, обычно о хешировании для hashmap / hashtable (если мы не работаем над аутентификацией или паролями, которые вы могли бы выполнять на стороне сервера, используя NodeJS. ..).

Это зависит от того, какие данные у вас есть и чего вы хотите достичь.

Ваши данные имеют некоторую естественную «простую» уникальность:

  • Хеш целого числа ... целое число, поскольку оно уникально, удачи вам!
  • Хеш строки ... это зависит от строки, если строка представляет уникальный идентификатор, вы можете рассматривать его как хеш (поэтому хеширование не требуется).
  • Все, что косвенно в значительной степени является уникальным целым числом, является простейшим случаем
  • Это будет уважать: хэш-код равен, если объекты равны

Ваши данные имеют некоторую естественную «составную» уникальность:

  • Например, для объекта person вы можете вычислить хеш, используя имя, фамилию, дату рождения, ... посмотреть, как это делает Java: хорошая функция хеширования для строк , или использовать другую информацию идентификатора, которая является дешевой и достаточно уникальной для вашего сценария использования.

Вы не представляете, какими будут ваши данные:

  • Удачи ... вы можете сериализовать строку и хэшировать ее в стиле Java, но это может быть дорого, если строка большая, и это не позволит избежать коллизий, а также произнесет хэш целого числа (self).

Не существует магически эффективного метода хеширования неизвестных данных, в некоторых случаях это довольно просто, в других случаях вам, возможно, придется подумать дважды. Таким образом, даже если JavaScript / ECMAScript добавляет дополнительную поддержку, для этой проблемы не существует волшебного решения.

На практике вам нужны две вещи: достаточно уникальности, достаточно скорости

В дополнение к этому здорово иметь: «хэш-код равен, если объекты равны»

Кристоф Русси
источник
0

Если вы действительно хотите установить поведение (я знаю Java), вам будет сложно найти решение в JavaScript. Большинство разработчиков рекомендуют уникальный ключ для представления каждого объекта, но это не так, как установлено, так как вы можете получить два идентичных объекта каждый с уникальным ключом. Java API выполняет проверку на наличие дублирующихся значений путем сравнения значений хеш-кода, а не ключей, и, поскольку в JavaScript отсутствует представление значений хэш-кода объектов в JavaScript, практически невозможно сделать то же самое. Даже библиотека Prototype JS допускает этот недостаток, когда говорит:

«Хеш можно рассматривать как ассоциативный массив, связывающий уникальные ключи со значениями (которые не обязательно уникальны) ...»

http://www.prototypejs.org/api/hash


источник
0

В дополнение к ответу без век, вот функция, которая возвращает воспроизводимый уникальный идентификатор для любого объекта:

var uniqueIdList = [];
function getConstantUniqueIdFor(element) {
    // HACK, using a list results in O(n), but how do we hash e.g. a DOM node?
    if (uniqueIdList.indexOf(element) < 0) {
        uniqueIdList.push(element);
    }
    return uniqueIdList.indexOf(element);
}

Как вы можете видеть, он использует список для поиска, который очень неэффективен, однако это лучшее, что я смог найти на данный момент.

cburgmer
источник
0

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

Я написал небольшую библиотеку, которая создает хэши из объектов, которые вы можете легко использовать для этой цели. Объекты могут даже иметь другой порядок, хэши будут одинаковыми. Внутренне вы можете использовать различные типы для вашего хэша (djb2, md5, sha1, sha256, sha512, palemd160).

Вот небольшой пример из документации:

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-Js.

Репозиторий: https://bitbucket.org/tehrengruber/es-js-hash

darthmatch
источник
0

Если вы хотите иметь уникальные значения в объекте поиска, вы можете сделать что-то вроде этого:

Создание объекта поиска

var lookup = {};

Настройка функции хэш-кода

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;

Другие типы

var key = getHashCode('StackOverflow');
// key = 'StackOverflow'
lookup[key] = true;

Конечный результат

{ 1337: true, 01132337: true, StackOverflow: true }

Обратите внимание, что getHashCodeне возвращает никакого значения, когда объект или массив пуст

getHashCode([{},{},{}]);
// '012'
getHashCode([[],[],[]]);
// '012'

Это похоже на решение @ijmacd, только getHashCodeне имеет JSONзависимости.

A1rPun
источник
У вас должна быть проблема с циклическими ссылками
tuxSlayer
@tuxSlayer Спасибо, что сообщили мне об этом. Вы можете легко расширить этот код с вашими потребностями, но я надеюсь, что идея несколько ясна :)
A1rPun
Это даст очень длинные ключи для больших объектов, которые могут сильно
Gershom
0

Я объединил ответы от век и КимХа.

Ниже приведен сервис angularjs, который поддерживает числа, строки и объекты.

exports.Hash = () => {
  let hashFunc;
  function stringHash(string, noType) {
    let hashString = string;
    if (!noType) {
      hashString = `string${string}`;
    }
    var hash = 0;
    for (var i = 0; i < hashString.length; i++) {
        var character = hashString.charCodeAt(i);
        hash = ((hash<<5)-hash)+character;
        hash = hash & hash; // Convert to 32bit integer
    }
    return hash;
  }

  function objectHash(obj, exclude) {
    if (exclude.indexOf(obj) > -1) {
      return undefined;
    }
    let hash = '';
    const keys = Object.keys(obj).sort();
    for (let index = 0; index < keys.length; index += 1) {
      const key = keys[index];
      const keyHash = hashFunc(key);
      const attrHash = hashFunc(obj[key], exclude);
      exclude.push(obj[key]);
      hash += stringHash(`object${keyHash}${attrHash}`, true);
    }
    return stringHash(hash, true);
  }

  function Hash(unkType, exclude) {
    let ex = exclude;
    if (ex === undefined) {
      ex = [];
    }
    if (!isNaN(unkType) && typeof unkType !== 'string') {
      return unkType;
    }
    switch (typeof unkType) {
      case 'object':
        return objectHash(unkType, ex);
      default:
        return stringHash(String(unkType));
    }
  }

  hashFunc = Hash;

  return Hash;
};

Пример использования:

Hash('hello world'), Hash('hello world') == Hash('hello world')
Hash({hello: 'hello world'}), Hash({hello: 'hello world'}) == Hash({hello: 'hello world'})
Hash({hello: 'hello world', goodbye: 'adios amigos'}), Hash({hello: 'hello world', goodbye: 'adios amigos'}) == Hash({goodbye: 'adios amigos', hello: 'hello world'})
Hash(['hello world']), Hash(['hello world']) == Hash(['hello world'])
Hash(1), Hash(1) == Hash(1)
Hash('1'), Hash('1') == Hash('1')

Вывод

432700947 true
-411117486 true
1725787021 true
-1585332251 true
1 true
-1881759168 true

объяснение

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

Сравнение объектов без век используется для предотвращения бесконечной рекурсии путем самообращения объектов.

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

Я создал этот сервис, чтобы у меня мог быть сервис ошибок, к которому обращаются объекты. Таким образом, одна служба может зарегистрировать ошибку с данным объектом, а другая может определить, были ли обнаружены какие-либо ошибки.

т.е.

JsonValidation.js

ErrorSvc({id: 1, json: '{attr: "not-valid"}'}, 'Invalid Json Syntax - key not double quoted');

UserOfData.js

ErrorSvc({id: 1, json: '{attr: "not-valid"}'});

Это вернуло бы:

['Invalid Json Syntax - key not double quoted']

Пока

ErrorSvc({id: 1, json: '{"attr": "not-valid"}'});

Это вернется

[]
Йожеф Моррисси
источник
0

Просто используйте скрытое секретное свойство с defineProperty enumerable: false

Работает очень быстро :

  • Первое прочитанное uniqueId: 1,257,500 операций в секунду
  • Все остальные: 309 226 485 операций / с
var nextObjectId = 1
function getNextObjectId() {
    return nextObjectId++
}

var UNIQUE_ID_PROPERTY_NAME = '458d576952bc489ab45e98ac7f296fd9'
function getObjectUniqueId(object) {
    if (object == null) {
        return null
    }

    var id = object[UNIQUE_ID_PROPERTY_NAME]

    if (id != null) {
        return id
    }

    if (Object.isFrozen(object)) {
        return null
    }

    var uniqueId = getNextObjectId()
    Object.defineProperty(object, UNIQUE_ID_PROPERTY_NAME, {
        enumerable: false,
        configurable: false,
        writable: false,
        value: uniqueId,
    })

    return uniqueId
}
Николай Махонин
источник