JS跨域解决方案:JSONP、document.domain、window.name与postMessage原理详解
本文详细解析JavaScript中四种实用的跨域通信方案。涵盖利用script标签的JSONP机制、通过修改document.domain解决子域交互、基于window.name持久性的跨域数据传递,以及HTML5标准API window.postMessage的使用。文章结合代码示例说明各方法的原理、适用场景及浏览器兼容性限制,帮助开发者根据实际需求选择最佳跨域策略。
1、通过JSONP跨域
在JavaScript中,直接使用XMLHttpRequest请求不同域的数据会受到限制。然而,在页面中引入不同域的JavaScript脚本文件是被允许的,JSONP正是利用这一特性来实现跨域数据获取的。
例如,假设有一个a.html页面,需要利用Ajax获取不同域上的JSON数据,目标地址为http://example.com/data.php。此时,a.html中的代码可以这样编写:

可以看到,获取数据的地址后附加了一个callback参数。虽然惯例使用此名称,但使用其他名称也是可行的。需要注意的是,如果数据源不是由你自己控制,则必须遵循提供方规定的格式进行操作。
由于该请求被视为引入一个JavaScript文件,因此http://example.com/data.php返回的内容必须是可执行的JavaScript代码。该页面的PHP代码可能如下所示:

最终输出的结果是:

因此,通过访问http://example.com/data.php?callback=dosomething,我们实际上获取了之前定义的dosomething函数,其参数即为我们所需的JSON数据。这样便成功实现了跨域数据获取。
JSONP的原理由此清晰:通过script标签引入一个JS文件,该文件加载成功后会执行URL参数中指定的函数,并将所需的JSON数据作为参数传入。因此,JSONP需要服务器端页面的配合。
了解JSONP原理后,我们可以使用JavaScript动态生成script标签来进行跨域操作,而无需手动编写这些标签。如果页面使用了jQuery,通过其封装的方法可以更方便地进行JSONP操作。

原理相同,只是无需手动插入script标签或定义回调函数。jQuery会自动生成一个全局函数来替换callback=?中的问号,获取数据后会自动销毁该函数,实际上起到了临时代理函数的作用。$.getJSON方法会自动判断是否跨域:若不跨域,则调用普通Ajax方法;若跨域,则以异步加载JS文件的形式调用JSONP回调函数。
2、通过修改document.domain跨子域
浏览器存在同源策略,其限制之一是无法通过Ajax请求不同源的文档。另一限制是不同域的框架间不能进行JS交互。需要注意的是,不同框架(父子或同辈)之间可以获取彼此的window对象,但通常无法使用获取到的window对象的属性和方法(HTML5中的postMessage方法是一个例外,部分旧浏览器如IE6支持top、parent等少数属性)。总之,这相当于只能获取到一个几乎无用的window对象。例如,页面地址为http://www.example.com/a.html,其中包含一个src为http://example.com/b.html的iframe。显然,页面与iframe不同域,因此无法通过页面JS获取iframe中的内容:

此时,document.domain便发挥作用。只需将http://www.example.com/a.html和http://example.com/b.html这两个页面的document.domain设置为相同的域名即可。但需注意,document.domain的设置有限制:只能设置为自身或更高一级的父域,且主域必须相同。例如,a.b.example.com中文档的document.domain可以设为a.b.example.com、b.example.com或example.com中的任意一个,但不能设为c.a.b.example.com(因为这是当前域的子域),也不能设为baidu.com(因为主域不同)。
在页面http://www.example.com/a.html中设置document.domain:

在页面http://example.com/b.html中也设置document.domain,这也是必须的,尽管该文档的domain已经是example.com,但仍需显式设置:

这样,我们就可以通过JS访问到iframe中的各种属性和对象了。
不过,如果想在http://www.example.com/a.html页面中通过Ajax直接请求http://example.com/b.html页面,即使设置了相同的document.domain也是不行的。因此,修改document.domain的方法仅适用于不同子域框架间的交互。若想通过Ajax与不同子域页面交互,除使用JSONP外,还可利用隐藏的iframe作为代理。原理是让该iframe载入与目标Ajax数据源同域的页面,从而正常使用Ajax获取数据,再通过修改document.domain的方法,使JS能完全控制该iframe,进而让iframe发送Ajax请求并获取数据。
3、使用window.name进行跨域
window对象有一个name属性,其特点是:在一个窗口(window)的生命周期内,窗口载入的所有页面都共享一个window.name,每个页面对window.name都有读写权限。window.name的值会持久存在于窗口载入过的所有页面中,不会因新页面的载入而重置。
例如,有一个页面a.html,其代码如下:

再看b.html页面的代码:

a.html页面载入3秒后跳转到b.html页面,结果为:

可以看到,在b.html页面上成功获取到了上一个页面a.html设置的window.name值。如果在之后所有载入的页面中未修改window.name,则所有这些页面获取到的window.name值均为a.html设置的值。当然,如有需要,其中任何一个页面都可以修改window.name的值。需要注意的是,window.name的值只能是字符串形式,大小最大允许2M左右甚至更大,具体取决于不同浏览器,但通常足够使用。
上述示例中使用的a.html和b.html处于同一域,但即使a.html与b.html处于不同域,上述结论同样适用,这也是利用window.name进行跨域的原理。
接下来看具体如何通过window.name跨域获取数据,举例说明。
假设有一个www.example.com/a.html页面,需要通过该页面的JS获取另一个位于不同域上的页面www.cnblogs.com/data.html中的数据。
data.html页面的代码很简单,即为当前window.name设置a.html页面想要得到的数据值。data.html代码:

那么在a.html页面中,如何载入data.html页面呢?显然不能直接在a.html中通过改变window.location来载入data.html,因为我们希望a.html页面不跳转也能得到data.html中的数据。答案是在a.html中使用一个隐藏的iframe充当中间人角色,由iframe获取data.html的数据,然后a.html再获取iframe中的数据。
充当中间人的iframe想要获取data.html通过window.name设置的数据,只需将该iframe的src设为www.cnblogs.com/data.html即可。然后,a.html想要获取iframe中的数据(即iframe的window.name值),还必须将该iframe的src设为与a.html页面同域,否则根据前述同源策略,a.html无法访问iframe里的window.name属性。这就是整个跨域过程。
查看a.html页面的代码:

上述代码仅为最简单的原理演示,你可以使用JS封装上述过程,例如动态创建iframe、动态注册各种事件等。当然,为了安全,获取完数据后还可以销毁作为代理的iframe。网上也有许多类似的现成代码,有兴趣的可以查找。
通过window.name进行跨域,就是这样子的。
4、使用HTML5中新引进的window.postMessage方法进行跨域传送数据
window.postMessage(message,targetOrigin)方法是HTML5新引进的特性,可用于向其他window对象发送消息,无论该window对象是否同源。目前IE8+、Firefox、Chrome、Opera等浏览器已支持window.postMessage方法。
调用postMessage方法的window对象是指接收消息的那个window对象。该方法的第一个参数message为要发送的消息,类型只能为字符串;第二个参数targetOrigin用来限定接收消息的window对象所在的域,如果不想限定域,可以使用通配符*。
需要接收消息的window对象,可通过监听自身的message事件来获取传来的消息,消息内容储存在该事件对象的data属性中。
上述向其他window对象发送消息,实际上是指页面中有多个框架的情况,因为每个框架都有一个window对象。在讨论第二种方法时,我们提到不同域的框架间可以获取对方的window对象,也可以使用window.postMessage方法。下面看一个简单的示例,有两个页面:


我们运行a页面后得到的结果:

可以看到,b页面成功收到了消息。
使用postMessage来跨域传送数据比较直观和方便,但缺点是IE6、IE7不支持,因此是否使用需根据实际需求决定。
到此这篇关于JS中几种实用的跨域方法原理详解的文章就介绍到这了,更多相关JS 跨域方法原理内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!
JavaScript入门书籍避坑:这些书不适合初学者
2026-11-05 16:31:15
JS使用Math.round实现保留两位小数的原理与代码
2026-11-05 15:35:55
JavaScript 原生 Date 对象怎么实现日期加减
2026-11-05 14:30:00
JavaScript函数定义的三种方式:声明、表达式与箭头函数
2026-11-05 13:50:55
JavaScript中escape、encodeURI和encodeURIComponent对空格的处理差异
2026-11-05 12:47:23
