js中几种实用的跨域方法原理详解

这里说的js跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同域的框架中(iframe)的数据 。只要协议、域名、端口有任何一个不同,都被当作是不同的域 。
下表给出了相对http://store.company.com/dir/page.html同源检测的结果:

js中几种实用的跨域方法原理详解

文章插图
 
要解决跨域的问题,我们可以使用以下几种方法:
一、通过jsonp跨域
在js中,我们直接用XMLHttpRequest请求不同域上的数据时,是不可以的 。但是,在页面上引入不同域上的js脚本文件却是可以的,jsonp正是利用这个特性来实现的 。
比如,有个a.html页面,它里面的代码需要利用ajax获取一个不同域上的json数据,假设这个json数据地址是http://example.com/data.php,那么a.html中的代码就可以这样:
js中几种实用的跨域方法原理详解

文章插图
 
我们看到获取数据的地址后面还有一个callback参数,按惯例是用这个参数名,但是你用其他的也一样 。当然如果获取数据的jsonp地址页面不是你自己能控制的,就得按照提供数据的那一方的规定格式来操作了 。
因为是当做一个js文件来引入的,所以http://example.com/data.php返回的必须是一个能执行的js文件,所以这个页面的php代码可能是这样的:
js中几种实用的跨域方法原理详解

文章插图
 
最终那个页面输出的结果是:
js中几种实用的跨域方法原理详解

文章插图
 
所以通过http://example.com/data.php?callback=dosomething得到的js文件,就是我们之前定义的dosomething函数,并且它的参数就是我们需要的json数据,这样我们就跨域获得了我们需要的数据 。
这样jsonp的原理就很清楚了,通过script标签引入一个js文件,这个js文件载入成功后会执行我们在url参数中指定的函数,并且会把我们需要的json数据作为参数传入 。所以jsonp是需要服务器端的页面进行相应的配合的 。
知道jsonp跨域的原理后我们就可以用js动态生成script标签来进行跨域操作了,而不用特意的手动的书写那些script标签 。如果你的页面使用jquery,那么通过它封装的方法就能很方便的来进行jsonp操作了 。
js中几种实用的跨域方法原理详解

文章插图
 
原理是一样的,只不过我们不需要手动的插入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 ,在这个页面里面有一个iframe,它的src是http://example.com/b.html, 很显然,这个页面与它里面的iframe框架是不同域的,所以我们是无法通过在页面中书写js代码来获取iframe中的东西的:
js中几种实用的跨域方法原理详解

文章插图
 
这个时候,document.domain就可以派上用场了,我们只要把http://www.example.com/a.html 和 http://example.com/b.html这两个页面的document.domain都设成相同的域名就可以了 。但要注意的是,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:
js中几种实用的跨域方法原理详解


推荐阅读