简体   繁体   English

在 Javascript 中从字符串生成哈希

[英]Generate a Hash from string in Javascript

I need to convert strings to some form of hash.我需要将字符串转换为某种形式的哈希。 Is this possible in JavaScript?这在 JavaScript 中可能吗?

I'm not utilizing a server-side language so I can't do it that way.我没有使用服务器端语言,所以我不能那样做。

String.prototype.hashCode = function() {
  var hash = 0, i, chr;
  if (this.length === 0) return hash;
  for (i = 0; i < this.length; i++) {
    chr   = this.charCodeAt(i);
    hash  = ((hash << 5) - hash) + chr;
    hash |= 0; // Convert to 32bit integer
  }
  return hash;
};

Source: http://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/来源: http ://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/

Many of the answers here are the same String.hashCode hash function taken from Java.这里的许多答案都是取自 Java 的相同String.hashCode哈希函数。 It dates back to 1981 from Gosling Emacs, is extremely weak, and makes zero sense performance-wise in modern JavaScript.它可以追溯到 1981 年的 Gosling Emacs,非常弱,并且在现代 JavaScript 中的性能方面是零意义的。 In fact, implementations could be significantly faster by using ES6 Math.imul , but no one took notice.事实上,使用 ES6 Math.imul可以显着加快实现速度,但没有人注意到。 We can do much better than this, at essentially identical performance.在基本相同的性能下,我们可以做得比这更好。

Here's one I did— cyrb53 , a simple but high quality 53-bit hash.这是我做的一个—— cyrb53 ,一个简单但高质量的 53 位散列。 It's quite fast, provides very good* hash distribution, and because it outputs 53 bits, has significantly lower collision rates compared to any 32-bit hash.它非常快,提供了非常好的*散列分布,并且因为它输出 53 位,与任何32 位散列相比,冲突率显着降低。 Also, you can ignore SA's CC license as it's public domain on my GitHub.此外,您可以忽略 SA 的 CC 许可证,因为它在我的 GitHub 上是公共域

const cyrb53 = function(str, seed = 0) {
    let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
    for (let i = 0, ch; i < str.length; i++) {
        ch = str.charCodeAt(i);
        h1 = Math.imul(h1 ^ ch, 2654435761);
        h2 = Math.imul(h2 ^ ch, 1597334677);
    }
    h1 = Math.imul(h1 ^ (h1>>>16), 2246822507) ^ Math.imul(h2 ^ (h2>>>13), 3266489909);
    h2 = Math.imul(h2 ^ (h2>>>16), 2246822507) ^ Math.imul(h1 ^ (h1>>>13), 3266489909);
    return 4294967296 * (2097151 & h2) + (h1>>>0);
};

*It is roughly similar to the well-known MurmurHash/xxHash algorithms. *与著名的 MurmurHash/xxHash 算法大致相似。 It uses a combination of multiplication and Xorshift to generate the hash, but not as thorough.它使用乘法和Xorshift的组合来生成哈希,但没有那么彻底。 As a result it's faster than either would be in JavaScript and significantly simpler to implement, but may not pass all tests in SMHasher.因此,它比 JavaScript 中的任何一个都更快,并且实现起来也更加简单,但可能无法通过 SMHasher 中的所有测试。 This is not a cryptographic hash function, so don't use this for security purposes.这不是加密散列函数,因此请勿将其用于安全目的。

Like any proper hash, it has an avalanche effect, which basically means small changes in the input have big changes in the output making the resulting hash appear more 'random':像任何适当的哈希一样,它具有雪崩效应,这基本上意味着输入的微小变化会在输出中产生很大的变化,从而使生成的哈希看起来更加“随机”:

"501c2ba782c97901" = cyrb53("a")
"459eda5bc254d2bf" = cyrb53("b")
"fbce64cc3b748385" = cyrb53("revenge")
"fb1d85148d13f93a" = cyrb53("revenue")

You can optionally supply a seed (unsigned integer, 32-bit max) for alternate streams of the same input:您可以选择为相同输入的备用流提供种子(无符号整数,最大 32 位):

"76fee5e6598ccd5c" = cyrb53("revenue", 1)
"1f672e2831253862" = cyrb53("revenue", 2)
"2b10de31708e6ab7" = cyrb53("revenue", 3)

Technically, it is a 64-bit hash, that is, two uncorrelated 32-bit hashes computed in parallel, but JavaScript is limited to 53-bit integers.从技术上讲,它是一个 64 位散列,即并行计算的两个不相关的 32 位散列,但 JavaScript 仅限于 53 位整数。 If convenient, the full 64-bit output can be used by altering the return statement with a hex string or array.如果方便,可以通过使用十六进制字符串或数组更改return 语句来使用完整的 64 位输出。

return [h2>>>0, h1>>>0];
// or
return (h2>>>0).toString(16).padStart(8,0)+(h1>>>0).toString(16).padStart(8,0);
// or 
return 4294967296n * BigInt(h2) + BigInt(h1);

