我需要将字符串转换为某种形式的哈希。这在JavaScript中是可能的吗?

我没有使用服务器端语言,所以我不能这样做。


当前回答

这里的许多答案都是取自Java的String.hashCode哈希函数。它可以追溯到1981年的Gosling Emacs,它非常脆弱,在现代JavaScript中表现得毫无意义。事实上,通过使用ES6 Math.imul,实现速度可能会大大加快,但没有人注意到。我们可以在基本相同的性能下做得更好。

这是我做的一个cryb53,一个简单但高质量的53位散列。它非常快,提供了非常好的*哈希分布,并且因为它输出53位,所以与任何32位哈希相比,具有明显更低的冲突率。此外,您可以忽略SA的CC许可证,因为它是我GitHub上的公共域。

常量cyrb53=(str,种子=0)=>{设h1=0xdeadbeef^种子,h2=0x41c6ce57^种子;for(设i=0,ch;i<str.length;i++){ch=str.charCodeAt(i);h1=数学模拟(h1^ch,2654435761);h2=数学模拟(h2^ch,1597334677);}h1=数学模拟(h1^(h1>>16),2246822507)^数学模拟(h2^(h2>>13),3266489909);h2=数学模拟(h2^(h2>>16),2246822507)^数学模拟(h1^(h1>>13),3266489909);返回4294967296*(2097151&h2)+(h1>>>0);};console.log(`cyrb53('a')->${cyrb53“'a')}`)console.log(`cyrb53('b')->${cyrb53console.log(`cyrb53('return')->${cyrb53('reten')}`)console.log(`cyrb53('resident')->${cyrb53console.log(`cyrb53('resident',1)->${cyrb53console.log(`cyrb53('resident',2)->${cyrb53console.log(`cyrb53('resident',3)->${cyrb53

*它大致类似于众所周知的MurmurHash/xxHash算法。它使用乘法和Xorshift的组合来生成哈希,但并不彻底。因此,它比JavaScript中的任何一种都要快,实现起来也要简单得多,但可能无法通过SMHasher中的所有测试。这不是加密哈希函数,因此不要将其用于安全目的。

与任何适当的哈希一样,它具有雪崩效应,这基本上意味着输入中的小变化会导致输出中的大变化,从而使生成的哈希看起来更“随机”:

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

您可以选择为相同输入的交替流提供种子(无符号整数,最大32位):

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

从技术上讲,它是一个64位散列,即两个不相关的32位散列并行计算,但JavaScript限于53位整数。如果方便,可以通过使用十六进制字符串或数组更改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);

请注意,构造十六进制字符串会大大降低批处理速度。该阵列效率更高,但显然需要两次检查而不是一次。我还包括BigInt,它应该比String稍快,但仍然比Array或Number慢得多。


为了好玩,这里有TinySimpleHash,这是我能想出的最小的哈希,它仍然很不错。这是一个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}

其他回答

添加这一点是因为还没有人这样做,而这似乎是用哈希来要求和实现的,但它总是做得很糟糕。。。

这需要一个字符串输入和您希望哈希值相等的最大值,并根据字符串输入生成一个唯一的数字。

您可以使用它来生成图像数组中的唯一索引(如果您希望为用户返回一个特定的化身,该化身是随机选择的,但也是基于其名称选择的,因此它将始终分配给具有该名称的人)。

当然,您也可以使用它将索引返回到颜色数组中,例如根据某人的姓名生成独特的化身背景颜色。

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);
}

如果这对任何人都有帮助的话,我将前两个答案组合成一个更老的浏览器容忍版本,如果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;
}

用法如下:

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

我需要一个类似的函数(但不同)来根据用户名和当前时间生成一个唯一的ish ID。因此:

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()

生产:

2DVFXJGEKL
6IZPAKFQFL
ORGOENVMG
... etc 

编辑2022年7月:正如@canRau指出的那样,shortid的作者现在更喜欢nanoidhttps://github.com/ai/nanoid/

我将这两种解决方案(用户esmiralha和lordflad)结合在一起,得到了一个对于支持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;
    }
};

例子:

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

函数hashCode(str){return str.split(“”).reduce((prevHash,currVal)=>(((prevHash<<5)-previvHash)+currVal.charCodeAt(0))|0,0);}//测试console.log(“hashCode(\“Hello!\”):“,hashCode('Hello!'));