检测外部URL的最快方法

mat*_*e64 25 javascript url location href

什么是最快的方法来检测是否foo='http://john.doe'是外部(与之相比window.location.href)?

Gum*_*mbo 32

如果您认为URL是外部的,如果方案,主机或端口不同,您可以执行以下操作:

function isExternal(url) {
    var match = url.match(/^([^:\/?#]+:)?(?:\/\/([^\/?#]*))?([^?#]+)?(\?[^#]*)?(#.*)?/);
    if (typeof match[1] === "string" && match[1].length > 0 && match[1].toLowerCase() !== location.protocol) return true;
    if (typeof match[2] === "string" && match[2].length > 0 && match[2].replace(new RegExp(":("+{"http:":80,"https:":443}[location.protocol]+")?$"), "") !== location.host) return true;
    return false;
}
Run Code Online (Sandbox Code Playgroud)

  • +1看看这个正则表达式的美.;)就像来自mr.Regex本人!;)huhu (10认同)
  • @roXon:正则表达式实际上是[来自当前的URI的URI](http://tools.ietf.org/html/rfc3986#appendix-B). (2认同)

pse*_*ant 17

我知道正则表达式版本已被接受,但我敢打赌这比正则表达式的复杂性"更快".String.replace很快.

var isExternal = function(url) {
    var domain = function(url) {
        return url.replace('http://','').replace('https://','').split('/')[0];
    };

    return domain(location.href) !== domain(url);
}
Run Code Online (Sandbox Code Playgroud)

更新

我决定对此进行更多的研究,并找到了一种使用正则表达式的更快的方法.

var isExternalRegexClosure = (function(){
    var domainRe = /https?:\/\/((?:[\w\d-]+\.)+[\w\d]{2,})/i;

    return function(url) {
        function domain(url) {
          return domainRe.exec(url)[1];  
        }

        return domain(location.href) !== domain(url);
    }
})();
Run Code Online (Sandbox Code Playgroud)

在IE中,这比String.replace方法稍快.但是在Chrome和Firefox中它的速度大约是其两倍.此外,在封闭内部仅定义一次Regex而不是仅仅在函数内部通常在Firefox中快30%左右.

这是一个jsperf,它检查了确定外部主机名的四种不同方法.

重要的是要注意,即使在旧手机上,我尝试过的每种方法都需要不到1毫秒的时间.因此,除非您正在进行大批量处理,否则性能可能不应该是您的主要考虑因素.

  • 对不起,这是一个可怕的答案.1.你的第一个方法不适用于以斜杠开头的东西.例如,isExternal("/ questions/123456")将返回true.2.你的第二个(正则表达式)方法将在不以http或https开头的任何内容上抛出异常. (3认同)
  • 怎么样`magnet:`或`mailto:`? (2认同)
  • 我宁愿维护这段代码,而不是接受的答案中的正则表达式。做得很好! (2认同)

shs*_*haw 16

我一直在使用psuedosavant的方法,但遇到了一些触发误报的情况,例如无域链接(/about,image.jpg)和锚链接(#about).旧方法也会为不同的协议(httpvs https)提供不准确的结果.

这是我的略微修改版本:

var checkDomain = function(url) {
  if ( url.indexOf('//') === 0 ) { url = location.protocol + url; }
  return url.toLowerCase().replace(/([a-z])?:\/\//,'$1').split('/')[0];
};

var isExternal = function(url) {
  return ( ( url.indexOf(':') > -1 || url.indexOf('//') > -1 ) && checkDomain(location.href) !== checkDomain(url) );
};
Run Code Online (Sandbox Code Playgroud)

以下是一些更新功能的测试:

isExternal('http://google.com'); // true
isExternal('https://google.com'); // true
isExternal('//google.com'); // true (no protocol)
isExternal('mailto:mail@example.com'); // true
isExternal('http://samedomain.com:8080/port'); // true (same domain, different port)
isExternal('https://samedomain.com/secure'); // true (same domain, https)

isExternal('http://samedomain.com/about'); // false (same domain, different page)
isExternal('HTTP://SAMEDOMAIN.COM/about'); // false (same domain, but different casing)
isExternal('//samedomain.com/about'); // false (same domain, no protocol)
isExternal('/about'); // false
isExternal('image.jpg'); // false
isExternal('#anchor'); // false
Run Code Online (Sandbox Code Playgroud)

根据一些基本的jsperf测试,它总体来说更准确,甚至最终会略微加快.如果您.toLowerCase()不进行不区分大小写的测试,则可以进一步加快速度.