Be aware that constructing hex strings drastically slows down batch processing.请注意,构建十六进制字符串会大大减慢批处理速度。 The array is much more efficient, but obviously requires two checks instead of one.该数组效率更高,但显然需要两次检查而不是一次检查。 I also included BigInt , which should be slightly faster than String , but still much slower than Array or Number .我还包括BigInt ,它应该比String稍快,但仍然比ArrayNumber慢得多。


Just for fun, here's TinySimpleHash, the smallest hash I could come up with that's still decent.只是为了好玩,这里是 TinySimpleHash,我能想出的最小哈希值仍然不错。 It's a 32-bit hash in 89 chars with better quality randomness than even FNV or DJB2:它是89 个字符的 32 位散列,具有比 FNV 或 DJB2 更好的随机性:

TSH=s=>{for(var i=0,h=9;i<s.length;)h=Math.imul(h^s.charCodeAt(i++),9**9);return h^h>>>9}

EDIT编辑

based on my jsperf tests, the accepted answer is actually faster: http://jsperf.com/hashcodelordvlad根据我的 jsperf 测试,接受的答案实际上更快:http: //jsperf.com/hashcodelordvlad

ORIGINAL原来的

if anyone is interested, here is an improved ( faster ) version, which will fail on older browsers who lack the reduce array function.如果有人感兴趣,这里有一个改进的(更快的)版本,它在缺少reduce数组功能的旧浏览器上会失败。

hashCode = function(s){
  return s.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0);              
}

one-liner arrow function version :单线箭头功能版本:

hashCode = s => s.split('').reduce((a,b)=>{a=((a<<5)-a)+b.charCodeAt(0);return a&a},0)

Note: Even with the best 32-bit hash, collisions will occur sooner or later.注意:即使使用最好的 32 位散列,也迟早发生冲突。

The hash collision probablility can be calculated as哈希碰撞概率可以计算为1 - e ^ (-k(k-1) / 2N , aproximated as , 近似为k^2 / 2N ( see here ). 见这里)。 This may be higher than intuition suggests:这可能比直觉所暗示的要高:
Assuming a 32-bit hash and k=10,000 items, a collision will occur with a probablility of 1.2%.假设 32 位散列和 k=10,000 项,将发生冲突的概率为 1.2%。 For 77,163 samples the probability becomes 50%!对于 77,163 个样本,概率变为 50%! ( calculator ). 计算器)。
I suggest a workaround at the bottom.我建议底部的解决方法。

In an answer to this question Which hashing algorithm is best for uniqueness and speed?在回答这个问题时, 哪种散列算法最适合唯一性和速度? , Ian Boyd posted a good in depth analysis . ,伊恩博伊德发表了一篇很好的深入分析 In short (as I interpret it), he comes to the conclusion that Murmur is best, followed by FNV-1a.简而言之(正如我所解释的那样),他得出的结论是,Murmur 是最好的,其次是 FNV-1a。
Java's String.hashCode() algorithm that esmiralha proposed seems to be a variant of DJB2. esmiralha 提出的 Java 的 String.hashCode() 算法似乎是 DJB2 的变体。

  • FNV-1a has aa better distribution than DJB2, but is slower FNV-1a 的分布比 DJB2 好,但速度较慢
  • DJB2 is faster than FNV-1a, but tends to yield more collisions DJB2 比 FNV-1a 快,但往往会产生更多的碰撞
  • MurmurHash3 is better and faster than DJB2 and FNV-1a (but the optimized implementation requires more lines of code than FNV and DJB2) MurmurHash3 比 DJB2 和 FNV-1a 更好更快(但优化后的实现需要比 FNV 和 DJB2 更多的代码行)

Some benchmarks with large input strings here: http://jsperf.com/32-bit-hash这里有一些带有大输入字符串的基准:http: //jsperf.com/32-bit-hash
When short input strings are hashed, murmur's performance drops, relative to DJ2B and FNV-1a: http://jsperf.com/32-bit-hash/3当对输入字符串进行哈希处理时,相对于 DJ2B 和 FNV-1a,murmur 的性能下降:http: //jsperf.com/32-bit-hash/3

So in general I would recommend murmur3.所以总的来说我会推荐murmur3。
See here for a JavaScript implementation: https://github.com/garycourt/murmurhash-js有关 JavaScript 实现,请参见此处: https ://github.com/garycourt/murmurhash-js

If input strings are short and performance is more important than distribution quality, use DJB2 (as proposed by the accepted answer by esmiralha).如果输入字符串很短并且性能比分发质量更重要,请使用 DJB2(如 esmiralha 接受的答案所建议的那样)。

If quality and small code size are more important than speed, I use this implementation of FNV-1a (based on this code ).如果质量和小代码量比速度更重要,我使用 FNV-1a 的这个实现(基于此代码)。

