一文带你掌握 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 函数调用 + 全局回调收数据
"下载即执行,执行即回调"------掌握它,跨域历史就懂了一半!

相关推荐
Peter-Code3 分钟前
Nginx 常用操作整理(前端)
服务器·前端·nginx
怕浪猫9 小时前
2028编程圈很火的10个Skills
算法·面试·github
Java后端的Ai之路10 小时前
06-React-Props
前端·react.js·前端框架
Flynt10 小时前
开源"间谍卫星"说数据全是真的,我把它的免key数据源挨个试了一遍
前端·开源·数据可视化
Csvn11 小时前
渲染管线
前端
程序员大阳11 小时前
副队长CSS教程(33)–圆角与透明
前端·css·透明·圆角
志尊宝11 小时前
Vue3 零基础每日笔记(082):文件上传实战——el-upload 校验、自定义上传、头像预览
前端·javascript·vue.js·笔记·#javascript·#vue.js
鬼手点金12 小时前
opencode-rules 插件
java·开发语言·前端·人工智能·chrome·git·github
柳杉12 小时前
借助 Codex 和可视化工作流,从调研到完整实现,3D 大屏竟然可以这么简单
前端·ai编程·数据可视化
IMPYLH12 小时前
HTML 的 <wbr> 元素
前端·html