AJAX基础知识及核心原理详解。先后端分离和不分离的优点与弊端

AJAX基础知识及核心原理解读

AJAX基础知识

什么是AJAX?

async javascript and xml 异步的JS和XMLjavascript

XML:可扩展的标记语言

做用:是用来存储数据的(经过本身扩展的标记名称清晰地展现出数据结构)

ajax之因此称为异步的js和xml,主要缘由是:之前最开始使用ajax实现客户端和服务端数据通讯的时候,传输数据的格式通常都是xml格式的数据,咱们把他称之为异步的js和xml(如今通常都是基于JSON格式来进行数据传输的)html

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <student>
        <name>海洋</name>
        <age>10</age>
        <score>
            <deutsch>100</deutsch>
            <IT>100</IT>
            <english>100</english>
        </score>
    </student>
</root>

异步的JS

这里的异步不是说ajax只能基于异步进行请求(虽然建议都是使用异步编程),这里的异步特指的是 局部刷新
局部刷新 VS 全局刷新
全局刷新

在非彻底先后端分离的项目中,前端开发只须要完成页面的制做,而且把一些基础的人机交互效果使用js完成便可,页面中须要动态呈现内容的部分,都是交给后台开发工程师作数据绑定和基于服务器进行渲染的(服务器端渲染)前端

图片描述

【优点】java

  1. 动态展现的数据在页面的源代码中能够看见,有利于SEO优化推广(有利于搜索引擎的收录和抓取)
  2. 从服务器端获取的结果已是解析渲染完成的了,不须要客户端再去解析渲染了,因此页面加载速度快(前提是服务器端处理的速度够快,可以处理过来),因此相似于京东,淘宝这些网站,首屏数据通常都是由服务器端渲染的

【弊端】git

  1. 若是页面中存在实时更新的数据,每一次想要展现最新的数据,页面都要从新刷新一次,这样确定不行,很是耗性能
  2. 都交给服务器端作数据渲染,服务器端的压力太大,若是服务器处理不过来,页面呈现的速度更慢(因此像京东和淘宝这类的网站,除了首屏是服务器端渲染的,其余屏通常都是客户端作数据渲染绑定的)
  3. 这种模式不利于开发,(开发效率低)

局部刷新github

目前市场上大部分项目都是先后端彻底分离的项目(也有非彻底先后端分离的占少数)面试

先后端彻底分离的项目,页面中须要动态绑定的数据是交给客户端完成渲染的ajax

  1. 向服务器端发送AJAX请求
  2. 把从服务器端获取的数据解析处理,拼接成咱们须要展现的HTML字符串
  3. 把拼接好的字符串替换页面中的某一部份内容(局部刷新),页面不须要总体从新加载,局部渲染便可

图片描述

【优点】编程

  1. 咱们能够根据需求,任意修改页面中的某一部份内容(例如实时刷新),总体页面不刷新,性能好,体验好(全部表单验证,须要实时刷新的等需求都要基于AJAX实现)
  2. 有利于开发,提升开发效率

    1)先后端的彻底分离,后台不须要考虑前端如何实现,前端也不须要考虑后台用什么技术,真正意义上实现了技术的划分json

    2)能够同时进行开发:项目开发开始,首先制定先后端数据交互的接口文档(文档中包含了,调取哪一个接口或者哪些数据等协议规范),后台把接口先写好(目前不少公司也须要前端本身拿NODE来模拟这些接口),客户端按照接口调取便可,后台再去实现接口功能便可

【弊端】

  1. 不利于SEO优化:第一次从服务器端获取的内容不包含须要动态绑定的数据,因此页面的源代码中没有这些内容,不利于SEO收录,后期经过JS添加到页面中的内容,并不会写在页面的源代码中(是源代码不是页面结构)
  2. 交由客户端渲染,首先须要把页面呈现,而后在经过JS的异步AJAX请求获取数据,在进行数据绑定,浏览器再把动态增长的部分从新渲染,无形中浪费了一些时间,没有服务器端渲染页面呈现速度快

基于原生JS实现AJAX