/**
 * Calculate a 32 bit FNV-1a hash
 * Found here: https://gist.github.com/vaiorabbit/5657561
 * Ref.: http://isthe.com/chongo/tech/comp/fnv/
 *
 * @param {string} str the input value
 * @param {boolean} [asString=false] set to true to return the hash value as 
 *     8-digit hex string instead of an integer
 * @param {integer} [seed] optionally pass the hash of the previous chunk
 * @returns {integer | string}
 */
function hashFnv32a(str, asString, seed) {
    /*jshint bitwise:false */
    var i, l,
        hval = (seed === undefined) ? 0x811c9dc5 : seed;

    for (i = 0, l = str.length; i < l; i++) {
        hval ^= str.charCodeAt(i);
        hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24);
    }
    if( asString ){
        // Convert to 8 digit hex string
        return ("0000000" + (hval >>> 0).toString(16)).substr(-8);
    }
    return hval >>> 0;
}

Improve Collision Probability提高碰撞概率

As explained here , we can extend the hash bit size using this trick:正如这里所解释的,我们可以使用这个技巧来扩展哈希位大小:

function hash64(str) {
    var h1 = hash32(str);  // returns 32 bit (as 8 byte hex string)
    return h1 + hash32(h1 + str);  // 64 bit (as 16 byte hex string)
}

Use it with care and don't expect too much though.小心使用它,不要期望太多。

Based on accepted answer in ES6.基于 ES6 中接受的答案 Smaller, maintainable and works in modern browsers.更小,可维护,适用于现代浏览器。

 function hashCode(str) { return str.split('').reduce((prevHash, currVal) => (((prevHash << 5) - prevHash) + currVal.charCodeAt(0))|0, 0); } // Test console.log("hashCode(\"Hello!\"): ", hashCode('Hello!'));

EDIT (2019-11-04) :编辑(2019-11-04)

one-liner arrow function version :单线箭头功能版本:

 const hashCode = s => s.split('').reduce((a,b) => (((a << 5) - a) + b.charCodeAt(0))|0, 0) // test console.log(hashCode('Hello!'))

I'm a bit surprised nobody has talked about the new SubtleCrypto API yet.我有点惊讶没有人谈论新的SubtleCrypto API

