摘要:最近,看了下慕课2周刷完n道面试题,记录下...
1.请说明Ajax、Fetch、Axios三者的区别
三者都用于网络请求,但维度不同:
- Ajax(Asynchronous Javascript ang XML),是一种在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页内容的技术。
- Fetch,浏览器原生API,用于网络请求,和XMLHttpRequest一个级别;Fetch语法更简介易用,且支持Promise。
- Axios最常用的网络请求lib库(随着Vue火爆起来),内部可用XMLHttpRequest和Fetch来实现, 官网:
Axios中文文档 | Axios中文网Axios 是一个基于 promise 的网络请求库,可以用于浏览器和 node.jshttps://www.axios-http.cn/ 使用原生的 XMLHttpRequest 对象实现 Ajax 请求,需要编写大量的冗余代码,而且不能支持 Promise 和 async/await 等现代 JavaScript 特性。Fetch 提供了一组丰富的 API,可以配置请求头、请求体、响应类型等参数,也可以取消请求、处理跨域请求等复杂场景。但是,Fetch 也存在一些限制,例如无法中止请求、不支持进度监视等。
使用XmlHttpRequest实现Ajax
//1. 创建XMLHttpRequest对象
var xmlhttp; // 用于保存创建好的对象
/**
* XMLHttpRequest是Ajax的核心
* 它是一个构造函数,可以通过new关键字来调用
* Ajax使用该对象发起请求、接收响应
* 语法:var 变量名 = new XMLHttpRequest();
* XMLHttpRequest并不是W3C的标准,不同浏览器的创建方式不同
* 例如:Chrome、Firefox、IE7+、Opera、Safari等浏览器使用的是XMLHttpRequest
* 注意:IE5和IE6不支持该对象,它们使用ActiveXObject
*/
// 判断浏览器是否支持该对象
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest(); }
else { // IE5和IE6使用的是ActiveXObject
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
/**
* 先创建请求,open()方法,指定请求类型和URL
* 语法:xmlhttp.open("请求类型","请求的URL",是否异步) * 参数:请求类型,表示请求的类型,例如GET、POST等
* 请求的URL,表示请求的地址,可以是相对地址,也可以是绝对地址
* 是否异步,表示是否采用异步方式,如果为true,表示异步,如果为false,表示同步
* 例如:xmlhttp.open("GET","/content",true);
* 注意:该方法并不会真正发送请求,而只是启动一个请求以备发送
* 然后调用send()方法,发送请求
*/
// 2. 配置请求信息
xmlhttp.open("GET", "/content");
// 3. 发送Ajax
xmlhttp.send();
// 4. 处理服务器响应
/**
* XMLHttpRequest.onreadystatechange()是一个事件处理函数,当XMLHttpRequest对象的
* readyState属性值发生变化时,该函数将被触发。
* 以下是readyState属性的值说明:
* 0:表示XMLHttpRequest对象正在初始化,尚未发送请求。
* 1:表示请求已经发送,但尚未收到响应。
* 2:表示请求已经发送,并且服务器已经处理请求,正在等待服务器响应。
* 3:表示请求已经发送,并且服务器已经处理请求,并且响应已经就绪,但尚未读取。
* 4:表示请求已经发送,并且服务器已经处理请求,并且响应已经就绪,并且响应的内容已经读取完毕。 */
xmlhttp.onreadystatechange = function () {
// 响应已被接受且服务器处理成功时才执行(响应状态码200)
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
// 获取响应体的文本
var t = xmlhttp.responseText; console.log(t);
document.getElementById("divContent").innerHTML = document.getElementById("divContent").innerHTML + "
" + t; }
}
使用Fetch实现Ajax
// 创建一个函数来发送 Fetch 请求
function fetchData(url) {
fetch(url)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json(); // 解析 JSON 数据
})
.then(data => {
console.log(data); // 输出获取到的数据
})
.catch(error => {
console.error('There was a problem with your fetch operation:', error);
});
}
// 调用函数发送 Fetch 请求
fetchData('https://jsonplaceholder.typicode.com/posts/1');
2. 节流和防抖
防抖是指在连续触发事件后,等待一定时间间隔(例如 200ms)后再执行函数。如果在等待时间间隔内再次触发了事件,则重新计时,直到等待时间间隔内没有再次触发事件,才最终执行函数。这样可以确保函数在连续触发事件后等待一段时间再执行,避免频繁触发导致函数多次执行。如下图
防抖函数实现
function debounce(func, delay=200) {
let timerId = 0
return function (...args) {
if (timerId) clearTimeout(timerId)
timerId = setTimeout(() => {
func.apply(this, args); //透传this和参数
timerId = 0
}, delay);
};
}
节流是指在一定时间间隔内最多执行一次函数。当事件触发时,如果距离上次执行函数的时间小于指定的时间间隔,则不会执行该函数;如果距离上次执行函数的时间大于等于指定的时间间隔,则会执行该函数。这样可以确保函数在一定时间间隔内最多只被执行一次。
节流函数实现
function throttle(func, delay) {
let timerId = 0
return function (...args) {
if (timerId) return
timerId = setTimeout(() => {
func.apply(this, args); //透传this和参数
timerId = 0
}, delay);
};
}
总结起来,节流是限制执行频率,有节奏的执行;防抖是限制执行次数,多次密集的触发值执行一次,节流关注过程,防抖关注结果。节流的使用场景包括:元素拖拽、滚动事件、窗口调整大小、按钮点击、搜索框输入等;防抖的使用场景包括:输入框输入、表单验证、文本编辑器、窗口缩放等。
实际工作中通常使用lodash中的节流和防抖函数。
3. px % em rem vw/vh有什么区别?
px(像素):px 是绝对单位,也是最常见的长度单位,表示屏幕上的一个像素点,常用于设定固定尺寸的元素。
%(百分比):% 是相对单位,相对于父元素的宽度比例。例如,设置元素的宽度为50%,表示该元素的宽度是其父元素宽度的一半。
em:em 是相对单位,相对于元素自身的字体font-size大小计算。如果没有设置字体大小,则 1em 默认等于父元素的字体大小,常用于设置文字大小。
rem(根元素em):rem 也是相对单位,但相对于根元素(html 元素)的字体大小计计算。使用 rem 可以有效避免多层嵌套带来的计算复杂性,更方便地控制整个页面的布局
vw(视窗宽度单位):vw 表示视窗(viewport)宽度的百分比,1vw 等于视窗宽度的1%。使用 vw 单位可以根据视窗的大小来调整元素的大小,适合响应式设计。
vh(视窗高度单位):vh 表示视窗(viewport)高度的百分比,1vh 等于视窗高度的1%。与 vw 类似,使用 vh 单位可以根据视窗的高度来调整元素的大小,实现响应式设计。
注:vmin表示vw/vh取最小值;vmax是vw/vh取最大值。
4. 什么时候不能使用箭头函数
首先,说下箭头函数的缺点:
- 没有arguments
function fn1() {
console.log('arguments1', arguments)
}
fn1(100,200) // 正常输出arguments
const fn2 = () => {
console.log('arguments2', arguments)
}
fn2(100,200)
- 箭头函数没有自己的this(继承父作用域的this),无法通过apply call bind 改变this
const fn3 = () => {
console.log('this',this)
}
fn3()
fn3.call({x:100})
function fn4() {
console.log('this4',this)
}
fn4.call({x:100})
- 可读性降低
由于箭头函数的这些缺点,导致一些情况下不适合使用箭头函数:
1.对象方法
const obj = {
name: 'czh',
getName: function () {
return this.name
}
}
console.log(obj.getName()) // 正常返回结果czh
const obj2 = {
name: 'czh2',
getName: () => { // this并不指向obj
return this.name
}
}
console.log(obj2.getName())
2. 原型方法
3. 构造函数
const Foo = (name, city) => {
this.name = name
this.city = city
}
const f = new Foo('czh', '北京') // VM379:5 Uncaught TypeError: Foo is not a constructor
4. 动态上下文中的回调函数
// 例如下面按钮中添加点击事件,不用到this时没有问题,用到this则不适用箭头函数
const btn1 = document.getElementById('btn1')
btn.addEventListener('click', () => {
//console.log(this === window)
this.innerHTML = 'clicked'
})
5. Vue生命周期和method
Vue本质上是一个JS对象,对象的方法不能使用箭头函数,参考第一条
注:React组件(非Hooks)他本质上是一个ES6 class,可以使用箭头函数
class Foo {
constructor (name, city) {
this.name = name
this.city = city
}
getName = () => {
return this.name
}
}
const f = new Foo('czh', '北京')
console.log(f.getName())
总之,熟练使用箭头函数,要对this arguments足够敏感
5. 请描述TCP三次握手和四次挥手
这个题目是个常考题,记得我毕业那年就被不止一家公司问过
建立TCP连接
先建立连接(确保双方都有收发消息的能力)
再传输内容(如发送一个get请求)
网络连接时TCP协议,传输内容是http协议
三次握手-建立连接
- 第一步:客户端发送 SYN
- 客户端向服务器发送一个带有 SYN(同步序列编号)标志的数据包,表示客户端请求建立连接。
- 第二步:服务器发送 SYN + ACK
- 服务器收到客户端发送的 SYN 数据包后,会回复一个带有 SYN 和 ACK 标志的数据包,表示确认客户端的请求,并表明服务器也希望建立连接。
- 第三步:客户端发送 ACK
- 客户端收到服务器发送的 SYN + ACK 数据包后,会向服务器发送一个带有 ACK 标志的数据包,表示客户端也确认建立连接。此时,TCP 连接建立成功,可以开始进行数据传输。
四次挥手-断开连接
- 第一步:客户端发送 FIN
- 客户端在完成数据传输后,向服务器发送一个带有 FIN(结束)标志的数据包,表示客户端不再发送数据,并请求关闭连接。
- 第二步:服务器发送 ACK
- 服务器收到客户端发送的 FIN 数据包后,会向客户端发送一个带有 ACK 标志的数据包,表示确认客户端的关闭请求,但仍然允许数据传输。
- 第三步:服务器发送 FIN
- 当服务器准备好关闭连接时,会向客户端发送一个带有 FIN 标志的数据包,表示服务器已经完成数据传输,请求关闭连接。
- 第四步:客户端发送 ACK
- 客户端收到服务器发送的 FIN 数据包后,会向服务器发送一个带有 ACK 标志的数据包,表示确认服务器的关闭请求。此时,TCP 连接关闭完成,双方都结束了数据传输。
6. for...in和for...of的区别
遍历的内容:for...in遍历的是key,可以通过键访问对应的值; for...of遍历的是value
const arr = [10, 20, 30]
for (let key in arr) {
console.log(key) // 0 1 2
}
for (let val of arr) {
console.log(val) // 10 20 30
}
适用的数据类型不同:for...in:用于遍历对象的可枚举属性(对象、数组和字符串),包括原型链上的属性; for...of:用于遍历可迭代对象,如数组、字符串、Set、Map,类数组对象(arguments、NodeList) 等。
例如: 遍历对象 : for...in 可以, for...of 不可以
遍历Map Set: for...in 不可以, for...of 可以
遍历generateor: for...in 不可以, for...of 可以
const obj = {
name: 'czh',
city: '北京'
}
for (let val of obj) { // 对象不可以用for...of
console.log(val) // VM449:5 Uncaught TypeError: obj is not iterable
}
const map = new Map([
['x', 100],
['y', 200],
['z', 300]
])
for (let n of map) { //map可用for...of,不可用for...i n
console.log(n)
}
function* foo() {
yield 10
yield 20
yield 30
}
for(let n of foo()){ //generater可用for...of,不可用for...i n
console.log(n)
}
相关问题:for await...of有什么作用?
for await...of 是 ES2018 中新增的一个语法,它是 for...of 的异步版本,用于遍历多个Promise。
function createPromise(val) {
return new Promise ((resolve) => {
setTimeout(() => {
resolve(val)
},1000)
})
}
(async function () {
const p1 = createPromise(100)
const p2 = createPromise(200)
const p3 = createPromise(300)
const res1 = await p1
console.log(res1)
const res2 = await p2
console.log(res2)
const res3 = await p3
console.log(res3)
const list = [p1,p2,p3]
Promise.all(list).then(res => console.log(res)) // API形式
for await(let res of list) { // 循环中获取所有Promise运行结果
console.log(res)
}
})()