原生JavaScript实现AJAX与JSONP跨域请求详解

作者:闭包旅人时间:2026-10-21 21:36:43浏览:557

本文详细讲解如何使用原生JavaScript实现AJAX请求及JSONP跨域方案。通过封装XMLHttpRequest对象,逐步演示实例化、发送请求及处理readyState状态的全过程。针对浏览器同源策略限制,深入解析JSONP利用script标签src属性实现跨域的原理、实现步骤及其安全性与局限性,并提供完整的原生代码示例。

原生JavaScript实现AJAX与JSONP跨域请求详解

在前端开发中,与后端进行数据交互时,许多开发者倾向于使用jQuery封装好的AJAX方法,因其便捷高效。然而,若项目仅需AJAX功能而无需jQuery的其他庞大特性,引入整个库显得冗余且不必要的增加负担。

实际上,使用原生JavaScript实现AJAX并不复杂。本文将深入讲解如何从零开始实现基础的AJAX功能,并进一步探讨如何处理跨域请求的JSONP方案。

AJAX基础原理与实现

一、AJAX基础原理

AJAX技术的核心在于XMLHttpRequest对象,它是实现异步数据交换的关键组件。

一个标准的AJAX请求流程通常包含以下关键步骤:

  • 首先,需要实例化XMLHttpRequest对象,创建请求实例。
  • 其次,配置并连接服务器,指定请求的URL及方法。
  • 接着,发送构建好的请求数据至服务器端。
  • 最后,监听并接收服务器返回的响应数据,进行后续处理。

为了简化调用,我将AJAX请求逻辑封装为一个名为ajax()的函数,该函数接收一个配置对象params作为参数,以支持灵活的请求配置。


function ajax(params) {  
 params = params || {};  
 params.data = params.data || {};  
 // 判断是ajax请求还是jsonp请求
 var json = params.jsonp ? jsonp(params) : json(params);  
 // ajax请求  
 function json(params) {  
  // 请求方式,默认是GET
  params.type = (params.type || 'GET').toUpperCase(); 
  // 避免有特殊字符,必须格式化传输数据 
  params.data = formatParams(params.data);  
  var xhr = null;  
  // 实例化XMLHttpRequest对象  
  if(window.XMLHttpRequest) {  
   xhr = new XMLHttpRequest();  
  } else {  
   // IE6及其以下版本  
   xhr = new ActiveXObjcet('Microsoft.XMLHTTP');  
  }; 
     // 监听事件,只要 readyState 的值变化,就会调用 readystatechange 事件
  xhr.onreadystatechange = function() { 
   // readyState属性表示请求/响应过程的当前活动阶段,4为完成,已经接收到全部响应数据
   if(xhr.readyState == 4) {  
    var status = xhr.status; 
    // status:响应的HTTP状态码,以2开头的都是成功
    if(status >= 200 && status < 300) {  
     var response = ''; 
     // 判断接受数据的内容类型 
     var type = xhr.getResponseHeader('Content-type');  
     if(type.indexOf('xml') !== -1 && xhr.responseXML) {  
      response = xhr.responseXML; //Document对象响应  
     } else if(type === 'application/json') {  
      response = JSON.parse(xhr.responseText); //JSON响应  
     } else {  
      response = xhr.responseText; //字符串响应  
     }; 
     // 成功回调函数 
     params.success && params.success(response);  
    } else {  
     params.error && params.error(status);  
    }  
   };  
  }; 
  // 连接和传输数据  
  if(params.type == 'GET') {
   // 三个参数:请求方式、请求地址(get方式时,传输数据是加在地址后的)、是否异步请求(同步请求的情况极少);
   xhr.open(params.type, params.url + '?' + params.data, true);  
   xhr.send(null);  
  } else {  
   xhr.open(params.type, params.url, true);  
   //必须,设置提交时的内容类型  
   xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); 
   // 传输数据 
   xhr.send(params.data);  
  }  
 } 
 //格式化参数  
 function formatParams(data) {  
  var arr = [];  
  for(var name in data) { 
   //  encodeURIComponent() :用于对 URI 中的某一部分进行编码
   arr.push(encodeURIComponent(name) + '=' + encodeURIComponent(data[name]));  
  };  
  // 添加一个随机数参数,防止缓存  
  arr.push('v=' + random());  
  return arr.join('&');  
 }
 // 获取随机数  
 function random() {  
  return Math.floor(Math.random() * 10000 + 500);  
 }
}

在上述实现代码中,readyState属性用于表示请求/响应过程所处的当前活动阶段,其不同数值代表不同的状态含义如下:

0:表示初始化状态,此时尚未调用open()方法。

1:表示已启动,open()方法已调用,但send()方法尚未执行。

2:表示请求已发送,send()方法已调用,但尚未接收到任何响应数据。

3:表示正在接收数据,已接收到部分响应数据,但尚未完成。

4:表示请求完成,已接收到全部响应数据,客户端可以安全地使用这些数据。

以下是AJAX请求的具体使用示例,展示了如何配置参数并处理回调。


ajax({  
 url: 'test.php',  // 请求地址
 type: 'POST',  // 请求类型,默认"GET",还可以是"POST"
 data: {'b': '异步请求'},  // 传输数据
 success: function(res){  // 请求成功的回调函数
  console.log(JSON.parse(res));  
 },
 error: function(error) {}  // 请求失败的回调函数
});

