1.这里所说的js跨域是指不同域之间通过js进行数据传输或通信,比如使用ajax向不同域请求数据,或者通过js获取页面中不同域的frame(iframe)中的数据。只要协议、域名、端口中的任何一项不同,就认为是不同的域。(1)下表是相对于http://store.company.com/dir/page.html的同源检测结果:解决跨域问题,可以采用以下方法:1.通过jsonp域在js中,当我们直接使用XMLHttpRequest去请求不同域的数据时,是不允许的。但是可以在页面引入不同域的js脚本文件。script标签中的src属性是用来补全的。jsonp就是利用这个特性来实现的。比如有一个a.html页面,里面的代码需要使用ajax获取异域的json数据。假设json数据地址是http://example.com/data.php,那么a.html中的代码可以是这样的:我们看到地址后面有一个回调参数,用来获取数据。按照惯例,使用这个参数名称,但如果使用其他参数也是一样的。当然,如果获取数据的jsonp地址页面不在你的控制范围内,就得按照数据提供方规定的格式进行操作。因为是作为js文件导入的,所以http://example.com/data.php一定会返回一个可执行的js文件,所以这个页面的php代码可能是这样的:那个页面最终的输出结果是:dosomething(['a','b','c']);再比如,php代码也可能是这样的:先连接一个数据库,然后从中获取一个名为chat的表,然后循环输出。该页面最终输出的就是我们需要的json数据:var_obj={"rows":[{"id":0,"uName":"李四","pwd":"123456","message":“他说”,……}]}里面还有很多就不一一列举了。所以通过http://example.com/data.php?callback=dosomething得到的js文件就是我们之前定义的dosomething函数,它的参数就是我们需要的json数据,这样我们就可以跨域得到我们需要的了数据。这样jsonp的原理就很清楚了。通过script标签引入一个js文件。js文件加载成功后,会执行我们在url参数中指定的函数,并将我们需要的json数据作为参数传入。所以jsonp需要服务端页面的相应配合。了解了jsonp跨域的原理后,我们就可以使用js动态生成跨域操作的脚本标签,而不用手动编写那些脚本标签。如果你的页面使用了jquery,那么通过它的封装方式进行jsonp操作是非常方便的。原理是一样的,只是我们不需要手动插入脚本标签和定义回调函数。jquery会自动生成一个全局函数来替换callback=?中的问号,然后在拿到数据后自动销毁,实际上起到了一个临时代理函数的作用。$.getJSON方法会自动判断是否跨域。如果不是跨域,则调用普通的ajax方法;如果是跨域,会以异步加载js文件的形式调用jsonp回调函数。2、跨子域浏览器通过修改document.domain实现同源策略。其中一个限制是在第一种方法中,我们不能通过ajax请求来自不同来源的文档。它的第二个限制是浏览器中不同域的框架之间不能进行js交互。需要注意的一点是,不同的框架(parent-childorpeers)可以互相获取对方的window对象,但是不好的是不能使用获取到的window对象的属性和方法(html5中的postMessage方法是一个exception,有些浏览器比如ie6也可以使用top,parent等几个属性),总之只能得到一个window对象,几乎没什么用。比如有一个页面,它的地址是http://www.example.com/a.html,这个页面里面有一个iframe,它的src是http://example.com/b.html,显然,这个页面和里面的iframe在不同的域,所以我们无法通过在页面中写js代码来获取iframe中的内容:这时候document.domain就可以派上用场了,我们只需要把http文档可以将://www.example.com/a.html和http://example.com/b.html这两个页面的.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:在http://example.com/b.html页面也设置document.domain,这也是必须的,虽然这个文档域是example.com,但是还是要显示document.domain的值:这样我们就可以通过js访问iframe中的各种属性和对象了。但是,如果想在http://www.example.com/a.html页面直接通过ajax请求http://example.com/b.html页面,即使设置了相同的document.domain,还是不行,所以修改document.domain的方法只适用于不同子域的frame之间的交互。如果想通过ajax与不同子域的页面进行交互,除了使用jsonp之外,还可以使用一个隐藏的iframe作为代理。原理是让这个iframe加载一个和你要通过ajax获取数据的目标页面同域的页面,这样这个iframe中的页面就可以正常使用ajax获取你想要的数据,然后通过我们的方法刚才说的修改document.domain可以让我们通过js完全控制iframe,这样我们就可以让iframe发送一个ajax请求,然后我们就可以拿到接收到的数据了。3、使用window.name跨域window对象有一个name属性,它有一个特点:即在一个窗口(window)的生命周期中,该window加载的所有页面共享一个window.name,每个page对window.name有读写权限,window.name在一个window加载的所有页面中是持久化的,不会在加载新页面时被重置。例如:有一个页面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页面,需要使用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同域。网页。否则按照前面说到的同源策略,a.html是无法访问到iframe中的window.name属性的。这就是整个跨域过程。看a.html页面的代码:上面的代码只是最简单的原理演示代码。可以用js封装上面的过程,比如动态创建iframe,动态注册各种事件等,当然为了安全起见,get完数据后,作为代理的iframe也可以销毁。网上也有很多类似的现成代码,有兴趣的可以找找看。通过window.name跨域,就这样。4、使用HTML5中新引入的window.postMessage方法跨域传输数据。window.postMessage(message,targetOrigin)方法是HTML5新引入的功能。您可以使用它向其他窗口对象发送消息,无论该窗口对象是属于同一源还是不同源。目前IE8+、FireFox、Chrome、Opera等浏览器已经支持window.postMessage方法。调用postMessage方法的window对象就是指接收消息的window对象。该方法第一个参数message为要发送的消息,类型只能是字符串;第二个参数targetOrigin用于限定接收消息的窗口对象所在域。如果不想限制域,可以使用通配符*。需要接收消息的窗口对象可以通过监听自身的消息事件获取传入的消息,消息内容保存在事件对象的data属性中。上面说的给其他window对象发送消息,其实是指一个页面有几个frame的情况,因为每个frame都有一个window对象。在讨论第二种方法的时候,我们说过不同领域的框架可以互相获取对方的window对象,也可以使用window.postMessage方法。让我们看一个简单的例子。有两个页面,运行a页面后得到的结果:我们看到b页面已经成功收到消息了。使用postMessage跨域传输数据还是比较直观方便的,但是缺点是IE6、IE7不支持,要不要用还是要根据实际需要来决定。结语:除了上述方法外,还有flash、在服务器设置代理页面等跨域方法,这里不再介绍。以上四种方法可根据项目实际情况选择应用。个人认为window.name的方法并不复杂,而且几乎可以兼容所有的浏览器。这真是一个极好的跨域方法。
