JSONP 原理详解:跨域实现、jQuery 封装与 JSON/Ajax 区别

作者:青柠

时间:2026-10-29

浏览:556

本文深入解析 JSONP 跨域原理,指出其核心是利用 script 标签不受同源策略限制的特性。详细对比了 JSON 数据格式与 JSONP 协议的区别,以及 Ajax 基于 XMLHttpRequest 与 JSONP 基于动态脚本加载的差异。同时涵盖 jQuery 对 JSONP 的封装机制、回调函数处理及常见使用注意事项。

文章目录

JSONP 概述与背景

前言

初次接触 JSONP 是在两年前,当时负责开发活动页面的抽奖模块,需要从服务端获取概率数据。由于缺乏经验,同事建议使用 Ajax,我便照做;当同事提示将 dataType 修改为 jsonp 时,我也直接修改。活动页面完成后,很长一段时间内未再遇到 JSONP,期间我一直误以为它与 Ajax 紧密相关,是 XMLHttpRequest(XHR)的一种特殊跨域形式。直到一个月前面试中被问及 JSONP 而表现不佳,我才决定深入研究。实际上,JSONP 的原理并不复杂。

JSONP 的应用场景与原理

为什么要用 JSONP?

大家对跨域和同源策略应该不陌生。如果对此感到困惑,不妨从基础概念重新梳理,以便深入浅出地理解。

假设编写了一个 index 页面,其中包含一个请求 JSON 数据的接口(若不了解 JSON,可参考相关简介与用法汇总)。经过简单思考,代码如下:


<script src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
<script type="text/javascript">
 $.ajax({
 url: 'http://localhost/a.json',
 dataType: "json",
 success: function (data) {
  console.log(data);
 }
 })
</script> 
{
 "name": "hanzichi",
 "age": 10
} 

作者将两个文件均放置在 WAMP 服务器的 www 目录下,由于 Ajax 请求未涉及跨域,因此能够完美获取结果:

JSONP 概述与背景

然而,如果 JSON 文件与 index 文件位于不同的域下,即发生跨域(关于跨域概念,可参考 JavaScript 同源策略),情况会如何呢?

尝试在 WAMP 中开启一个新的 Apache 端口(参考多端口访问配置),并将 JSON 文件放置在该端口对应的文件夹下(作者设置端口为 8080,默认端口为 80),尝试发送请求:


<script src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
<script type="text/javascript">
 $.ajax({
 url: 'http://localhost:8080/a.json',
 dataType: "json",
 success: function (data) {
  console.log(data);
 }
 })
</script> 

JSONP 概述与背景

结果显然提示跨域错误!该如何解决?此时 JSONP 便派上用场。

利用 Script 标签实现跨域

神奇的 script 标签

JSONP 与 script 标签密切相关,而与 XHR 或传统意义上的 Ajax 并无直接关联。

回顾上述 index.html 代码,页面引用了百度 CDN 的 jQuery 路径。这种用法已司空见惯,但仔细思考会发现,script 标签本身是完全支持跨域的。没错,JSONP 的核心实现正是利用了 script 标签的跨域能力。因此,我们可以动态生成一个 script 标签,将 JSON 的 URL 赋值给其 src 属性,随后将该标签插入 DOM 中。


<body>
 <script src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
 <script type="text/javascript">
 var s = document.createElement('script');
 s.src = 'http://localhost:8080/a.json';
 document.body.appendChild(s);
 </script>
</body> 

我们创建了一个 script 标签,其内部包裹的内容正是所需的 JSON 数据,但执行时报错如下:

 JSONP 概述与背景

原因在于 JSON 数据并非合法的 JavaScript 语句。最简单的方法是将上述 JSON 数据包裹在一个回调函数中:


<body>
 <script src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
 <script type="text/javascript">
 function jsonpcallback(json) {
  console.log(json);
 }
 var s = document.createElement('script');
 s.src = 'http://localhost:8080/a.json';
 document.body.appendChild(s);
 </script>
</body> 
jsonpcallback({
 "name": "hanzichi",
 "age": 10
}); 

JSONP 概述与背景

此时,a.json 文件的命名并非必须如此,即使将其重命名为 a.js 也不会产生任何问题。

若需与服务器端交互,原理相同。例如与 PHP 交互:


<body>
 <script src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
 <script type="text/javascript">
 function jsonpcallback(json) {
  console.log(json);
 }
 var s = document.createElement('script');
 s.src="http://localhost:8080/test.php?callback=jsonpcallback";
 document.body.appendChild(s);
 </script>
</body> 
<?php
 $jsondata = '{
 "name": "hanzichi",
 "age": 10
 }';
 echo $_GET['callback'].'('.$jsondata.')';
?> 

需要注意的是,JSONP 提供的 URL(即动态生成的 script 标签的 src 属性),无论其表面形式如何,最终返回的都是一段 JavaScript 代码。

jQuery 对 JSONP 的封装与使用

jQuery 对 JSONP 的封装

为了简化开发,jQuery 对 JSONP 进行了封装,并将其集成在 Ajax 方法中。


<script src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
<script type="text/javascript">
 $.ajax({
 url: 'http://localhost:8080/a.json',
 dataType: 'jsonp',
 jsonpCallback: 'CallBack',
 success: function (data) {
  console.log(data);
 }
 });
</script> 
CallBack({
 "name": "hanzichi",
 "age": 10
}); 

上述代码针对的是请求文件中已写死 callback 函数名的情况。由于请求的是 JSON 文件,JSON 本身不是服务器端动态语言,无法进行解析;如果是 PHP 或其他服务器端语言,则无需写死函数名,例如:


<script src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
<script type="text/javascript">
 $.ajax({
 url: 'http://localhost:8080/test.php',
 dataType: 'jsonp',
 success: function (data) {
  console.log(data);
 }
 });
</script> 
<?php
 $jsondata = '{
 "name": "hanzichi",
 "age": 10
 }';
 echo $_GET['callback'].'('.$jsondata.')';
?> 

当然,类似的封装方法还有几种:


// 1
$.getJSON("http://localhost:8080/test.php?callback=?", function(data) { 
 console.log(data);
});
// 2
$.get('http://localhost:8080/test.php', function(data) { 
 console.log(data);
}, 'jsonp'); 

需要注意的是,使用 getJSON 方法时,请求地址 URL 中必须包含 callback=?。因为 jQuery 对该方法进行了封装,默认回调函数变量名并非 callback,导致 PHP 中的 $_GET['callback'] 无法获取变量值。

而一般的 jQuery 方法 URL 中无需指定 callback 参数。对于 jQuery 中的 JSONP,callback 参数是自动添加的。默认情况下,jQuery 生成的 JSONP 请求中 callback 参数形如 callback=jQuery200023559735575690866_1434954892929,这是一种看似随机生成的名称,对应的是 success 处理函数,因此通常无需特别处理。如果要写死 callback 名称,可参考上文。

 JSONP 概述与背景

JSONP 核心原理总结

总结

受同源策略限制,XmlHttpRequest 仅允许请求当前源(域名、协议、端口)的资源。为实现跨域请求,可通过 script 标签实现跨域请求,并在服务端输出 JSON 数据以执行回调函数,从而解决跨域数据请求问题,这就是 JSONP 的核心。

JSONP 原理:

1. 首先在客户端注册一个 callback,并将该 callback 的名字传给服务器。 2. 服务器先生成 JSON 数据,然后以 JavaScript 语法的方式生成一个 function,function 的名字即为传递上来的参数 jsonp。最后将 JSON 数据直接作为入参放置到 function 中,从而生成一段 JavaScript 语法的文档并返回给客户端。 3. 客户端浏览器解析 script 标签,并执行返回的 JavaScript 文档,此时数据作为参数传入客户端预先定义好的 callback 函数中(动态执行回调函数)。

JSON、JSONP 与 Ajax 的区别对比

JSON 和 JSONP 的区别,以及 Ajax 和 JSONP 的区别

JSON 和 JSONP 虽然仅有一个字母之差,但二者之间并无直接关系。

JSON 是一种轻量级的数据交换格式。

JSONP 是一种跨域数据交互协议。

JSON 的优点包括:(1)基于纯文本传递,极其简单;(2)轻量级数据格式,适合互联网传递;(3)易于编写和解析。

Ajax 和 JSONP 的区别:

相同点:都是请求一个 URL。

不同点:Ajax 的核心是通过 XMLHttpRequest 获取内容。

JSONP 的核心则是动态添加 <script> 标签来调用服务器提供的 JavaScript 脚本。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章