1

前言

最近遇到一个需求:用postMessage来实现跨域通信。
其中有一个注意的地方:即接收方来收到消息时需要对发送方的 origin 进行检查,如果不是目标发送方,则什么都不做,这是出于安全考虑推荐的方式。

我手中有的数据有:消息事件中的 event.origin(即当前发送方的源)、目标发送方的url字符串。所以为了进行安全检查,我得先从目标发送方的url字符串中提取出 origin信息。

那么问题来了,怎么提取呢?
——当然用正则表达式最简单!

origin是什么

在介绍提取的正则表达式之前,我们得先搞清楚我们要提取的是url字符串中的哪一部分的字符。我们知道要提取的是origin信息,那origin是什么意思呢?
前端开发经常听到一个词“同源策略”origin即为其中的“源”。一个 URL 具体由哪几部分组成,可以参考这篇文章
而 origin = scheme(协议)+ domain(域名)+port(端口)
例如:
url= "http://baidu.com:8080/pub/new";
origin = "http://baidu.com:8080";

匹配正则表达式

我想用 RegExp.exec()方法来提取 origin。构造一个RegExp对象有两种方法:

  • RegExp(pattern [, flags]), 如 new RegExp("ab+c", "i");

  • /pattern/flags, 如 /ab+c/i;

注意:很多人误认为这2种方法的区别只在于前者 pattern 是一个字符串,我原本也是这么认为的。结果就被这个潜意识坑得很惨。

匹配 origin 的正则 pattern 如下所示,能匹配http/https协议、域名中带“-”、“_”字符如“dx-meituan.dxw_mei.com”。至于具体如何匹配到每一个字符的,请自行参考RegExp对象

^https?://[\w-.]+(:\d+)?

下面将分别用前面介绍的2种方法创建 RegExp 对象,看看会遇到什么坑儿。

  1. RegExp(pattern [, flags])

TEST 0:

var url = "https://dx.sansan.com:8080/test/index";
var origin = new RegExp("^https?://[\w-.]+(:\d+)?","i").exec(url)[0];

结果:null, 即没有找到任何匹配结果

怎么会这样??!!没有问题呀,我重新检查了每一个字符具体匹配过程,确实没问题呀。。。。

TEST 1:

var url = "https://dx.sansan.com:8080/test/index";
var origin = new RegExp("^https?://[\\w-.]+(:\\d+)?",i).exec(url)[0];

结果:https://dx.sansan.com:8080

居然匹配成功了!!!这是个什么鬼?

原来是因为使用new RegExp(pattern [, flags])去创建一个正则对象,pattern 是一个 string 类型,所以 pattern 里面的所有字符都是要匹配的真实字符。如 pattern = “\w”,就是从“\world”中匹配“w”这个字母,而不是匹配 0-9或 A-Z 或 a-z 中的一个字符。所以要想达到后一种效果,就得写成pattern = “\\w”。这也解释了匹配"."这个字符时,不用写成“\.”(因为.是正则中的特殊字符,要想匹配"."字符一般要写成"\.")。

我实验后的结论是:使用new RegExp(pattern [, flags]),pattern中字符就是要匹配的字符,其中单一的“\”会被忽略,如"\w"=="w"; 要想使用正则中的包含“\”的特殊符号(如\w和\d),得写成"\\w"和"\\d"。

  1. /pattern/flags

TEST 0:

var url = "https://dx.sansan.com:8080/test/index";
var origin = /^https?://[\w-.]+(:\d+)?/i.exec(url)[0];

结果:报错 unexpected SyntexError !!

语法错误,这个比较容易排查。发现是"?://"中的"/"与 /pattern/flags 中的"/"冲突了,在这种方式下,"/"字符变成了一个特殊字符。

TEST 1:

var url = "https://dx.sansan.com:8080/test/index";
var origin = /^https?:\/\/[\w-.]+(:\d+)?/i.exec(url)[0];

结果:https://dx.sansan.com:8080

匹配成功了!
所以这种方式匹配时,正则中的特殊符号如\w和\d是生效的,但要想匹配"//",得写成"\/\/"。

结语

所以以前自己的潜意识认知就存在错误。
下面两种方式匹配字符串时,pattern 除了是否是字符串类型,写法上有些字符也是不同的。

  • RegExp(pattern [, flags]), 如 new RegExp("ab+c", "i");

  • /pattern/flags, 如 /ab+c/i;


ruoyiqing
4.9k 声望1.2k 粉丝

如果你问我:我最开心的事是什么?