To get an hash from a string, you can use the subtle.digest method :要从字符串中获取哈希,您可以使用微妙的subtle.digest方法:

 function getHash(str, algo = "SHA-256") { let strBuf = new TextEncoder().encode(str); return crypto.subtle.digest(algo, strBuf) .then(hash => { window.hash = hash; // here hash is an arrayBuffer, // so we'll connvert it to its hex version let result = ''; const view = new DataView(hash); for (let i = 0; i < hash.byteLength; i += 4) { result += ('00000000' + view.getUint32(i).toString(16)).slice(-8); } return result; }); } getHash('hello world') .then(hash => { console.log(hash); });

This is a refined and better performing variant, and matches Java's implementation of the standard object.hashCode() for CharSequence .这是一个精炼且性能更好的变体,JavaCharSequence的标准object.hashCode()的实现相匹配。

String.prototype.hashCode = function() {
    var hash = 0, i = 0, len = this.length;
    while ( i < len ) {
        hash  = ((hash << 5) - hash + this.charCodeAt(i++)) << 0;
    }
    return hash;
};

Here is also one that returns only positive hashcodes:这也是一个只返回哈希码的方法:

String.prototype.hashcode = function() {
    return this.hashCode()+ 2147483647 + 1;
};

And here is a matching one for Java that only returns positive hashcodes:这是一个只返回正哈希码的Java匹配:

public static long hashcode(Object obj) {
    return ((long) obj.hashCode()) + Integer.MAX_VALUE + 1l;
}

Without prototype for those that do not want to attach it to String :对于那些不想将其附加到String的人来说,没有原型:

function hashCode(str) {
    var hash = 0, i = 0, len = str.length;
    while ( i < len ) {
        hash  = ((hash << 5) - hash + str.charCodeAt(i++)) << 0;
    }
    return hash;
}

function hashcode(str) {
    hashCode(str) + 2147483647 + 1;
}

Enjoy!享受!

If it helps anyone, I combined the top two answers into an older-browser-tolerant version, which uses the fast version if reduce is available and falls back to esmiralha's solution if it's not.如果它对任何人有帮助,我将前两个答案组合成一个旧的浏览器容忍版本,如果reduce可用,它使用快速版本,如果不是,则回退到 esmiralha 的解决方案。

/**
 * @see http://stackoverflow.com/q/7616461/940217
 * @return {number}
 */
String.prototype.hashCode = function(){
    if (Array.prototype.reduce){
        return this.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0);              
    } 
    var hash = 0;
    if (this.length === 0) return hash;
    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;
}

Usage is like:用法如下:

var hash = "some string to be hashed".hashCode();

Here is a compact ES6 friendly readable snippet这是一个紧凑的 ES6 友好可读片段

const stringHashCode = str => {
  let hash = 0
  for (let i = 0; i < str.length; ++i)
    hash = Math.imul(31, hash) + str.charCodeAt(i)

  return hash | 0
}

UUID v3 and UUID v5 actually are hashes for a given input string. UUID v3 和 UUID v5 实际上是给定输入字符串的哈希值。

  • UUID v3 is based on MD5, UUID v3 基于 MD5,
  • UUID v5 is based on SHA-1. UUID v5 基于 SHA-1。

So, the most obvious choice would be to go for UUID v5.因此,最明显的选择是使用 UUID v5。

Fortunately, there is a popular npm package, which includes all UUID algorithms.幸运的是,有一个流行的 npm 包,其中包含所有 UUID 算法。

npm install uuid

To actually generate a UUID v5, you need a unique namespace.要实际生成 UUID v5,您需要一个唯一的命名空间。 This namespace acts like a seed, and should be a constant, to assure that for a given input the output will always be the same.这个命名空间就像一个种子,应该是一个常数,以确保对于给定的输入,输出总是相同的。 Ironically, you should generate a UUID v4 as a namespace.具有讽刺意味的是,您应该生成一个 UUID v4 作为命名空间。 And the easiest way to do so, is using some online tool .最简单的方法是使用一些在线工具

Once you've got a namespace, you're all set.一旦你有了一个命名空间,你就准备好了。

import { v5 as uuidv5 } from 'uuid';

const MY_NAMESPACE = '1b671a64-40d5-491e-99b0-da01ff1f3341';
const hash = uuidv5('input', MY_NAMESPACE);

If your input string will always be an URL for instance, then there are some default namespaces which you can use.例如,如果您的输入字符串始终是 URL,那么您可以使用一些默认命名空间。

const hashForURL = uuidv5('https://www.w3.org/', uuidv5.URL);

Thanks to the example by mar10, I found a way to get the same results in C# AND Javascript for an FNV-1a.感谢 mar10 的示例,我找到了一种在 C# 和 Javascript 中为 FNV-1a 获得相同结果的方法。 If unicode chars are present, the upper portion is discarded for the sake of performance.如果存在 unicode 字符,则出于性能考虑,将丢弃上部。 Don't know why it would be helpful to maintain those when hashing, as am only hashing url paths for now.不知道为什么在散列时维护它们会有所帮助,因为现在只散列 url 路径。

C# Version C#版本

private static readonly UInt32 FNV_OFFSET_32 = 0x811c9dc5;   // 2166136261
private static readonly UInt32 FNV_PRIME_32 = 0x1000193;     // 16777619

// Unsigned 32bit integer FNV-1a
public static UInt32 HashFnv32u(this string s)
{
    // byte[] arr = Encoding.UTF8.GetBytes(s);      // 8 bit expanded unicode array
    char[] arr = s.ToCharArray();                   // 16 bit unicode is native .net 

    UInt32 hash = FNV_OFFSET_32;
    for (var i = 0; i < s.Length; i++)
    {
        // Strips unicode bits, only the lower 8 bits of the values are used
        hash = hash ^ unchecked((byte)(arr[i] & 0xFF));
        hash = hash * FNV_PRIME_32;
    }
    return hash;
}

// Signed hash for storing in SQL Server
public static Int32 HashFnv32s(this string s)
{
    return unchecked((int)s.HashFnv32u());
}

JavaScript Version JavaScript 版本

var utils = utils || {};

utils.FNV_OFFSET_32 = 0x811c9dc5;

utils.hashFnv32a = function (input) {
    var hval = utils.FNV_OFFSET_32;

    // Strips unicode bits, only the lower 8 bits of the values are used
    for (var i = 0; i < input.length; i++) {
        hval = hval ^ (input.charCodeAt(i) & 0xFF);
        hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24);
    }

    return hval >>> 0;
}

utils.toHex = function (val) {
    return ("0000000" + (val >>> 0).toString(16)).substr(-8);
}

我基于 FNV 的Multiply+Xor方法的快速(很长)一个班轮:

my_string.split('').map(v=>v.charCodeAt(0)).reduce((a,v)=>a+((a<<7)+(a<<3))^v).toString(16);

A fast and concise one which was adapted from here :这里改编的快速简洁的:

String.prototype.hashCode = function() {
  var hash = 5381, i = this.length
  while(i)
    hash = (hash * 33) ^ this.charCodeAt(--i)
  return hash >>> 0;
}

I'm kinda late to the party, but you can use this module: crypto :我参加聚会有点晚了,但你可以使用这个模块: crypto

const crypto = require('crypto');

const SALT = '$ome$alt';

function generateHash(pass) {
  return crypto.createHmac('sha256', SALT)
    .update(pass)
    .digest('hex');
}

The result of this function is always is 64 characters string;该函数的结果始终是64字符的字符串; something like this: "aa54e7563b1964037849528e7ba068eb7767b1fab74a8d80fe300828b996714a"像这样的东西: "aa54e7563b1964037849528e7ba068eb7767b1fab74a8d80fe300828b996714a"

I needed a similar function (but different) to generate a unique-ish ID based on the username and current time.我需要一个类似的函数(但不同)来根据用户名和当前时间生成一个唯一的 ID。 So:所以:

window.newId = ->
  # create a number based on the username
  unless window.userNumber?
    window.userNumber = 0
  for c,i in window.MyNamespace.userName
    char = window.MyNamespace.userName.charCodeAt(i)
    window.MyNamespace.userNumber+=char
  ((window.MyNamespace.userNumber + Math.floor(Math.random() * 1e15) + new Date().getMilliseconds()).toString(36)).toUpperCase()

Produces:产生:

2DVFXJGEKL
6IZPAKFQFL
ORGOENVMG
... etc 

edit Jul 2022: As @canRau points out the authors of shortid prefer nanoid now https://github.com/ai/nanoid/ 2022 年 7 月编辑:正如 @canRau 指出的,shortid 的作者现在更喜欢 nanoid https://github.com/ai/nanoid/

SubtleCrypto.digest SubtleCrypto.digest

I'm not utilizing a server-side language so I can't do it that way.我没有使用服务器端语言,所以我不能那样做。

Are you sure you can't do it that way ?你确定你不能那样做吗?

Did you forget you're using Javascript, the language ever-evolving?您是否忘记了您正在使用 Javascript,这是一种不断发展的语言?

Try SubtleCrypto .试试SubtleCrypto It supports SHA-1, SHA-128, SHA-256, and SHA-512 hash functions.它支持 SHA-1、SHA-128、SHA-256 和 SHA-512 哈希函数。


 async function hash(message/*: string */) { const text_encoder = new TextEncoder; const data = text_encoder.encode(message); const message_digest = await window.crypto.subtle.digest("SHA-512", data); return message_digest; } // -> ArrayBuffer function in_hex(data/*: ArrayBuffer */) { const octets = new Uint8Array(data); const hex = [].map.call(octets, octet => octet.toString(16).padStart(2, "0")).join(""); return hex; } // -> string (async function demo() { console.log(in_hex(await hash("Thanks for the magic."))); })();

The Jenkins One at a Time Hash is quite nice: Jenkins One at a Time Hash非常好:

//Credits (modified code): Bob Jenkins (http://www.burtleburtle.net/bob/hash/doobs.html)
//See also: https://en.wikipedia.org/wiki/Jenkins_hash_function
//Takes a string of any size and returns an avalanching hash string of 8 hex characters.
function jenkinsOneAtATimeHash(keyString)
{
  let hash = 0;
  for (charIndex = 0; charIndex < keyString.length; ++charIndex)
  {
    hash += keyString.charCodeAt(charIndex);
    hash += hash << 10;
    hash ^= hash >> 6;
  }
  hash += hash << 3;
  hash ^= hash >> 11;
  //4,294,967,295 is FFFFFFFF, the maximum 32 bit unsigned integer value, used here as a mask.
  return (((hash + (hash << 15)) & 4294967295) >>> 0).toString(16)
};

Examples:例子:

jenkinsOneAtATimeHash('test')
"31c25ec1"
jenkinsOneAtATimeHash('a')
"ca2e9442"
jenkinsOneAtATimeHash('0')
"6e3c5c6b"

You can also remove the .toString(16) part at the end to generate numbers:您还可以删除末尾的.toString(16)部分以生成数字:

jenkinsOneAtATimeHash2('test')
834821825
jenkinsOneAtATimeHash2('a')
3392050242
jenkinsOneAtATimeHash2('0')
1849449579

Note that if you do not need to hash a string or key specifically, but just need a hash generated out of thin air, you can use :请注意,如果您不需要专门对字符串进行散列,而只需要凭空生成的散列,则可以使用

window.crypto.getRandomValues(new Uint32Array(1))[0].toString(16)

Examples:例子:

window.crypto.getRandomValues(new Uint32Array(1))[0].toString(16)
"6ba9ea7"
window.crypto.getRandomValues(new Uint32Array(1))[0].toString(16)
"13fe7edf"
window.crypto.getRandomValues(new Uint32Array(1))[0].toString(16)
"971ffed4"

and the same as above, you can remove the `.toString(16) part at the end to generate numbers:和上面一样,你可以去掉最后的`.toString(16)部分来生成数字:

window.crypto.getRandomValues(new Uint32Array(1))[0]
1154752776
window.crypto.getRandomValues(new Uint32Array(1))[0]
3420298692
window.crypto.getRandomValues(new Uint32Array(1))[0]
1781389127

Note: You can also generate multiple values at once with this method, eg:注意:您也可以使用此方法一次生成多个值,例如:

window.crypto.getRandomValues(new Uint32Array(3))
Uint32Array(3) [ 2050530949, 3280127172, 3001752815 ]

I have combined the two solutions (users esmiralha and lordvlad) to get a function that should be faster for browsers that support the js function reduce() and still compatible with old browsers:我结合了两种解决方案(用户 esmiralha 和 lordvlad)来获得一个对于支持 js 函数reduce()并且仍然与旧浏览器兼容的浏览器应该更快的函数:

String.prototype.hashCode = function() {

    if (Array.prototype.reduce) {
        return this.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0);   
    } else {

        var hash = 0, i, chr, len;
        if (this.length == 0) return hash;
        for (i = 0, len = this.length; i < len; i++) {
        chr   = this.charCodeAt(i);
        hash  = ((hash << 5) - hash) + chr;
        hash |= 0; // Convert to 32bit integer
        }
        return hash;
    }
};