// 建立一个AJAX对象
let xhr = new XMLHttpRequest(); // 不兼容IE6及更低版本浏览器(IE6:ActiveXObject)

// 打开请求地址(能够理解为一些基础配置,可是并无发送请求呢)
xhr.open([method],[url],[async],[userName],[passWord]);

// 监听AJAX状态改变,获取响应信息(获取响应头信息,获取响应主体信息)
xhr.onreadystatechange = ()=>{
    if(xhr.readyState === 4 && xhr.status === 200){
        let result = xhr.responseText; // 获取响应主体中的内容
    }
};

// 发送AJAX请求(括号中传递的内容就是请求主体的内容)
xhr.send(null);

分析第二步中的细节点

xhr.open([method],[url],[async],[userName],[passWord])

[AJAX请求方式]

  1. GET请求系列(获取)

    • get
    • delete:从服务器上删除某些资源文件
    • head:只想获取服务器返回的响应头信息(响应主体不须要获取)
  2. POST请求系列(推送)

    • post
    • put:向服务器中增长指定的资源文件

无论哪种请求方式,客户端均可以把信息传递给服务器端,服务器端也能够把信息返回给客户端,只是GET系列通常以获取为主(给的少,拿回来的多),而POST系列通常以推送为主(给的多,拿回来的少)

1)咱们想要获取一些动态展现的信息,通常使用GET请求,由于只须要向服务器端发送请求,告诉服务器端咱们想要什么,服务器端就会把须要的数据返回

2)在实现注册功能的时候,咱们须要把客户输入的信息发送给服务器进行存储,服务器通常返回成功或失败等状态,此时咱们通常都是基于POST请求完成的


GET系列请求和POST系列请求,在项目实战中存在不少的区别

  1. GET请求传递给服务器的内容通常没有POST请求传递给服务器的内容多

    缘由:GET请求传递给服务器内容通常都是基于 URL地址问号传递参数 来实现的,而POST请求通常都是基于 设置请求主体 来实现的,各个浏览器都有URL最大长度的限制(谷歌:8kb,火狐:7kb,IE:2kb),超出长度部分,浏览器会自动截取掉,致使传递给服务器的数据缺失

    理论上POST请求经过请求主体传递是没有大小限制的,真实项目中为了保证传输的速率,咱们也会限制大小(例如:上传的资料或者图片咱们会作大小的限制)

  2. GET请求很容易出现缓存(这个缓存不可控,通常咱们都不须要),而POST不会出现缓存(除非本身作特殊处理)

    缘由:GET是经过URL问号传参传递给服务器信息,会出现缓存;而POST是设置请求主体,不会出现缓存。

// 每隔一分钟从新请求服务器端最新的数据,而后展现在页面中(页面中某些数据实时刷新)
setTimeout(()=>{
    $.ajax({
        url: 'getList?lx = news',
        ...
        success: result=>{
            // 第一次请求数据回来,间隔一分钟后,浏览器又发送一次请求,可是新发送的请求不管是地址仍是传递的参数都和第一次如出一辙,浏览器颇有可能会把上一次的数据获取,而不是获取最新的数据
        }
    })
},600)

// 解决方案:在每一次从新请求的时候,在URL的末尾追加一个随机数,保证每一次请求的地址不彻底一致,就能够避免是从缓存中读取的数据
setTimeout(()=>{
    $.ajax({
        url: 'getList?lx = news&_=' + Math.random(),
        ...
        success: result=>{
            
        }
    })
},600)
  1. GET请求没有POST请求安全(POST也并非十分安全,只是相对安全)

    缘由:仍是由于GET是URL传参给服务器

    有一种比较简单的黑客技术:URL劫持,也就是能够把客户端传递给服务器的数据劫持到,致使信息泄露


URL:请求数据的地址(API地址),在真实项目中,后台开发工程师会编写一个API文档,在API文档中汇总了获取哪些数据须要使用哪些地址,咱们按照文档操做便可

ASYNC:异步(SYNC 同步),设置当前AJAX请求是异步的仍是同步的,不写默认是异步(TRUE),若是设置为FALSE,则表明当前请求是同步的

