检测外部URL的最快方法

时间:2011-06-04 17:36:59

标签: javascript url location href

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

9 个答案:

答案 0 :(得分:29)

如果您认为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;
}

答案 1 :(得分: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);
}

更新

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

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

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

Here is a jsperf检查了确定外部主机名的四种不同方法。

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

答案 2 :(得分:16)

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

这是我稍微修改过的版本:

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

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

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
根据一些基本的jsperf tests,总的来说它更准确,甚至可能会稍快一些。如果您不使用.toLowerCase()进行不区分大小写的测试,则可以加快速度。

答案 3 :(得分:4)

pseudosavant的回答并不适合我,所以我改进了它。

var isExternal = function(url) {
    return !(location.href.replace("http://", "").replace("https://", "").split("/")[0] === url.replace("http://", "").replace("https://", "").split("/")[0]);   
}

答案 4 :(得分:1)

我必须建立在假性和Jon的答案之上,因为我还需要捕获以" //"开头的网址案例。和不包含子域的URL。这对我有用:



Q = quad(@(x) F(x,y), 0, 2)

var getDomainName = function(domain) {
    var parts = domain.split('.').reverse();
    var cnt = parts.length;
    if (cnt >= 3) {
        // see if the second level domain is a common SLD.
        if (parts[1].match(/^(com|edu|gov|net|mil|org|nom|co|name|info|biz)$/i)) {
            return parts[2] + '.' + parts[1] + '.' + parts[0];
        }
    }
    return parts[1]+'.'+parts[0];
};
var isExternalUrl = function(url) {
	var curLocationUrl = getDomainName(location.href.replace("http://", "").replace("https://", "").replace("//", "").split("/")[0].toLowerCase());
	var destinationUrl = getDomainName(url.replace("http://", "").replace("https://", "").replace("//", "").split("/")[0].toLowerCase());
	return !(curLocationUrl === destinationUrl)
};

$(document).delegate('a', 'click', function() {
	var aHrefTarget = $(this).attr('target');
	if(typeof aHrefTarget === 'undefined')
		return;
	if(aHrefTarget !== '_blank')
		return;  // not an external link
	var aHrefUrl = $(this).attr('href');
	if(aHrefUrl.substr(0,2) !== '//' && (aHrefUrl.substr(0,1) == '/' || aHrefUrl.substr(0,1) == '#'))
		return;  // this is a relative link or anchor link
	if(isExternalUrl(aHrefUrl))
		alert('clicked external link');
});




答案 5 :(得分:1)

出于我的目的,我只是对shshaw的答案进行了一些修改,以验证链接是否为空或只是一个字符(假设它是#'#'),原始答案方法返回误报。这是出于我的目的,通过添加一些FA图标向用户表明他们将离开我的页面。

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

function isExternal(url) {
    // verify if link is empty or just 1 char + original answer
    return (url.length > 1 && url.indexOf(':') > -1 || url.indexOf('//') > -1 ) && checkDomain(location.href) !== checkDomain(url);
};

// add some icon to external links (function is called in an init method)
function addExternalLinkIcon(){
    $("a[href]").each(function(i,ob){
        // we check it
        if(isExternal($(ob).attr("href"))){
            // then add some beauty if it's external
            // (we assume Font Awesome CSS and font is loaded for my example, of course :-P)
            $(ob).append(" <i class='fa fa-external-link'></i> ");
        }
    });
}

答案 6 :(得分:0)

不应该

function is_external( url ) {
    return url.match( /[a-zA-Z0-9]*:\/\/[^\s]*/g ) != null;
}

诀窍?不适用于绝对(内部)网址。

答案 7 :(得分:0)

主要问题是如何解析URL,并获取我们的主机名。 可以通过以下方式完成:

var _getHostname = function(url) {
  var parser = document.createElement('a');
  parser.href = url;

  return parser.hostname;
}

var isExternal = (_getHostname(window.location.href) !== _getHostname('http://john.doe'));

或者您可以使用is-url-external模块。

var isExternal = require('is-url-external');
isExternal('http://john.doe'); // true | false 

答案 8 :(得分:-1)

您可以简单地使用使用npm软件包is-internal-link

安装

npm install --save is-internal-link

用法

import { isInternalLink } from "is-internal-link"
isInternalLink('https://www.google.com') // false
isInternalLink('/page1') // true

我通常也会这样反应

import React from 'react'

import { Link as ReactRouterLink} from 'react-router-dom'
import { isInternalLink } from 'is-internal-link'

const Link = ({ children, to, activeClassName, ...other }) => {
  if (isInternalLink(to)) {
    return (
      <ReactRouterLink to={to} activeClassName={activeClassName} {...other}>
        {children}
      </ReactRouterLink>
    )
  }
  return (
    <a href={to} target="_blank" {...other}>
      {children}
    </a>
  )
}

export default Link

免责声明:我是这个库的作者