面试题:手写ajax

您所在的位置:网站首页 js手写 面试题:手写ajax

面试题:手写ajax

2024-04-22 16:37| 来源: 网络整理| 查看: 265

前言

有时候会刷一些面试题

今天刷到一题,让手写ajax:

image.png

我当时的第一反应是:老子不会😂

...

回想一下,为什么会有这个反应?主要有两点

工作中都是直接使用jquery、axios或者fetch等等

像这种默写API的题,觉得没什么意义

就像手写ajax,只要记清楚原生xhr的几个判断,send/open/abort和readyState就可以了

类似的题其实很多很多,很多面试都会考察API的使用,但我就是不喜欢

老问这样的问题,我就不相信,出这些题的公司的工程师都能把所有的原生 API 默写出来,而不上网查

非常赞同尤大之前说过一个观点:

技术面试如果足够有诚意就让人拿出笔记本当场写,随便查,你才能看到面试者在真实的写代码的时候是什么状态,什么思路。就看面试者去哪里查,怎么查,你就能得到比让人默写 API 要有价值得多的信息

个人也比较喜欢这种方式

...

但是到这,发现还不够500字

那今天就来细说ajax吧,就当重新复习复习,当以后如果真被面,而且还记不太清楚了,那就直接翻开此文!

文中作者给了答案,Promise版本的,如下:

const getJSON = function (url) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open("GET", url, false); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onreadystatechange = function () { if (xhr.readyState !== 4) return; if (xhr.status === 200 || xhr.status === 304) { resolve(xhr.responseText); } else { reject(new Error(xhr.responseText)); } }; xhr.send(); }); };

那我们来实现一个更详细的、更原始的版本,一次性彻底搞清楚AJAX

手写实现

AJAX是Asynchronous JavaScript And XML的简称,它允许我们在不刷新整个页面的情况下,就可以异步获取数据,并更新页面的部分内容。具体的实现步骤如下:

创建一个异步对象 var xmlhttp; if (window.XMLHttpRequest) {// code for IE7+, Firefox, Chrome, Opera, Safari xmlhttp=new XMLHttpRequest(); }else {// code for IE6, IE5 xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); }

通过XMLHttpRequest构造函数创建一个异步对象xmlhttp, IE6, IE5 使用ActiveXObject创建,创建的这个异步对象上有很多属性和方法,常用的有:

onreadystatechange

监听异步对象请求状态码readyState的改变,每当readyState改变时,就会触发onreadystatechange事件

readyState:请求状态码

readyState表示异步对象目前的状态,状态码从0到4:

0: 表示请求未初始化,还没有调用 open()

1: 服务器连接已建立,但是还没有调用 send()

2: 请求已接收,正在处理中(通常现在可以从响应中获取内容头)

3: 请求处理中,通常响应中已有部分数据可用了,没有全部完成

4: 当readyState状态码为4时,表示请求已完成;此阶段确认全部数据都已经解析完毕,可以通过异步对象的属性获取对应数据

status:http状态码

http状态码表示成功的http状态码有xmlhttp.status >= 200 && xmlhttp.status < 300 || xmlhttp.status == 304

responseText:后台返回的字符串形式的响应数据

responseXML:后台返回的XML形式的响应数据

设置请求方式和请求地址

创建异步对象之后,通过open()方法设置ajax请求方式和请求地址 格式:xmlhttp.open("GET/POST","ajax-get.txt",true);

第一个参数:请求的类型;GET 还是 POST

第二个参数:表示请求的文件的地址url

第三个参数:设置请求方法是不是异步async,true为异步, false为同步。AJAX存在的意义就是发异步请求,所以第三个参数永远传true

这里有个问题,就是IE中的缓存问题

在IE浏览器中,如果通过Ajax发送GET请求,那么IE浏览器认为,同一个URL只有一个结果,如果地址没有发生变化,它就会把上一次返回的结果,直接返回。这样我们不能实时的拿到变化后的数据。如果要想我们拿到实时数据,必须保证每次的URL都是不一样的,有两种方式:

Math.random()

new Date().getTime()

即在请求地址后面拼接上?t=随机数或者1970.01.01至当前的毫秒数 所以在IE中通过ajax发送get请求时,可以设置请求地址为:

xmlhttp.open("GET","ajax-get.txt?t=" + (new Date().getTime()),true); //或 xmlhttp.open("GET","ajax-get.txt?t=" + Math.random(),true); 发送请求

直接通过异步对象的send()发送请求

xmlhttp.send();

特别注意的是: 如果发送POST请求,请使用setRequestHeader()来添加 HTTP请求头,并在send()方法中传递要发送的数据:

xmlhttp.open("POST","ajax_test.html",true); xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded"); xmlhttp.send("fname=Henry&lname=Ford"); 通过onreadystatechange监听状态变化

每当异步对象的readyState发生改变,就会触发onreadystatechange函数,当readyState变成为4时,表示当前状态是请求完毕的状态,同时当http的响应码status为200到300之间(包括200和300)或为304时,表示ajax请求成功;当http状态码不是200到300之间的数也不是304时,表示请求不成功

//4.监听状态变化 xmlhttp.onreadystatechange = function(){ // 判断当前状态改变是请求完毕的状态吗 if (xmlhttp.readyState === 4) { if (xmlhttp.status >= 200 && xmlhttp.status < 300 || xmlhttp.status == 304) { console.log("成功的接收到服务器返回的数据"); }else{ console.log("不成功!"); } } } 处理返回的结果

如果成功,可通过异步对象的responseText属性来获取服务器返回的字符串

console.log(xmlhttp.responseText);

根据上述情况,如果每次请求都要手写以上五个步骤,那很麻烦,接下来,我们来封装一个方法ajax()用于发送请求

封装的时候,需要注意

URL当中只能出现字母 数字 下划线和ASCII码,不能出现中文,可以使用encodeURIComponent()转码

当我们利用我们的ajax放的发送一个请求到远处服务器时,我们需要等待远程服务器去响应我们的请求,等待远程服务器将响应的结果返回给我们,但是这个响应的速度是不确定的,因为响应的速度是由本地网络和远程服务器的网速等共同决定的,所以我们不可能一直等待服务器的响应。那么这里我们需要新增另外一个功能:设置超时时间的功能,即告诉它我的请求会等待多长的时间,如果这么长的时间内都没有响应我们发送的请求,那我就在这里自动的终止这次请求;

function ajax(type, url, obj, timeout, success, error) { // 0.将对象转换成字符串 var str = objToString(obj); // 1.创建一个异步对象xmlhttp; var xmlhttp, timer; if (window.XMLHttpRequest) { xmlhttp = new XMLHttpRequest(); } else {// code for IE6, IE5 xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } // 2.设置请求方式和请求地址; // 判断请求的类型是POST还是GET if (type === 'GET') { xmlhttp.open(type, url + "?t=" + str, true); // 3.发送请求; xmlhttp.send(); } else { //如果是post请求,需要设置请求头 xmlhttp.open(type, url, true); // 注意:在post请求中,必须在open和send之间添加HTTP请求头:setRequestHeader(header,value); xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); // 3.发送请求; xmlhttp.send(str); } // 4.监听状态的变化; xmlhttp.onreadystatechange = function () { clearInterval(timer); if (xmlhttp.readyState === 4) { if (xmlhttp.status >= 200 && xmlhttp.status < 300 || xmlhttp.status == 304) { // 5.处理返回的结果; success(xmlhttp);//成功后回调; } else { error(xmlhttp);//失败后回调; } } } //处理obj function objToString(obj) { obj.t = new Date().getTime(); var res = []; for (var key in obj) { //需要将key和value转成非中文的形式,因为url不能有中文。使用encodeURIComponent(); res.push(encodeURIComponent(key) + " = " + encodeURIComponent(obj[key])); } return res.join("&"); } //判断外界是否传入了超时时间 if (timeout) { timer = setInterval(function () { xmlhttp.abort();//中断请求 clearInterval(timer); }, timeout); } } 完善问题

在使用自己封装的代码的时候,感觉跟jQuery官方的ajax还是有一定的差异,所以还需要进一步完善

首先看第一个问题,传递多个参数,需要保持传递顺序。解决方案是可以改写成传递的是一个对象;因为对象里面的值,传递的是一个对象就不用考虑先后顺序,里面用的参数通过对象名.属性名的形式获取

第二,传递请求类型的区分大小写,jQuery官方的是大小写都可以;解决方案是可以使用toLowerCase()或者toUpperCase()将类型转成大写或小写再对比;

第三,我们传递的数据用的名字是obj,jQuery官方用的是data,语义更适合。所以将传递数据obj改成data

完善如下:

function ajax(option) {//type,url,obj,timeout,success,error将所有参数换成一个对象{} // 0.将对象转换成字符串 var str = objToString(option.data); // 1.创建一个异步对象xmlhttp; var xmlhttp, timer; if (window.XMLHttpRequest) { xmlhttp = new XMLHttpRequest(); } else {// code for IE6, IE5 xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } // 2.设置请求方式和请求地址; // 判断请求的类型是POST还是GET if (option.type.toLowerCase() === 'get') { xmlhttp.open(option.type, option.url + "?t=" + str, true); // 3.发送请求; xmlhttp.send(); } else { xmlhttp.open(option.type, option.url, true); // 注意:在post请求中,必须在open和send之间添加HTTP请求头:setRequestHeader(header,value); xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); // 3.发送请求; xmlhttp.send(str); } // 4.监听状态的变化; xmlhttp.onreadystatechange = function () { clearInterval(timer); if (xmlhttp.readyState === 4) { if (xmlhttp.status >= 200 && xmlhttp.status < 300 || xmlhttp.status == 304) { // 5.处理返回的结果; option.success(xmlhttp);//成功后回调; } else { option.error(xmlhttp);//失败后回调; } } } //处理obj function objToString(data) { data.t = new Date().getTime(); var res = []; for (var key in data) { //需要将key和value转成非中文的形式,因为url不能有中文。使用encodeURIComponent(); res.push(encodeURIComponent(key) + " = " + encodeURIComponent(data[key])); } return res.join("&"); } //判断外界是否传入了超时时间 if (option.timeout) { timer = setInterval(function () { xmlhttp.abort();//中断请求 clearInterval(timer); }, timeout); } }

以上就是ajax基本实现,到这面试应该问题不大了吧!实际工作中请用人家封装好的~

最后,希望:以后如果遇到需要默写API的,希望大家允许我上网,因为我记不住😂

好了,本次内容到这结束!

如果有问题或者你也有相同经历,欢迎留言分享~

END~

参考文献:www.w3school.com.cn/ajax/index.…



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3