用户名和密码:这两个参数通常不用,若是你请求的URL地址所在的服务器设定了访问权限,则须要咱们提供可通行的用户名和密码才能够(通常服务器都是能够匿名访问的)


分析第三步中的细节点

AJAX状态码:描述当前AJAX操做状态的:

xhr.readyState

0:UNSENT未发送,只要建立一个AJAX对象,默认值就是零

1:OPENED 咱们已经执行了xhr.open这个操做

2:HEADERS_RECEIVED 当前AJAX的请求已经发送,而且已经接收到服务器端返回的响应头信息了

3:LOADING 响应主体的内容正在返回的路上

4:DONE 响应主体内容已经返回到客户端


HTTP网络状态码:记录了当前服务器返回信息的状态 :

xhr.status

200:成功,一个完整的HTTP事务完成(以2开头的状态码通常都是成功)

以3开头通常也是成功,只不过服务器端作了不少处理

301:Moved Permanently 永久转移(永久重定向),通常应用于域名迁移

302:Move temporarily 临时转移 (临时重定向,新的HTTP版本中任务307是临时重定向),通常用于服务器的负载均衡:当前服务器处理不过来,把当前请求临时交给其余的服务器处理(通常图片请求常常出现302,不少公司都有单独的图片服务器)

304:Not Modified 从浏览器缓存中获取数据。把一些不常常更新的文件或者内容缓存到浏览器中,下一次从缓存中获取,减轻服务器压力,也提升页面加载速度

以4开头的,通常都是失败,并且客户端的问题偏大

400:请求参数错误

401:无权限访问

404:访问地址不存在

以5开头的,通常都是失败,并且服务器端的问题偏大

500:Internal Server Error 未知的服务器错误

503:Service Unavailable 服务器超负载


AJAX中其余经常使用的属性和方法

面试题:AJAX中总共支持几个方法?
let xhr = new XMLHttpRequest();
console.dir(xhr);

//【属性】
// 1.readyState:存储的是当前AJAX的状态码
// response/responseText/responseXML:都是用来接收服务器返回的响应主体中的内容,只是根据服务器返回内容格式的不同,咱们使用不一样的属性接收便可
// responseText是最经常使用的,接收的结果是字符串格式的(通常服务器返回的数据都是JSON格式字符串)
// responseXML偶尔会用到,若是服务器返回的是XML文档数据,咱们须要使用这个属性接收
// status:记录了服务器端返回的HTTP状态码
// statusText:对返回的状态码的描述
// timeout:设置当前AJAX请求的超时时间,假设咱们设置时间为3000ms,从AJAX 请求发送开始,3秒后响应主体内容尚未返回,浏览器会把当前AJAX请求任务强制断开

// 【方法】
// abort():强制中断AJAX请求
// getAllResponseHeaders():获取所有的响应头信息(获取的结果是一堆字符串文本)
// getResponseHeader(key):获取指定属性名的响应头信息,例如:xhr.getResponseHeader('date')获取响应头中存储的服务器的时间
// open():打开一个URL地址
// overrideMimeType():重写数据的MIME类型
// send():发送AJAX请求(括号中写的是客户端基于请求主体把信息传递给服务器)
// setRequestHeader(key,value):设置请求头信息(能够是设置自定义请求头信息)

// [事件]
// onabort:当AJAX被中断请求时触发这个事件
// onreadystatechange:AJAX状态发生改变会触发这个事件
// ontimeout:当AJAX请求超时,会触发这个事件

例子:

let xhr = new XMLHttpRequest();
xhr.open('get','temp.json?_=' + Math.random(), true);
xhr.setRequestHeader('aaa', '123'); // 注意:请求头部的内容不得出现中文汉字。设置请求头信息必须在OPEN以后和SEND以前

// 设置超时
xhr.timeout = 10;
xhr.ontimeout = ()=>{
    console.log('当前请求已经超时');
    xhr.abort();
};


