JSONP跨域原理与原生JavaScript实现详解

作者:青柠时间:2026-10-22 10:17:38浏览:614

本文深入解析JSONP跨域请求的实现原理,利用script标签不受同源策略限制的特性,通过动态创建脚本节点加载数据。文章提供了从基础到完善的原生JavaScript代码示例,涵盖回调函数命名、异步处理及超时错误机制,帮助开发者理解并实现JSONP功能。

JSONP跨域原理与原生JavaScript实现详解

前言

在我参与的项目中,首个涉及前后端分离架构,前端静态资源部署于独立域名,通过接口获取数据以完成页面渲染等交互操作。

关于跨域解决方案,常见手段包括JSONP和CORS。JSONP主要侧重于前端实现,属于前端的一种Hack技巧;而CORS则更依赖后端配置,服务端需进行较多设置。

本文将重点解析JSONP的具体实现原理。

JSONP基本原理

基本原理

原理阐述较为直观:HTML中部分标签如img、script、link等不受浏览器同源策略限制。若将所需数据封装在JS文件中,即可突破同源限制。

动态创建Script标签

创建script标签

《高性能JavaScript》一书中提及动态脚本元素,作者指出:

1、文件在元素加入页面时开始下载。该技术核心在于:无论何时启动下载,文件的下载与执行过程均不阻塞页面其他进程。

2、使用动态脚本节点下载文件时,返回的代码通常会立即执行(Firefox和Opera除外,它们会等待此前所有动态脚本节点执行完毕)。当脚本自执行时,此机制运行正常。

引用1确保了JSONP请求不会阻塞主线程,引用2保证了JSONP代码在加载完成后能立即自执行且不出错。

Callback参数处理

callback

服务端接收GET请求后,通常需检查是否存在callback参数。若存在,则需在返回数据外层包裹方法名及括号。例如,发起如下请求:


http://www.a.com/getSomething?callback=jsonp0

服务端将返回如下内容:


jsonp0({code:200,data:{}})

显然,由于该内容包含在动态加载的Script标签中,因此这是一段自执行代码,仅调用了一个函数——jsonp0。

当然,有执行必先创建,否则将报错。创建步骤需在调用前执行。

基础JSONP实现代码

具体实现如下:


function jsonp (url, successCallback, errorCallback, completeCallback) {

 // 声明对象,需要将函数声明至全局作用域
 window.jsonp0 = function (data) {
  successCallback(data);
  if (completeCallback) completeCallback();
 }
 // 创建script标签,并将url后加上callback参数
 var 
  script = document.createElement('script')
  , url = url + (url.indexOf('?') == -1 ? '?' : '&') + 'callback=jsonp0'
 ;
 script.src = url;
 document.head.parentNode.insertBefore(script, document.head);
 // 等到script加载完毕以后,就会自己执行
}

上述代码基本完成了JSONP方法的核心逻辑。此时,jsonp0是我们声明好的函数,若服务端正常回传,则执行jsonp0函数,其中的successCallback回调也随之执行。

完善JSONP功能

完善一下

实际场景中,通常存在多个JSONP请求同时调用的情况,

既然jsonp0能满足需求,为何常看到jsonp1、jsonp2等依次累加的代码呢?

这是因为请求可能是异步进行的。在第一次执行jsonp方法时,window.jsonp0指向函数A,此时加载JS文件;若在JS未加载完毕时再次调用jsonp方法,window.jsonp0将指向函数B。待两次JS加载完毕后,均会执行第二次的回调。

因此,需对callback名称进行区别处理,采用累加方式即可满足需求。

处理多请求与错误回调

修改代码:


var jsonpCounter = 0;
function jsonp (url, successCallback, errorCallback, completeCallback) {
 
 var jsId = 'jsonp' + jsonpCounter++;
 
 // 声明对象,需要将函数声明至全局作用域
 window[jsId] = function (data) {
  successCallback(data);
  if (completeCallback) completeCallback();
  clean();
 }
 // 创建script标签,并将url后加上callback参数
 var 
  script = document.createElement('script')
  , url = url + (url.indexOf('?') == -1 ? '?' : '&') + 'callback=' + jsId
 ;
 script.src = url;
 document.head.parentNode.insertBefore(script, document.head);
 // 等到script加载完毕以后,就会自己执行
 
 //在执行完我们这个方法以后,会有很多script标签出现在head之前,我们需要手动的删除掉他们。
 function clean () {
  script.parentNode.removeChild(script);
  window[jsId] = function(){};
 }
}

加入累加和清理逻辑后,还需处理错误回调。通常请求JSONP时会设定超时时间,若超时则抛出异常。

实现如下:


var jsonpCounter = 0;
function jsonp (url, successCallback, errorCallback, completeCallback, timeout) {
 // 略去上面写过的代码
 var 
  timeout = timeout || 10000
  , timer
 ;
 if (timeout) {
  timer = setTimeout(function () {
   if (errorCallback) {
    errorCallback(new Error('timeout'));
   }
   clean();
  }, timeout)
 }
 
 function clean () {
  script.parentNode.removeChild(script);
  window[jsId] = function(){};
  if (timer) clearTimeout(timer);
 }
}

至此,基本完成了JSONP的全部功能,剩余部分可能需进行兼容性修改,方可视为完整的JSONP方法。

REFER

《高性能JavaScript》

npm上的一个jsonp实现,JSONP

以上这篇基于JSONP原理解析(推荐)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持本站。

作者最新文章