JavaScript中的简单(不安全)哈希函数?

196

可能是重复问题:
在Javascript/jQuery中生成哈希值

有人能推荐一个简单的(即代码只有几十行而不是几百行),用(浏览器兼容的)JavaScript编写的哈希函数吗?理想情况下,当输入一个字符串时,它应该产生类似于MD5、SHA1等输出的32个字符的十六进制字符串。它不必是加密安全的,只需要对碰撞有合理的抵抗力。(我最初使用用例是URL,但我将来可能也会在其他字符串上使用它。)


你不想使用sha1有特别的原因吗?在js中有大量的例子。 - Alex K.
4
我想将这个函数和其他约50行代码一起打包,但我不希望我的哈希函数比“有趣的”部分长10倍。 - mjs
我现在认为我理解了你的观点。你不能使用include的原因是什么?你真的需要只使用一个文件吗? - jsalonen
5
是的,我可以这样做,如果有必要的话我也已经准备好了,但我更喜欢一个自包含的东西,可以发布到Github或类似的地方。 - mjs
6
实现了Jenkins的单个哈希函数 window.hashJoaat=function(b){for(var a=0,c=b.length;c--;)a+=b.charCodeAt(c),a+=a<<10,a^=a>>6;a+=a<<3;a^=a>>11;return((a+(a<<15)&4294967295)>>>0).toString(16)}; - Orwellophile
显示剩余2条评论
5个回答

213

我没有亲自验证过,但是您可以查看这篇JavaScript实现Java的String.hashCode()方法。看起来相当简短。

使用这个原型,您可以在任何字符串上简单地调用.hashCode(),例如"some string".hashCode(),并接收一个数值哈希码(更具体地说,是Java等效哈希码),如1395333309。

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

2022年修改:

长期以来,人们普遍认为修改内置原型是不良做法,因此你应该使用一个纯函数(plain function)代替:

/**
 * Returns a hash code from a string
 * @param  {String} str The string to hash.
 * @return {Number}    A 32bit integer
 * @see http://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
 */
function hashCode(str) {
    let hash = 0;
    for (let i = 0, len = str.length; i < len; i++) {
        let chr = str.charCodeAt(i);
        hash = (hash << 5) - hash + chr;
        hash |= 0; // Convert to 32bit integer
    }
    return hash;
}

13
SHA1和MD5运行速度极慢。我进行了一系列的比较测试,这个Java哈希实现证明是最快的,并且在相对均匀的数据上与其他任何我尝试过的哈希函数一样少碰撞。简短而精炼。 - Jimbly
41
太酷了!唯一的问题是这会污染 String 的原型,增加一个非 Ecmascript 的方法。我建议将其重写为独立的函数,可以将它放在你的工具库中。 - Husky
7
另一个问题是,因为他在循环中忘记了使用 var 关键字,导致创建了一个全局变量 i。但这些问题可以很容易地被解决。 - Stijn de Witt
24
微优化:移除 if (this.length == 0) {return hash} 块,因为它是多余的(for 循环在长度为正时执行,否则默认返回0)。我是否漏掉了什么? - Eugen Mihailescu
14
一行代码:Array.from(str).reduce((hash, char) => 0 | (31 * hash + char.charCodeAt(0)), 0) (其中 str 是字符串) - Mingwei Samuel
显示剩余16条评论

20

14
base64编码后的字符串长度基本上与原始字符串相同;我希望得到一个更短的东西,比如哈希。 - mjs
11
顺便说一下,Base64编码后的长度比输入数据还要长。为了更清楚地阐明这一点。 - My1

6

简单对象哈希器:

(function () {
    Number.prototype.toHex = function () {
        var ret = ((this<0?0x8:0)+((this >> 28) & 0x7)).toString(16) + (this & 0xfffffff).toString(16);
        while (ret.length < 8) ret = '0'+ret;
        return ret;
    };
    Object.hashCode = function hashCode(o, l) {
        l = l || 2;
        var i, c, r = [];
        for (i=0; i<l; i++)
            r.push(i*268803292);
        function stringify(o) {
            var i,r;
            if (o === null) return 'n';
            if (o === true) return 't';
            if (o === false) return 'f';
            if (o instanceof Date) return 'd:'+(0+o);
            i=typeof o;
            if (i === 'string') return 's:'+o.replace(/([\\\\;])/g,'\\$1');
            if (i === 'number') return 'n:'+o;
            if (o instanceof Function) return 'm:'+o.toString().replace(/([\\\\;])/g,'\\$1');
            if (o instanceof Array) {
                r=[];
                for (i=0; i<o.length; i++) 
                    r.push(stringify(o[i]));
                return 'a:'+r.join(';');
            }
            r=[];
            for (i in o) {
                r.push(i+':'+stringify(o[i]))
            }
            return 'o:'+r.join(';');
        }
        o = stringify(o);
        for (i=0; i<o.length; i++) {
            for (c=0; c<r.length; c++) {
                r[c] = (r[c] << 13)-(r[c] >> 19);
                r[c] += o.charCodeAt(i) << (r[c] % 24);
                r[c] = r[c] & r[c];
            }
        }
        for (i=0; i<r.length; i++) {
            r[i] = r[i].toHex();
        }
        return r.join('');
    }
}());

这里的关键是字符串化器,它可以将任何对象转换为唯一的字符串。hashCode接着对该对象进行哈希运算,将字符串化后的对象字符组合在一起。
如果要额外加分,则需导出字符串化器并创建解析器。

有没有不使用JSON.stringify的原因? - Jehan
2
2012年3月。当时我无法确认某个浏览器是否能正确地使用JSON。此外,JSON会丢掉一些函数,因此如果你使用JSON作为字符串转换器,则这些函数将不能被哈希化。 - Fordi

2
// Simple but unreliable function to create string hash by Sergey.Shuchkin [t] gmail.com
// alert( strhash('http://www.w3schools.com/js/default.asp') ); // 6mn6tf7st333r2q4o134o58888888888
function strhash( str ) {
    if (str.length % 32 > 0) str += Array(33 - str.length % 32).join("z");
    var hash = '', bytes = [], i = 0, j = 0, k = 0, a = 0, dict = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','1','2','3','4','5','6','7','8','9'];
    for (i = 0; i < str.length; i++ ) {
        ch = str.charCodeAt(i);
        bytes[j++] = (ch < 127) ? ch & 0xFF : 127;
    }
    var chunk_len = Math.ceil(bytes.length / 32);   
    for (i=0; i<bytes.length; i++) {
        j += bytes[i];
        k++;
        if ((k == chunk_len) || (i == bytes.length-1)) {
            a = Math.floor( j / k );
            if (a < 32)
                hash += '0';
            else if (a > 126)
                hash += 'z';
            else
                hash += dict[  Math.floor( (a-32) / 2.76) ];
            j = k = 0;
        }
    }
    return hash;
}

18
此脚本将使用以下变量污染全局作用域:jka。原因是因为它们不是var语句的一部分,仅仅是被评估为var i表达式的一部分。应该使用 var i, j, k, a; i = j = k = a = 0; - Niet the Dark Absol
6
是的,这段JS代码写得不太好,但它似乎直接回答了楼主的问题,并提供了示例代码。谢谢! - mkoistinen

2

уюІуюІУ┐ЎСИфJavaScriptуџёMD5т«ъуј░сђѓт«ЃТў»BSDУ«ИтЈ»У»Ђ№╝їжЮътИИТўЊС║јСй┐ућесђѓСЙІтдѓ№╝џ

md5 = hex_md5("message to digest")

我已经找到了那个,它可以工作,但我希望有更小更简单的东西。另外,“消息摘要”不是hex_md5(message)的结果吗? - mjs
1
是的,digest 是结果,参数是您想要摘要的消息 - 因此是“要被摘要的消息”。 - jsalonen
你希望以何种方式简化实现呢?那个实现是一个不到400行的简单JavaScript文件。你只是想要一个单一的函数吗? - jsalonen
现在我更明白了;以前你有类似 hex_md5("message_digest") = "fb6cecc85a100197ae3ad68d1f9f2886" 的内容,对吧?(我找不到你回答的修订版本。) - mjs
是的,我纠正了那个问题。但你能详细说明一下简洁性问题吗?你希望这个库以哪种方式更简单? - jsalonen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接