Example:例子:

my_string = 'xyz';
my_string.hashCode();

I went for a simple concatenation of char codes converted to hex strings.我进行了转换为十六进制字符串的字符代码的简单串联。 This serves a relatively narrow purpose, namely just needing a hash representation of a SHORT string (eg titles, tags) to be exchanged with a server side that for not relevant reasons can't easily implement the accepted hashCode Java port.这服务于一个相对狭窄的目的,即只需要一个短字符串(例如标题、标签)的散列表示与服务器端交换,由于不相关的原因不能轻易实现接受的 hashCode Java 端口。 Obviously no security application here.显然这里没有安全应用程序。

String.prototype.hash = function() {
  var self = this, range = Array(this.length);
  for(var i = 0; i < this.length; i++) {
    range[i] = i;
  }
  return Array.prototype.map.call(range, function(i) {
    return self.charCodeAt(i).toString(16);
  }).join('');
}

This can be made more terse and browser-tolerant with Underscore.使用下划线可以使这更加简洁和浏览器容忍。 Example:例子:

"Lorem Ipsum".hash()
"4c6f72656d20497073756d"

I suppose if you wanted to hash larger strings in similar fashion you could just reduce the char codes and hexify the resulting sum rather than concatenate the individual characters together:我想如果你想以类似的方式散列更大的字符串,你可以减少字符代码并将结果总和十六进制,而不是将单个字符连接在一起:

String.prototype.hashLarge = function() {
  var self = this, range = Array(this.length);
  for(var i = 0; i < this.length; i++) {
    range[i] = i;
  }
  return Array.prototype.reduce.call(range, function(sum, i) {
    return sum + self.charCodeAt(i);
  }, 0).toString(16);
}

'One time, I hired a monkey to take notes for me in class. I would just sit back with my mind completely blank while the monkey scribbled on little pieces of paper. At the end of the week, the teacher said, "Class, I want you to write a paper using your notes." So I wrote a paper that said, "Hello! My name is Bingo! I like to climb on things! Can I have a banana? Eek, eek!" I got an F. When I told my mom about it, she said, "I told you, never trust a monkey!"'.hashLarge()
"9ce7"

Naturally more risk of collision with this method, though you could fiddle with the arithmetic in the reduce however you wanted to diversify and lengthen the hash.当然,与此方法发生冲突的风险更大,尽管您可以摆弄 reduce 中的算术,但是您想多样化和延长散列。

If you want to avoid collisions you may want to use a secure hash like SHA-256 .如果您想避免冲突,您可能需要使用像SHA-256这样的安全哈希 There are several JavaScript SHA-256 implementations.有几种 JavaScript SHA-256 实现。

I wrote tests to compare several hash implementations, see https://github.com/brillout/test-javascript-hash-implementations .我编写了测试来比较几种哈希实现,请参阅https://github.com/brillout/test-javascript-hash-implementations

Or go tohttp://brillout.github.io/test-javascript-hash-implementations/ , to run the tests.或者去http://brillout.github.io/test-javascript-hash-implementations/运行测试。

I do not see any reason to use this overcomplicated crypto code instead of ready-to-use solutions, like object-hash library, or etc. relying on vendor is more productive, saves time and reduces maintenance cost.我认为没有任何理由使用这种过于复杂的加密代码而不是现成的解决方案,如对象哈希库等。依赖供应商的效率更高,节省时间并降低维护成本。

Just use https://github.com/puleos/object-hash只需使用https://github.com/puleos/object-hash

var hash = require('object-hash');

hash({foo: 'bar'}) // => '67b69634f9880a282c14a0f0cb7ba20cf5d677e9'
hash([1, 2, 2.718, 3.14159]) // => '136b9b88375971dff9f1af09d7356e3e04281951'

Adding this because nobody did yet, and this seems to be asked for and implemented a lot with hashes, but it's always done very poorly...添加这个是因为还没有人这样做,这似乎被要求并用哈希实现了很多,但它总是做得很差......

This takes a string input, and a maximum number you want the hash to equal, and produces a unique number based on the string input.这需要一个字符串输入和一个您希望哈希相等的最大数字,并根据字符串输入生成一个唯一数字。

