这个属于看到就查系列。内容主要转载自:https://blog.csdn.net/weixin_44392418/article/details/88782809。
json 返回的是数据;而 jsonp 返回的是脚本代码(含一个函数调用)。
json 其实就是 JavaScript 中的一个对象,跟 var obj={}
在本质上完全一样,只是在量上可以无限扩展。简单地讲,json 其实就是 JavaScript 中的对象(Object)和数组(Array,其实也是对象)这哥俩你嵌我我嵌你地套上许多层,以此表示出复杂的数据结构。
json 易于人阅读和编写,也易于机器解析和生成,相对网络传输速率较高,功能型网站前后端往往要频繁大量交换数据,json 凭借其强大的表现力和高颜值逐步成为了理想的前后端数据交换语言。前辈 xml 就逐步功成身退了。
同源的前后端数据交换格式确定使用 json 后,问题来了:如果想获取其它网站提供的数据怎么办呢?这也就是跨域读取数据问题。出于安全方面的考虑,只有 img
、script
、iframe
这类可以指定 src
属性的标签有跨域获取数据(图片/脚本/源文件其实都是数据)能力。比如:
1 2 3 4 |
<!--京东商品图片--> <img src="http://img.jd.com/jgsq-productsoa/56615e00N7a475ee6.jpg" /> <!--百度CDN--> <script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script> |
直接获取 json 是行不通了,那有没有其他方法呢?于是 jsonp 就这样被聪明的开发者给发现了。说是发现而非发明,是因为这儿没有涉及到任何新技术,就像发现 ajax 一样。
jsonp 原理是这样的。网站 A 需要获取网站 B 的数据,网站 B 说你要这样,
- 使用 <script src=”http://www.b.com/open.js”></script> 标签先获取到 open.js 文件(数据提供者即网站 B 的责任),里面有所需要的数据;
- 获取到数据后,处理数据的方法名必须命名为 foo(数据请求者即网站 A 的责任和义务)。
这里相当于数据提供者和数据请求者之间建立了一个协议,要求请求者按照规则办事,不遵守就无法按预期获取数据。
open.js
内容:
1 2 3 4 |
// 不直接写成 json 数据 { "name": "B", "age": 23 } 的原因很简单,放在 js 文件中总得合乎语法要求吧 // 这也是为什么协议中明确规定处理数据的方法名必须命名为 foo,因为 B 网站是在假定请求者的脚本中已经定义了 // 数据处理方法 foo 的情况下返回数据的,不然就会报 foo is not defined 错误 foo({ "name": "B", "age": 23 }); |
网站 A 脚本须有:
1 2 3 4 |
function foo(data) { console.log(data); // TODO... } |
拐了个弯,数据总算得到了,网站 A 和网站 B 都高兴。可问题又来了,网站 C 说也需要网站 B 的数据,可网站 C 一看协议,卧槽,foo 这个名字已经在自己脚本文件的各个角落用到过了,变更会导致很多潜在问题。为了避免此情况发生,开发者使用了动态生成 js 文件的方法,例如 php 的版本:
open.php
1 2 3 4 5 6 |
<?php header('Content-type: application/javascript'); $jsonCallback = htmlspecialchars($_REQUEST ['callback']); // 获取请求者自定义的回调函数名 $jsonData ='{ "name": "B", "age": 23 }'; // 待返回的 json 数据 echo $jsonCallback . "(" . $jsonData . ")"; // 输出 jsonp 格式的数据,即一行函数调用语句 ?> |
于是网站 A 用 <script src="http://www.b.com/open.php?callback=foo"></script>
来请求数据,不需要修改任何变量,返回给 A 的脚本文件内容是:
1 |
foo({ "name": "B", "age": 23 }); // 所谓的 jsonp,就是一句函数调用,数据都被包裹传递到参数中了,千万别穿个马甲就不认识了 |
网站 C 就用 <script src="http://www.b.com/open.php?callback=blah"></script>
来请求数据,返回给 C 的脚本文件内容是:
1 |
blah({ "name": "B", "age": 23 }); |
网站 N 就用 <script src="http://www.b.com/open.php?callback=what"></script>
来请求数据,返回给 N 的脚本文件内容是:
1 |
what({ "name": "B", "age": 23 }); |
Problem Solved,大家都取到了期望的数据,并且避免了冲突。
jsonp 全名叫做 json with padding,很形象,就是把 json 对象用符合 js 语法的形式包裹起来以使其它网站可以请求得到,也就是将 json 数据封装成 js 文件。
json 是理想的数据交换格式,但没办法跨域直接获取,于是就将 json 包裹(padding)在一个合法的 js 语句中作为 js 文件传过去。json 是一种轻量级的数据交换格式,jsonp 是一种跨域数据交互协议。这就是 json 和 jsonp 的区别。
json 是想要的东西,jsonp 是为达到这个目的而普遍采用的一种方法,当然最终获得和处理的还是 json。json 是目的而 jsonp 只是手段。json 总会用到,而 jsonp 只有在跨域获取数据才会用到。
理解了 json 和 jsonp 的区别之后,ajax 里的跨域获取数据就很好理解和实现了。同源时候并没有什么特别的,直接取就行,跨域时候需要拐个弯来达到目的。附上 jQuery 中 ajax 请求 json 数据的实例。
同源:
1 2 3 4 5 6 7 |
$.ajax({ url: "persons.json", success: function(data) { console.log(data); // TODO... } }); |
跨域:
1 2 3 4 5 6 7 8 |
$.ajax({ url: "http://www.B.com/open.php?callback=?", dataType: "jsonp", success: function(data) { console.log(data); // TODO... } }); |
jQuery 已把 jsonp 封装进 ajax,很合理,因为毕竟绝大多数的 jsonp 请求都是 ajax,关于 jquery 的 ajax 具体用法请自行百度。另外要注意的一点就是不同的网站提供的数据接口的 $_REQUEST [‘callback’]
中不一定绝对是 callback
也可能是 cb
、cbk
等,具体使用时务必阅读服务端提供的有关接口使用的详细文档。