JSONP跨域解决方案与原理

二、JSONP跨域解决方案

理解同源策略是解决跨域问题的前提。

AJAX请求之所以面临“跨域”限制,根本原因在于浏览器的同源策略。该策略规定,一个页面的AJAX请求只能获取与该页面具有相同源(或同域)的数据。所谓“同源”,是指协议、域名和端口号三者必须完全一致。例如:

http://example.com 与 https://example.com 被视为不同源,因为协议不同(http与https);

http://localhost:8080 与 http://localhost:1000 被视为不同源,因为端口号不同(8080与1000);

http://localhost:8080 与 https://example.com 则因协议、域名和端口号均不同,完全不属于同一源,无法直接交互。

当尝试进行跨域请求时,浏览器通常会抛出如下错误信息:

XMLHttpRequest cannot load http://ghmagical.com/article/?intro=jsonp%E8%AF%B7%E6%B1%82&v=5520. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.

面对跨域限制,JSONP(JSON with Padding)作为一种常用的解决方案应运而生。

JSONP全称JSON with Padding(填充式JSON或参数式JSON),是一种广泛使用的跨域请求技术。其核心原理是利用HTML中script标签的src属性不受同源策略限制的特性,通过src属性向服务器发送请求。服务器返回JavaScript代码,浏览器接收并直接执行这段代码,这与通过script标签引用外部脚本文件的原理一致。

JSONP由两部分组成:回调函数名和数据。回调函数是响应到达时页面中需要调用的函数,其名称通常在请求中指定。当服务器响应时,会将数据作为参数包裹在回调函数名中,形成类似“callbackName(data)”的字符串返回给客户端。

JSONP的具体请求过程如下:

  • 请求阶段:浏览器动态创建一个script标签,并将其src属性设置为包含回调函数名的API地址(例如 http://example.com/api/?callback=jsonpCallback)。
  • 发送请求:一旦script标签的src属性被赋值,浏览器会自动发起一个GET请求。
  • 数据响应:服务器将待返回的数据与回调函数名拼接(格式如“jsonpCallback({name: 'abc'})”)后返回。浏览器接收到响应后,由于请求源是script标签,因此会直接执行该JavaScript代码,即调用jsonpCallback方法并传入数据参数。

接下来,我们将讲解如何使用原生JavaScript实现JSONP功能。

我们在之前封装的ajax()方法中增加对JSONP的支持,后续会将两者整合。JSONP的配置参数主要多出一个jsonp参数,用于指定回调函数的名称。


function ajax(params) {  
 params = params || {};  
 params.data = params.data || {};  
 var json = params.jsonp ? jsonp(params) : json(params);   
 // jsonp请求  
 function jsonp(params) {  
  //创建script标签并加入到页面中  
  var callbackName = params.jsonp;  
  var head = document.getElementsByTagName('head')[0];  
  // 设置传递给后台的回调参数名  
  params.data['callback'] = callbackName;  
  var data = formatParams(params.data);  
  var script = document.createElement('script');  
  head.appendChild(script);  
  //创建jsonp回调函数  
  window[callbackName] = function(json) {  
  head.removeChild(script);  
  clearTimeout(script.timer);  
  window[callbackName] = null;  
  params.success && params.success(json);  
  };  
  //发送请求  
  script.src = params.url + '?' + data;  
  //为了得知此次请求是否成功,设置超时处理  
  if(params.time) {  
   script.timer = setTimeout(function() {  
    window[callbackName] = null;  
    head.removeChild(script);  
    params.error && params.error({  
     message: '超时'  
    });  
   }, time);  
  }  
 };  
 //格式化参数  
 function formatParams(data) {  
  var arr = [];  
  for(var name in data) {  
   arr.push(encodeURIComponent(name) + '=' + encodeURIComponent(data[name]));  
  };  
  // 添加一个随机数,防止缓存  
  arr.push('v=' + random()); 
  return arr.join('&');  
 }  
 // 获取随机数  
 function random() {  
  return Math.floor(Math.random() * 10000 + 500);  
 }
}

需要注意:由于script标签的src属性仅在首次设置时生效,导致script标签无法复用,因此每次请求完成后必须从DOM中移除该标签,以避免内存泄漏或重复请求。

以下是JSONP请求的使用示例,展示了如何配置回调函数名及处理响应。


ajax({  
  url: 'test',  // 请求地址
 jsonp: 'jsonpCallback', // 采用jsonp请求,且回调函数名为"jsonpCallbak",可以设置为合法的字符串
 data: {'b': '异步请求'},  // 传输数据
 success:function(res){  // 请求成功的回调函数
  console.log(res);  
 },
 error: function(error) {}  // 请求失败的回调函数
});

尽管JSONP实现简单且易于使用,但它存在两个明显的不足之处:

  • JSONP是从其他域加载并执行代码,如果该域不安全,可能会夹带恶意代码。此时,除了完全放弃使用JSONP外,开发者难以采取其他措施进行防范或追责。
  • 确认JSONP请求是否失败较为困难,因为script标签通常不提供类似XMLHttpRequest的错误回调机制,难以准确捕获网络错误。

以上就是本文关于原生JavaScript实现AJAX及JSONP跨域请求的全部内容,希望这些内容能对您的学习或工作有所帮助,欢迎继续支持本站!

作者最新文章