一文带你掌握 JSONP:从 Script 标签到手写实现

一、JSONP 是什么?用来做什么?

JSONP(JSON with Padding)诞生于 CORS 尚未普及 的年代,是前端解决 "跨域 GET 请求" 的鼻祖级方案。核心思想:

利用 <script> 标签没有同源限制的特性,让服务器把数据"包"成一段 JavaScript 函数调用返回,浏览器执行后即可拿到数据。

  • 只能发 GET
  • 兼容 IE6+
  • 无需任何浏览器插件或 CORS 配置

在现代前端,JSONP 已逐渐被 CORS 取代,但仍在 老旧系统、第三方统计脚本、CDN 回调 等场景活跃,同时也是 面试常考题


二、Script 标签及其属性回顾

属性 作用 对 JSONP 的影响
src 发起 GET 请求加载外部 JS 核心字段,承载接口地址 + 查询参数
async 异步加载,不保证执行顺序 默认行为,JSONP 无需顺序
defer 异步但 DOM 后再执行 一般不用,防止延迟
crossorigin 开启 CORS 错误详情 JSONP 不需要,否则报错
onload / onerror 监听加载成功/失败 可用来做 超时/异常 处理

关键特性

  1. <script src="xxx"> 不受同源限制
  2. 下载完成后立即在全局作用域执行
  3. 不会把响应文本暴露给 JS,只能靠"执行后的副作用"拿数据

三、Callback 是怎么传递与执行的?

① 传递:前端 → 后端

  1. 前端生成全局唯一 函数名(如 jsonp_1710000000000

  2. 把函数名作为 GET 查询参数 拼到 script 的 src:

    bash 复制代码
    https://api.example.com/jsonp?callback=jsonp_1710000000000&id=123
  3. window 上挂同名函数:

    js 复制代码
    window[jsonp_1710000000000] = function (data) { /* 处理数据 */ };

② 执行:后端 → 浏览器

  1. 服务器读取 req.query.callback(即 jsonp_1710000000000

  2. 把数据包进该函数名,返回一段可执行 JS

    http 复制代码
    Content-Type: text/javascript

    响应体:

    js 复制代码
    jsonp_1710000000000({"name": "jsonp-demo"});
  3. 浏览器下载完后立即在全局作用域执行 上述代码 →
    函数被调用,参数即为数据,副作用完成

③ 清理:前端自己

执行完立即 delete window[jsonp_1710000000000] 并移除 <script>,防止堆积。


四、手写一个简洁版 JSONP(含超时 + 错误)

js 复制代码
function jsonp(url, data = {}, timeout = 7000) {
  return new Promise((resolve, reject) => {
    const cb = `jp_${Date.now()}`;
    const script = document.createElement('script');
    const timer = setTimeout(() => cleanup(reject('timeout')), timeout);

    window[cb] = (data) => cleanup(resolve(data));

    function cleanup(fn) {
      clearTimeout(timer);
      script.remove();
      delete window[cb];
      fn();
    }

    script.onerror = () => cleanup(reject('script error'));
    script.src = `${url}${url.includes('?') ? '&' : '?'}callback=${cb}&${new URLSearchParams(data)}`;
    document.head.appendChild(script);
  });
}

/* 使用 */
jsonp('https://api.example.com/jsonp', { id: 123 })
  .then(console.log)   // { id: '123', name: 'jsonp-demo' }
  .catch(console.error);

五、常见问题与坑

问题 原因 解决
返回纯 JSON 报语法错 <script> 期望 JS 而非 JSON 服务器务必返回 callback(JSON);
无法捕捉 HTTP 状态码 <script> 只有 onload/onerror onerror + 超时做模糊失败处理
只能 GET <script> 天生 GET 换 CORS 或代理
回调名冲突 全局变量重名 使用时间戳+随机数唯一化

六、今天还用 JSONP 吗?

  • 新项目:优先 CORS,简单、标准、支持所有 HTTP 方法
  • 老系统/统计脚本/CDN :JSONP 仍活跃,零配置跨域不可替代
  • 面试 :手写 JSONP 是高频手写题 ,考察 Promise + Script 加载 + 全局回调 综合功底

七、一句话总结

JSONP = <script> 无同源限制 + 服务器包成 JS 函数调用 + 全局回调收数据
"下载即执行,执行即回调"------掌握它,跨域历史就懂了一半!

相关推荐
Crazy_Urus2 小时前
深入解析 React 史上最严重的 RCE 漏洞 CVE-2025-55182
前端·安全·react.js
八荒启_交互动画2 小时前
【基础篇007】GeoGebra工具系列_多边形(Polygon)
前端·javascript
清风扶我腰_直上青天三万里2 小时前
vue框架无痛开发浏览器插件,好用!!本人使用脚手架开发了一款浏览器tab主页加收藏网址弹窗,以后可以自己开发需要的插件了!!
前端
知其然亦知其所以然2 小时前
小米的奇幻编程之旅:当 JavaScript 语法变成了一座魔法城
前端·javascript·面试
webkubor2 小时前
一次 H5 表单事故:100vh 在 Android 上到底坑在哪
前端·javascript·vue.js
是一碗螺丝粉2 小时前
突破小程序5层限制:如何用“逻辑物理分离”思维实现无限跳转
前端·架构
Aniugel2 小时前
Vue2怎么搭建前端性能/错误/行为监控体系
vue.js·面试·监控
神秘的猪头2 小时前
🎉 React 的 JSX 语法与组件思想:开启你的前端‘搭积木’之旅(深度对比 Vue 哲学)
前端·vue.js·react.js
三十_2 小时前
如何正确实现圆角渐变边框?为什么 border-radius 对 border-image 不生效?
前端·css