You can use this to produce a unique index into an array of images (If you want to return a specific avatar for a user, chosen at random, but also chosen based on their name, so it will always be assigned to someone with that name).您可以使用它来生成图像数组的唯一索引(如果您想为用户返回一个特定的头像,随机选择,但也根据他们的名字选择,所以它总是被分配给具有该名称的人)。

You can also use this, of course, to return an index into an array of colors, like for generating unique avatar background colors based on someone's name.当然,您也可以使用它来将索引返回到颜色数组中,例如根据某人的姓名生成独特的头像背景颜色。

function hashInt (str, max = 1000) {
    var hash = 0;
    for (var i = 0; i < str.length; i++) {
      hash = ((hash << 5) - hash) + str.charCodeAt(i);
      hash = hash & hash;
    }
    return Math.round(max * Math.abs(hash) / 2147483648);
}

This generates a consistent hash based on any number of params passed in:这会根据传入的任意数量的参数生成一致的哈希:

/**
 * Generates a hash from params passed in
 * @returns {string} hash based on params
 */
function fastHashParams() {
    var args = Array.prototype.slice.call(arguments).join('|');
    var hash = 0;
    if (args.length == 0) {
        return hash;
    }
    for (var i = 0; i < args.length; i++) {
        var char = args.charCodeAt(i);
        hash = ((hash << 5) - hash) + char;
        hash = hash & hash; // Convert to 32bit integer
    }
    return String(hash);
}

fastHashParams('hello world') outputs "990433808" fastHashParams('hello world')输出"990433808"

fastHashParams('this',1,'has','lots','of','params',true) outputs "1465480334" fastHashParams('this',1,'has','lots','of','params',true)输出"1465480334"

Slightly simplified version of @esmiralha's answer. @esmiralha 答案的略微简化版本。

I don't override String in this version, since that could result in some undesired behaviour.我不会在这个版本中覆盖 String,因为这可能会导致一些不良行为。

function hashCode(str) {
    var hash = 0;
    for (var i = 0; i < str.length; i++) {
        hash = ~~(((hash << 5) - hash) + str.charCodeAt(i));
    }
    return hash;
}

This should be a bit more secure hash than some other answers, but in a function, without any preloaded source这应该比其他一些答案更安全一点,但在一个函数中,没有任何预加载的源

I created basicly a minified simplified version of sha1.我basicly创建SHA1的缩小简化版
You take the bytes of the string and group them by 4 to 32bit "words"您获取字符串的字节并将它们按 4 到 32 位“字”分组
Then we extend every 8 words to 40 words (for bigger impact on the result).然后我们将每 8 个单词扩展到 40 个单词(对结果有更大的影响)。
This goes to the hashing function (the last reduce) where we do some maths with the current state and the input.这进入散列函数(最后一个reduce),在那里我们用当前状态和输入做一些数学运算。 We always get 4 words out.我们总是说 4 个字。
This is almost a one-command/one-line version using map,reduce... instead of loops, but it is still pretty fast这几乎是一个使用 map,reduce... 而不是循环的单命令/单行版本,但它仍然相当快

String.prototype.hash = function(){
    var rot = (word, shift) => word << shift | word >>> (32 - shift);
    return unescape(encodeURIComponent(this.valueOf())).split("").map(char =>
            char.charCodeAt(0)
        ).reduce((done, byte, idx, arr) =>
            idx % 4 == 0 ? [...done, arr.slice(idx, idx + 4)] : done
        , []).reduce((done, group) =>
            [...done, group[0] << 24 | group[1] << 16 | group[2] << 8 | group[3]]
        , []).reduce((done, word, idx, arr) =>
            idx % 8 == 0 ? [...done, arr.slice(idx, idx + 8)] : done
        , []).map(group => {
            while(group.length < 40)
                group.push(rot(group[group.length - 2] ^ group[group.length - 5] ^ group[group.length - 8], 3));
            return group;
        }).flat().reduce((state, word, idx, arr) => {
            var temp = ((state[0] + rot(state[1], 5) + word + idx + state[3]) & 0xffffffff) ^ state[idx % 2 == 0 ? 4 : 5](state[0], state[1], state[2]);
            state[0] = rot(state[1] ^ state[2], 11);
            state[1] = ~state[2] ^ rot(~state[3], 19);
            state[2] = rot(~state[3], 11);
            state[3] = temp;
            return state;
        }, [0xbd173622, 0x96d8975c, 0x3a6d1a23, 0xe5843775,
            (w1, w2, w3) => (w1 & rot(w2, 5)) | (~rot(w1, 11) & w3),
            (w1, w2, w3) => w1 ^ rot(w2, 5) ^ rot(w3, 11)]
        ).slice(0, 4).map(p =>
            p >>> 0
        ).map(word =>
            ("0000000" + word.toString(16)).slice(-8)
        ).join("");
};

