写在前面
7月11日-8月27日,一个半月过去了,以周为单位的时间过得特别快
每天都忙,却记不清在忙些什么。期间
没有再翻过新书(写博客时偶尔翻过Node和JS,因为内容有些淡忘了)
看了1.5篇散文
Kindle充电2次,但从未使用过
日语学习到第七課结束
周一FEX周刊(有一次是周二发的),周五奇舞周刊(这周来自凹凸实验室的mock淘宝造物节全景很不错)
最近在看什么书,有推荐的吗?
日语书,没有大片时间看书,对日常工作熟练了就有时间了
真是这样吗?为什么没有大片时间?
一.问题简述
请用URL规范的办法取出host,然后判断host,不要按照单个字符判断
场景就是这样,当前页的query string
携带了一个url
参数,我们需要从中解析出hostname
location.hostname
能够取出当前页的主机名,那对于任意一个URL串呢?有非正则方案吗?
之前笔者认为是没有的,记忆中JS API没有提供之类的,以为必须通过正则解析new URL()
出去搓的时候提出了这个问题,三位老司机瞬间给出了3种方案
二.解决方案
标准URL格式为:scheme://domain:port/path?query_string#fragment_id
,简单的正则捕获分分钟解析好
也有一些奇怪的(精心构造的)URL:
http://www.example.com/public/page/2015/index.html?url=http://12.23.34.45/hack.html?http://www.example.com//check.htm
http://www.example.com/public/page/2015/index.html?url=http://www.example.com@12.23.34.45/hack.html
P.S.为了看清楚,url
参数部分没有进行encode
,这样的参数看着就不怀好意
甚至还有一些不合规范的URL,使用一般的正则很难应对,比如:
// 这个东西作何解?
http://www.example.com/what??key=val?&{http://@www.abc.com?query=2#45
// 那这个呢?
http://www.example.com:8899@www.abc.com/what??key=val?&{http://?query=2#45
// 这个?
http://www.example.com:$88;9,9@www.abc.com$/what??key=val?&{http://?query=2#45
//...
1.a标签自动解析URL
var a = document.createElement('a');
a.href = 'http://www.example.com/news.php?id=10#footer';
var div = document.createElement('div');
for (var key in a) {
!(key in div) && console.log(`${key} = ${a[key]}`);
}
输出结果是这样:
// 说明在哪里显示指向的资源,当前窗体、新标签页等等
target =
// 通知UA下载指向的资源
download =
// 点击链接时异步POST指定地址,用于广告统计
ping =
// 表明指向的资源与当前资源的关系,备胎、书签等等
rel =
// 指向的资源的language
hreflang =
// 指向的资源的MIME type
type =
// 请求头referer字段策略,用于保护用户隐私
referrerpolicy =
//
text =
// 已废弃。支持自定义形状,传入一系列坐标点
coords =
// 已废弃。指向的资源的字符编码
charset =
// 已废弃。跳转到指定name的标签
name =
// 已废弃。反向关系,rel的反义词
rev =
// 已废弃。用于指定自定义形状热区
shape =
// 指向的资源的URL,或者URL的#fragment_id部分
href = http://www.example.com/news.php?id=10#footer
origin = http://www.example.com
protocol = http:
username =
password =
host = www.example.com
hostname = www.example.com
port =
pathname = /news.php
search = ?id=10
hash = #footer
这些都是a
标签特有的属性,里面就有我们想要的hostname
,也就是说,a
标签自动完成了URL解析,对于前端来说,这曾经是解析URL最廉价的方式:
var getHostname = function(url) {
var a = document.createElement('a');
a.href = url;
return a.hostname;
};
100%可靠,再复杂的URL也骗不了浏览器
2.JS URL API
var url = new URL('http://www.example.com:$88;9,9@www.abc.com$/what??key=val?&{http://?query=2#45');
for (var key in url) {
console.log(`${key} = ${url[key]}`);
}
Chrome下输出结果:
searchParams = %3Fkey=val%3F
href = http://www.example.com:$88%3B9,9@www.abc.com%24/what?%3Fkey=val%3F#123http://?query=2#45
origin = http://www.abc.com%24
protocol = http:
username = www.example.com
password = $88%3B9,9
host = www.abc.com%24
hostname = www.abc.com%24
port =
pathname = /what
search = ?%3Fkey=val%3F
hash = #123http://?query=2#45
因为这个URL太不合标准,UA处理细节有差异,FF下的结果不同:
"href = http://www%2Eexample%2Ecom:$88%3B9,9@www.abc.com$/what??key=val?&{http://?query=2#45"
"origin = http://www.abc.com$"
"protocol = http:"
username = www%2Eexample%2Ecom
password = $88%3B9,9
host = www.abc.com$
hostname = www.abc.com$
port =
pathname = /what
search = ??key=val?&&
searchParams = %3Fkey=val%3F
hash = #123http://?query=2#45
浏览器确实偷偷提供了URL
类,不是ES5也不是ES6、7标准,目前只是实验性的特性,兼容性如下:
Android4.0 webkitURL
Android4.4 URL
Safari6.0 webkitURL
Chrome32 URL
FF19 URL
IE10 URL
移动端基本可以放心使用,更多兼容性信息请查看URL – Web APIs | MDN
var getHostname = function(url) {
return new URL(url).hostname;
};
3.正则解析
var parseUrl = function(url) {
var urlParseRE = /^\s*(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/;
var matches = urlParseRE.exec(url || "") || [];
return {
href: matches[0] || "",
hrefNoHash: matches[1] || "",
hrefNoSearch: matches[2] || "",
domain: matches[3] || "",
protocol: matches[4] || "",
doubleSlash: matches[5] || "",
authority: matches[6] || "",
username: matches[8] || "",
password: matches[9] || "",
host: matches[10] || "",
hostname: matches[11] || "",
port: matches[12] || "",
pathname: matches[13] || "",
directory: matches[14] || "",
filename: matches[15] || "",
search: matches[16] || "",
hash: matches[17] || ""
};
};
被吓哭了,试一试够不够健壮:
var url = parseUrl('http://www.example.com:$88;9,9@www.abc.com$/what??key=val?&{http://?query=2#45');
for (var key in url) {
console.log(`${key} = ${url[key]}`);
}
输出结果:
href = http://www.example.com:$88;9,9@www.abc.com$/what??key=val?&{http://?query=2#45
hrefNoHash = http://www.example.com:$88;9,9@www.abc.com$/what??key=val?&&
hrefNoSearch = http://www.example.com:$88;9,9@www.abc.com$/what
domain = http://www.example.com:$88;9,9@www.abc.com$
protocol = http:
doubleSlash = //
authority = www.example.com:$88;9,9@www.abc.com$
username = www.example.com
password = $88;9,9
host = www.abc.com$
hostname = www.abc.com$
port =
pathname = /what
directory = /
filename = what
search = ??key=val?&&
hash = #123http://?query=2#45
与FF完全一致,结果可信度很高。那么我们尝试解读一下这个无敌的正则:
/^ #href
\s*
( #hrefNoHash
( #hrefNoSearch
( #domain
([^:\/#\?]+:)? #protocol
(?:
(\/\/) #doubleSlash
( #authority
(?:
( #取结果时$7被跳过了,应该也用非捕获型括号(?:
([^:@\/#\?]+) #username
(?:
\:
([^:@\/#\?]+) #password
)?
)
@
)?
( #host
([^:\/#\?\]\[]+|\[[^\/\]@#?]+\]) #hostname
(?:
\:
([0-9]+) #port
)?
)
)?
)?
)?
( #pathname
(\/?(?:[^\/\?#]+\/+)*) #directory
([^\?#]*) #filename
)
)?
(\?[^#]+)? #search
)
(#.*)? #hash
/
根据上面的分析,第9个左括号应该用非捕获型括号(?:
,取值的时就不用跳过$7
了,如下:
var getHostname = function(url) {
// 第9个小括号改掉了
var urlParseRE = /^\s*(((([^:\/#\?]+:)?(?:(\/\/)((?:(?:([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/;
var matches = urlParseRE.exec(url || "") || [];
return matches[10] || "";
};
看瞎了,正则再见。
三.方案分析
a标签
老司机1前端经验丰富,冷门技巧瞬间解决问题
兼容性没问题(很多年前的技巧了),纯前端方案,简单粗暴疗效确切,a
标签竟然这么强大
更多冷门技巧请查看前端不为人知的一面–前端冷知识集锦,昨天新发现的另一位老前辈,追随之
URL类
老司机2眼界开阔,细节扎实
非标准的URL类都知道,我天天用console也没主意这个,不细心就会少经验,就像超级玛丽
无敌正则
老司机3解决问题经验丰富,资源积累很多
这正则,吓哭了,orz
四.总结
早出晚归,不涨经验,我在忙些什么?
时间碎片化,没有明确的当前时段task,一抬头,又半小时过去了,一转眼,又该开周会了……一个半月过去了,经验条一动不动
长此以往,泯然码农矣(3年工作经历,1年工作经验)