xhr.onreadystatechange = ()=>{
    let {readyState:state, status} = xhr;
    if(!/^(2|3)\d{2}$/.test(status)) 
        return;
    
    // 在状态为2的时候就能够获取响应头信息
    if (state === 2){
        let headerAll = xhr.getAllResponseHeaders(),
            serverDate = xhr.getResponseHeader('date'); // 获取的服务器时间是格林尼治时间(相比北京时间差了8小时),经过new Date 能够把这个时间转换为北京时间
        console.log(headerAll, new Date(serverDate);
        return;
    }
    
    // 在状态为4的时候响应主体内容就已经回来了
    if (state === 4){
        let valueText = xhr.responseText, // 获取到的结果通常都是JSON字符串(可使用JSON.PARSE把其转换为JSON对象)
            valueXML = xhr.responseXML; // 获取到的结果是XML格式的数据(能够经过XML的一些常规操做获取存储的指定信息)
        // 若是服务器返回的是XML文档,用responseText获取的结果是字符串,而用responseXML获取的是标准XML文档
        console.log(valueText, valueXML);
    }
};
xhr.send('name=hy&age=6&sex=man');

JS中经常使用的编码和解码方法

正常的编码解码(非加密)

  1. escape / unescape: 主要就是把中文汉字进行编码和解码(通常只有JS语言支持:也常常应用于前端页面通讯时候的中文汉字编码)
str = '你好海洋 哈哈'
"你好海洋 哈哈"
escape(str)
"%u4F60%u597D%u6D77%u6D0B%20%u54C8%u54C8"
unescape("%u4F60%u597D%u6D77%u6D0B%20%u54C8%u54C8")
"你好海洋 哈哈"
  1. encodeURI / decodeURI : 基本上全部的编程语言都支持
str = '你好海洋 哈哈'
"你好海洋 哈哈"
encodeURI(str)
"%E4%BD%A0%E5%A5%BD%E6%B5%B7%E6%B4%8B%20%E5%93%88%E5%93%88"
decodeURI("%E4%BD%A0%E5%A5%BD%E6%B5%B7%E6%B4%8B%20%E5%93%88%E5%93%88")
"你好海洋 哈哈"
  1. encodeURIComponent / decodeURIComponent 和第二种方式很是相似,区别在于:

    当咱们经过URL问号传参的时候,咱们传递的参数值仍是一个URL或者包含不少的特殊字符,此时为了避免影响主要的URL,咱们须要把传递的参数值进行编码,使用encodeURI不能编码一些特殊字符,因此只能使用恩codeURLComponent处理

str = '你好海洋 哈哈'
"你好海洋 哈哈"
encodeURIComponent(str)
"%E4%BD%A0%E5%A5%BD%E6%B5%B7%E6%B4%8B%20%E5%93%88%E5%93%88"
decodeURIComponent("%E4%BD%A0%E5%A5%BD%E6%B5%B7%E6%B4%8B%20%E5%93%88%E5%93%88")
"你好海洋 哈哈"
let str = 'http://wudiufo.github.io/?',
    obj = {
        name:'haiyang',
        age:'8';
        url:'http://www.haiyang.com/?lx=1'
    };
// 需求:把obj中的每一项属性名和属性值拼接到URL末尾,经过问号传参的方式
for(let key in obj){
    str+=`${key}=${encodeURIComponent(obj[key])}&`;
    // 不能使用encodeURI,必须使用encodeURIComponent,缘由是encodeURI不能编码特殊的字符
}
console.log(str.replace(/&$/g), '');

// 后期获取URL问号参数的时候,咱们把获取的值在依次的解码便可
String.prototype.myQueryUrlParameter=function myQueryUrlParameter(){
    let reg=/[?&]([^?&=]+)(?:=([^?&=]*))?/g,
        obj={};
    this.replace(reg,(...arg)=>{
        let [,key,value]=arg;
        obj[key]=decodeURIComponent(value); //此处获取的时候能够进行解码 
    });
    return obj;
}

也能够经过加密的方法进行编码解码

  1. 可逆转加密(通常都是团队本身用的规则)
  2. 不可逆转加密(通常都是基于MD5完成的,可能会把MD5加密后的结果二次加密)


AJAX中的同步和异步编程

AJAX这个任务:发送请求接收到响应主体内容(完成一个完整的HTTP事务)

xhr.send() : 任务开始

xhr.readState===4 : 任务结束

let xhr = new XMLHttpRequest();
xhr.open('get', 'temp.json', false);
xhr.onreadystatechange = () =>{
    console.log(xhr.readyState);
};
xhr.send();
// 只输出一次结果是4

图片描述


let xhr = new XMLHttpRequest();
xhr.open('get', 'temp.json', false);
xhr.send(); // [同步]开始发送AJAX请求,开启AJAX任务,在任务没有完成以前,什么事情都作不了(下面绑定事件也作不了)=》loading =》当readyState===4的时候AJAX任务完成,开始执行下面的操做
// readyState===4
xhr.onreadystatechange = () =>{
    console.log(xhr.readyState);
};
// 绑定方法以前状态已经为4了,此时AJAX的状态不会再改变成其余的值了,因此事件永远都不会被触发,一次都没执行方法(使用AJAX同步编程,不要把send放在事件监听前,这样咱们没法再绑定的方法中获取到响应主体的内容)

let xhr = new XMLHttpRequest();
xhr.open('get', 'temp.json');
xhr.onreadystatechange = () =>{
    console.log(xhr.readyState);
};
xhr.send();
// 输出3次,结果分别是2,3 ,4

图片描述


let xhr = new XMLHttpRequest();
xhr.open('get', 'temp.json');
xhr.send();
// xhr.readyState===1
xhr.onreadystatechange = () =>{
    console.log(xhr.readyState);
};
// 2,3,4

let xhr = new XMLHttpRequest();
xhr.onreadystatechange = () =>{
    console.log(xhr.readyState);
};
xhr.open('get', 'temp.json');
xhr.send();
// 1,2,3,4

let xhr = new XMLHttpRequest();
// xhr.readyState===0
xhr.onreadystatechange = () =>{
    console.log(xhr.readyState);
};
xhr.open('get', 'temp.json',false);
// xhr.readyState===1,AJAX特殊处理了一件事,执行OPEN状态变为1,会主动把以前监听的方法执行一次,而后再去执行SEND
xhr.send();
// xhr.readyState===4 AJAX任务结束,住任务队列完成
// 1,4

实战案例:倒计时抢购

1,结构:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <div id="box"></div>
  <script src="./1.js"></script>
</body>
</html>

2, 交互:

~ function() {
  let box = document.getElementById('box');
  let serverTime = null;


  let fn = () => {
    // 1,计算当前时间和目标时间的差值
    // new Date():获取的是客户端本机时间(会受到客户端本身调整时间的影响),重要的时间参考不能基于这个完成,不论哪个客户端都要基于相同的服务器时间计算
    // 每间隔一秒钟,咱们须要把第一次获取的服务器时间进行累加

    serverTime = serverTime + 1000;


    let tarTime = new Date('2018/12/22 12:00:00').getTime(),
      spanTime = tarTime - serverTime;

    // 2,计算差值中包含多少时分秒
    if (spanTime < 0) {
      // 已经错过了抢购时间(已经开抢了)
      box.innerHTML = '开枪';
      clearInterval(autoTimer);
      return;
    }

    let hours = Math.floor(spanTime / (1000 * 60 * 60));
    spanTime -= hours * 3600000;
    let minus = Math.floor(spanTime / (1000 * 60));
    spanTime -= minus * 60000;
    let seconds = Math.floor(spanTime / 1000);
    hours < 10 ? hours = '0' + hours : null;
    minus < 10 ? minus = '0' + minus : null;
    seconds < 10 ? seconds = '0' + seconds : null;

    box.innerHTML = `距离开枪还剩下 ${hours}:${minus}:${seconds}`;
  };

  let autoTimer = setInterval(fn, 1000);



  // 从服务器端获取服务器时间
  let getServerTime = () => {
    let xhr = new XMLHttpRequest();
    xhr.onreadystatechange = () => {
      // console.log(xhr.readyState);
      // HEAD请求方式,状态码中没有3(不须要等待响应主体内容)
      if (!/^(2|3)\d{2}$/.test(xhr.status)) return;
      if (xhr.readyState === 2) {
        serverTime = new Date(xhr.getResponseHeader('date')).getTime();
        fn();
      }
    };
    xhr.open('head', 'temp.xml', true);
    xhr.send(null);

    // 1,服务器返回的时间在响应头就有,咱们只须要获取响应头信息便可,不必获取响应主体内容,因此请求方式使用HEAD便可
    // 2,必须使用异步编程:同步编程咱们没法再状态为2或3的时候作一些处理,而咱们获取响应头信息,在状态为2的时候就能够获取了,因此须要使用异步
    // 3,在状态为2的时候就把服务器时间获取到
    // 获取服务器时间总会出现时间差的问题,服务器端把时间记录好,到客户端获取到时间有延迟差(例如:服务器返回的时候记录的是10:00,到客户端获取的时候已是10:01,可是客户端获取的结果依然是10:00,这样就有了1秒钟的时间差)【尽量的减小时间差,是咱们优化的所有目的】
  };

  getServerTime();

}();

AJAX类库的封装

JQ中AJAX操做详解

$.ajax({
    url:'xxx.txt', //请求API地址
    method:'get', //请求方式GET/POST...,在老版本JQ中使用的是type,使用type和method实现的是相同的效果
    dataType:'json', //dataType只是咱们预设获取结果的类型,不会影响服务器的返回,(服务器端通常给咱们返回的都是JSON格式的字符串),若是咱们预设的是JSON,那么类库中将把服务器返回的字符串转换为JSON对象,若是咱们预设的是text(默认值),咱们把服务器获取的结果直接拿过来操做便可,咱们预设的值还能够是xml等
    cache:false, // 设置是否清除缓存,只对GET系列请求有做用,默认是true,有缓存,手动设置为false,无缓存,JQ类库会在请求URL的末尾追加一个随机数来清除缓存
    data:null, //咱们经过data能够把一些信息传递给服务器;GET系列请求会把data中的内容拼接在URL的末尾经过问号传参的方式传递给服务器,POST系列请求会把内容放在请求主体中传递给服务器。data的值能够设置为两种格式:字符串,对象,若是是字符串,设置的值是什么传递给服务器的就是什么,若是设置的是对象,JQ会把对象变为 xxx=xxx&xxx=xxx 这样的字符串传递给服务器
    async:true,//设置同步或者异步,默认是true表明异步,false是同步
    success:function(result){
        // 当AJAX请求成功(readyState===4 & status是以2或者4开头的)
        // 请求成功后JQ会把传递的回调函数执行,而且把获取的结果当作实参传递给回调函数(result就是咱们从服务器获取的结果)
    },
    error:function(msg){}, // 请求错误触发回调函数
    complete:function(){}, // 不论请求是错误的仍是正确的都会触发回调函数(他是完成的意思)
})

封装属于本身的AJAX类库

【支持的参数】

url

method/type

data

dataType

async

cache

success

~ function() {
  class ajaxClass {
    // SEND AJAX
    init() {
        // THIS:EXAMPLE
        let xhr = new XMLsHttpRequest();
        xhr.onreadystatechange = () => {
          if (!/[23]\d{2}$/.test(xhr.status)) return;
          if (xhr.readyState === 4) {
            let result = xhr.responseText;
            // DATA-TYPE
            switch (this.dataType.toUpperCase()) {
              case 'TEXT':
              case 'HTML':
                break;
              case 'JSON':
                result = JSON.parse(result);
                break;
              case 'XML':
                result = xhr.responseXML;
            }
            this.success(result);
          };
        };

        // DATA
        if (this.data != null) {
          this.formatData();
          if (this.isGET) {
            this.url += this.querySymbol() + this.data;
            this.data = null;
          }
        }

        // CACHE
        this.isGET ? this.cacheFn() : null;

        xhr.open(this.method, this.url, this.async);
        xhr.send(this.data);
      }
      //  把传递的对象格式data转换为字符串格式的data
    formatData() {
      // THIS:EXAMPLE
      if (Object.prototype.toString.call(this.data) === '[object Object]') {
        let obj = this.data,
          str = ``;
        for (let key in obj) {
          if (obj.hasOwnProperty(key)) {
            str += `${key}=${obj[key]}&`;
          }
        }
        str = str.replace(/&$/g, '');
        this.data = str;
      }
    }

    cacheFn() {
      // THIS:EXAMPLE
      !this.cache ? this.url += `${this.querySymbol()}_=${Math.random()}` : null;


    }

    querySymbol() {
      // THIS:EXAMPLE
      return this.querySymbol.indexof('?') > -1 ? '&' : '?';
    }
  }

  // init parameters
  window.ajax = function({
    url = null,
    method = 'GET',
    type = 'null',
    data = null,
    dataType = 'JSON',
    cache = true,
    async = true,
    success = null
  } = {}) {
    let example = new ajaxClass();
    example.url = url;
    example.method = type === null ? method : type;
    example.data = data;
    example.dataType = dataType;
    example.cache = cache;
    example.async = async;
    example.success = typeof success === 'function' ? success : new Function();
    example.isGET = /^(GET|DELETE|HEAD)$/i.test(example.method);
    example.init();
    return example;
  };
}();
ajax({});

优化代码:

~ function() {
  class ajaxClass {
    // SEND AJAX
    init() {
        // THIS:EXAMPLE
        let xhr = new XMLHttpRequest();
        xhr.onreadystatechange = () => {
          if (!/[23]\d{2}$/.test(xhr.status)) return;
          if (xhr.readyState === 4) {
            let result = xhr.responseText;
            // DATA-TYPE
            try {
              switch (this.dataType.toUpperCase()) {
                case 'TEXT':
                case 'HTML':
                  break;
                case 'JSON':
                  result = JSON.parse(result);
                  break;
                case 'XML':
                  result = xhr.responseXML;
              }
            } catch (e) {

            }

            this.success(result);
          };
        };

        // DATA
        if (this.data != null) {
          this.formatData();
          if (this.isGET) {
            this.url += this.querySymbol() + this.data;
            this.data = null;
          }
        }

        // CACHE
        this.isGET ? this.cacheFn() : null;

        xhr.open(this.method, this.url, this.async);
        xhr.send(this.data);
      }
      //  把传递的对象格式data转换为字符串格式的data
    formatData() {
      // THIS:EXAMPLE
      if (Object.prototype.toString.call(this.data) === '[object Object]') {
        let obj = this.data,
          str = ``;
        for (let key in obj) {
          if (obj.hasOwnProperty(key)) {
            str += `${key}=${obj[key]}&`;
          }
        }
        str = str.replace(/&$/g, '');
        this.data = str;
      }
    }

    cacheFn() {
      // THIS:EXAMPLE
      !this.cache ? this.url += `${this.querySymbol()}_=${Math.random()}` : null;


    }

    querySymbol() {
      // THIS:EXAMPLE
      return this.querySymbol.indexof('?') > -1 ? '&' : '?';
    }
  }

  // init parameters
  window.ajax = function({
    url = null,
    method = 'GET',
    type = 'null',
    data = null,
    dataType = 'JSON',
    cache = true,
    async = true,
    success = null
  } = {}) {
    let _this = new ajaxClass();
    ['url', 'method', 'data', 'dataType', 'cache', 'async', 'success'].forEach((item) => {
      if (item === 'method') {
        _this.method = type === null ? method : type;
        return;
      }
      if (item === 'success') {
        _this.success = typeof success === 'function' ? success : new Function();
        return;
      }
      _this[item] = eval(item);
    });

    _this.isGET = /^(GET|DELETE|HEAD)$/i.test(_this.method);
    _this.init();
    return _this;
  };
}();
ajax({});
相关文章
相关标签/搜索