we also convert the output to hex to get a string instead of word array.我们还将输出转换为十六进制以获取字符串而不是单词数组。
Usage is simple.用法很简单。 for expample "a string".hash() will return "88a09e8f9cc6f8c71c4497fbb36f84cd"例如"a string".hash()将返回"88a09e8f9cc6f8c71c4497fbb36f84cd"

 String.prototype.hash = function(){ var rot = (word, shift) => word << shift | word >>> (32 - shift); return unescape(encodeURIComponent(this.valueOf())).split("").map(char => char.charCodeAt(0) ).reduce((done, byte, idx, arr) => idx % 4 == 0 ? [...done, arr.slice(idx, idx + 4)] : done , []).reduce((done, group) => [...done, group[0] << 24 | group[1] << 16 | group[2] << 8 | group[3]] , []).reduce((done, word, idx, arr) => idx % 8 == 0 ? [...done, arr.slice(idx, idx + 8)] : done , []).map(group => { while(group.length < 40) group.push(rot(group[group.length - 2] ^ group[group.length - 5] ^ group[group.length - 8], 3)); return group; }).flat().reduce((state, word, idx, arr) => { var temp = ((state[0] + rot(state[1], 5) + word + idx + state[3]) & 0xffffffff) ^ state[idx % 2 == 0 ? 4 : 5](state[0], state[1], state[2]); state[0] = rot(state[1] ^ state[2], 11); state[1] = ~state[2] ^ rot(~state[3], 19); state[2] = rot(~state[3], 11); state[3] = temp; return state; }, [0xbd173622, 0x96d8975c, 0x3a6d1a23, 0xe5843775, (w1, w2, w3) => (w1 & rot(w2, 5)) | (~rot(w1, 11) & w3), (w1, w2, w3) => w1 ^ rot(w2, 5) ^ rot(w3, 11)] ).slice(0, 4).map(p => p >>> 0 ).map(word => ("0000000" + word.toString(16)).slice(-8) ).join(""); }; let str = "the string could even by empty"; console.log(str.hash())//9f9aeca899367572b875b51be0d566b5

I've actually utilized this in a web page recently, and it works nicely...我最近实际上在一个网页中使用了它,它运行良好......

//Credits (modified code): Bob Jenkins (http://www.burtleburtle.net/bob/hash/doobs.html)
//See also: https://en.wikipedia.org/wiki/Jenkins_hash_function
//Takes a string of any size and returns an avalanching hash string of 8 hex characters.
function jenkinsOneAtATimeHash(keyString)
{
  let hash = 0;
  for (charIndex = 0; charIndex < keyString.length; ++charIndex)
  {
    hash += keyString.charCodeAt(charIndex);
    hash += hash << 10;
    hash ^= hash >> 6;
  }
  hash += hash << 3;
  hash ^= hash >> 11;
  //4,294,967,295 is FFFFFFFF, the maximum 32 bit unsigned integer value, used here as a mask.
  return (((hash + (hash << 15)) & 4294967295) >>> 0).toString(16)
};

My problem now is that I want to validate the hex return code in a python script and so far am unable to reproduce the output of the above javascript code in Python.我现在的问题是我想验证 python 脚本中的十六进制返回码,到目前为止我无法在 Python 中重现上述 javascript 代码的输出。 :( :(

Ultimately, it seems like I can reproduce the hash from the Wiki in Python: https://en.wikipedia.org/wiki/Jenkins_hash_function But it would appear that this JavaScript code is the issue and not properly reproducing the results of the original one_at_a_time hash..??最终,我似乎可以在 Python 中从 Wiki 中重现哈希: https ://en.wikipedia.org/wiki/Jenkins_hash_function 但看起来这个 JavaScript 代码是问题所在,并且没有正确重现原始 one_at_a_time 的结果哈希..?? Watching the hash values as the code goes through it's iterations in Javascript show that the hash transforms to negative at times, which may be what then causes the mis-match with the original code?观察代码在 Javascript 中迭代时的散列值表明散列有时会转换为负数,这可能是导致与原始代码不匹配的原因吗?

Any help/ideas would be greatly appreciated...任何帮助/想法将不胜感激...

IMPORTANT!!!!重要的!!!! READ THIS!!!读这个!!!

 function hash() { var args = Array.prototype.slice.call(arguments).join('|'); var hash = 0; if (args.length == 0) { return hash; } for (var i = 0; i < args.length; i++) { var char = args.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; // Convert to 32bit integer } return String(hash); } console.log(hash('ti')) console.log(hash('v+'))

You still can have some collisions with this implementation!您仍然可以与此实现发生一些冲突! BE CAREFUL!当心!

console.log(hash('ti')) // 3701
console.log(hash('v+')) // 3701

 function hashCode(str) { return str.split('').reduce((prevHash, currVal) => (((prevHash << 5) - prevHash) + currVal.charCodeAt(0))|0, 0); } // Test console.log("hashCode(\"Hello:\"), "; hashCode('Hello!'));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM