原生JavaScript实现AJAX、JSONP


Posted in Javascript onFebruary 07, 2017

相信大多数前端开发者在需要与后端进行数据交互时,为了方便快捷,都会选择JQuery中封装的AJAX方法,但是有些时候,我们只需要JQuery的AJAX请求方法,而其他的功能用到的很少,这显然是没必要的。

其实,原生JavaScript实现AJAX并不难,这篇文章将会讲解如何实现简单的AJAX,还有跨域请求JSONP!

一、AJAX

AJAX的核心是XMLHttpRequest。

一个完整的AJAX请求一般包括以下步骤:

  • 实例化XMLHttpRequest对象
  • 连接服务器
  • 发送请求
  • 接收响应数据

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

二、JSONP

同源策略

AJAX之所以需要“跨域”,罪魁祸首就是浏览器的同源策略。即,一个页面的AJAX只能获取这个页面相同源或者相同域的数据。 如何叫“同源”或者“同域”呢?——协议、域名、端口号都必须相同。例如

http://example.com  和  https://example.com 不同,因为协议不同;

http://localhost:8080  和  http://localhost: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就登场了!

JSONP是JSON with Padding(填充式JSON或参数式JSON)的简写,是一种非常常用的跨域请求方式。主要原理是利用了script 标签可以跨域请求的特性,由其 src 属性发送请求到服务器,服务器返回 JavaScript 代码,浏览器接受响应,然后就直接执行了,这和通过 script 标签引用外部文件的原理是一样的。

JSONP由两部分组成:回调函数和数据,回调函数是当响应到来时应该在页面中调用的函数,回调函数的名字一般在请求中指定。当服务器响应时,服务器端就会把该函数和数据拼成字符串返回。

JSONP的请求过程:

  • 请求阶段:浏览器创建一个 script 标签,并给其src 赋值(类似 http://example.com/api/?callback=jsonpCallback)。
  • 发送请求:当给script的src赋值时,浏览器就会发起一个请求。
  • 数据响应:服务端将要返回的数据作为参数和函数名称拼接在一起(格式类似”jsonpCallback({name: 'abc'})”)返回。当浏览器接收到了响应数据,由于发起请求的是 script,所以相当于直接调用 jsonpCallback 方法,并且传入了一个参数。

在这里讲解一下用原生JavaScript如何实现。

依旧是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 标签没法重用,所以每次完成操作之后要移除;

使用实例:

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

虽然JSONP非常简单易用,不过,它也存在两点不足

  • JSONP是从其他域加载代码执行,如果其他域不安全,可能会夹带一些恶意代码,而此时除了完全放弃JSONP调用之外,没办法追究
  • 要确认JSONP请求是否失败并不容易

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持三水点靠木!

Javascript 相关文章推荐
xmlHTTP实例
Oct 24 Javascript
Extjs学习笔记之一 初识Extjs之MessageBox
Jan 07 Javascript
基于JQuery的Pager分页器实现代码
Jul 17 Javascript
新浪微博字数统计 textarea字数统计实现代码
Aug 28 Javascript
jQuery取id有.的值的方法
May 21 Javascript
jquery实现清新实用的网页菜单效果
Aug 28 Javascript
JS获取数组最大值、最小值及长度的方法
Nov 24 Javascript
JS 实现倒计时数字时钟效果【附实例代码】
Mar 30 Javascript
vue2.0实现导航菜单切换效果
May 08 Javascript
浅谈Node.js ORM框架Sequlize之表间关系
Jul 24 Javascript
web前端开发中常见的多列布局解决方案整理(一定要看)
Oct 15 Javascript
解决JS表单验证只有第一个IF起作用的问题
Dec 04 Javascript
[原创]SyntaxHighlighter自动识别并加载脚本语言
Feb 07 #Javascript
javascript表达式和运算符详解
Feb 07 #Javascript
利用jQuery实现滑动开关按钮效果(附demo源码下载)
Feb 07 #Javascript
原生js和css实现图片轮播效果
Feb 07 #Javascript
bootstrap输入框组使用方法
Feb 07 #Javascript
angularjs使用directive实现分页组件的示例
Feb 07 #Javascript
Bootstrap下拉菜单样式
Feb 07 #Javascript
You might like
解析thinkphp中的M()与D()方法的区别
2013/06/22 PHP
php设计模式之委托模式
2016/02/13 PHP
深入解析PHP中SESSION反序列化机制
2017/03/01 PHP
php连接MSsql server的五种方法总结
2018/03/04 PHP
php慢查询日志和错误日志使用详解
2021/02/27 PHP
Javascript 刷新全集常用代码
2009/11/22 Javascript
js获取下拉列表框中的value和text的值示例代码
2014/01/11 Javascript
用javascript将数据导入Excel示例代码
2014/09/10 Javascript
javascript中setInterval的用法
2015/07/19 Javascript
jQuery使用$.each遍历json数组的简单实现方法
2016/04/18 Javascript
JavaScript仿淘宝页面图片滚动加载及刷新回顶部的方法解析
2016/05/24 Javascript
利用jQuery实现CheckBox全选/全不选/反选的简单代码
2016/05/31 Javascript
input 禁止输入特殊字符的四种实现方式
2016/08/24 Javascript
AngularJS 单元测试(一)详解
2016/09/21 Javascript
Bootstrap源码解读导航条(7)
2016/12/23 Javascript
微信小程序开发搜索功能实现(前端+后端+数据库)
2020/03/04 Javascript
JavaScript实现栈结构Stack过程详解
2020/03/07 Javascript
Vue+scss白天和夜间模式切换功能的实现方法
2021/01/05 Vue.js
[02:41]DOTA2亚洲邀请赛小组赛第三日 赛事回顾
2015/02/01 DOTA
[03:07]【DOTA2亚洲邀请赛】我们,梦开始的地方
2017/03/07 DOTA
Python查询IP地址归属完整代码
2017/06/21 Python
Python3实现抓取javascript动态生成的html网页功能示例
2017/08/22 Python
Python异常处理操作实例详解
2018/05/10 Python
对Python中Iterator和Iterable的区别详解
2018/10/18 Python
python之当你发现QTimer不能用时的解决方法
2019/06/21 Python
python实现图像全景拼接
2020/03/27 Python
解决matplotlib.pyplot在Jupyter notebook中不显示图像问题
2020/04/22 Python
python入门教程之基本算术运算符
2020/11/13 Python
python 获取剪切板内容的两种方法
2020/11/28 Python
个人简历中的自我评价范例
2013/10/29 职场文书
数控技术专科生自我评价
2014/01/08 职场文书
司法所长先进事迹
2014/06/02 职场文书
房屋认购协议书
2015/01/29 职场文书
云台山导游词
2015/02/03 职场文书
入党转正介绍人意见
2015/06/03 职场文书
基于Python实现股票收益率分析
2022/04/02 Python