一、http面试
1、状态码
1.1 状态码分类
- 1xx 服务器收到请求
- 2xx 请求成功-如200
- 3xx 重定向-如302
- 4xx 客户端错误-如404
- 5xx 服务端错误-如500
1.2 常见状态码
- 200 成功
- 301 永久重定向(配合location,浏览器自动处理)
- 302 临时重定向(配合location,浏览器自动处理)
- 304 资源未被修改--缓存中很重要
- 404 资源未找到
- 403 没有权限-如未登录或权限等
- 500 服务器错误
- 504 网关超时
2、http methods
2.1 传统methods
2.2 现在methods
- get 获取数据
- post 新建数据
- patch、put 更新数据
- delete 删除数据
2.3 Restful API
- 是一种新的API设计方法
- 传统API设计是把每一个url当作一个功能(function)
- Restful API是把每一个url当作一个唯一的资源的标识
2.4 如何设计成一个资源
- 尽量不用url参数,如(/api/list?pageIndex=2)
- 用method表示操作类型,如(/api/list/2)
3、http headers
3.1 常见的Request Headers
- Accept 浏览器可接收的数据格式
- Accept-Encoding 浏览器可接收的压缩算法,如gzip
- Accept-Languange 浏览器可接收的语言,如zh-CN
- Connection:keep-alive 一次TCP链接重复使用
- cookie
- Host
- User-Agent(简称UA)浏览器信息,如什么浏览器
- Content-type 发送数据的格式,如application/json
3.2 常见的Response Headers
- Content-type 返回数据的格式,如application/json
- Content-length 返回数据的大小,多少字节
- Content-Encoding 返回的压缩算法,如gzip
- Set-Cookie
3.3 缓存相关的headers
- Cache-Control Expries
- Last-Modified If-Modified-Since
- Etag If-None-Match
4、http缓存
4.1 关于缓存介绍
- 何为缓存?
- 为什么需要缓存?
- 哪些资源可以被缓存?----静态资源(js、css、img)
4.2 http缓存策略(强制缓存+协商缓存)
4.21 强制缓存
- 在Response Headers中
- 控制强制缓存的逻辑
- 例如Cache-Control:max-age=31536000(单位是秒)
- Cache-Control的值
- max-age 缓存过期事件
- no-cache 不用强制缓存,正常向服务器请求
- no-store 不用强制缓存,也不用服务端做缓存,直接让服务端重新返回一份资源
- private 只能用户做缓存
- public 中间代理也可做缓存
- 关于Expires
- 同在Response Headers中
- 同为控制缓存过期
- 已被Cache-Control代替
4.22 协商缓存
- 服务器端缓存策略----服务端来判断一个是否可以被缓存
- 服务器判断客户端资源,是否和服务端资源一样
- 一致则返回304,否则返回200和最新的资源
4.23 资源标识
- 在Response Headers中,有两种
- Last-Modified If-Modified-Since 资源的最后修改时间
- Etag If-None-Match 资源的唯一标识(一个字符串,类似人类的指纹)
4.24 Last-Modified和Etag
- 会优先使用Etag
- Last-Modified只能精确到秒级
- 如果资源被重复生成,而内容不变,则Etag更精准
二、作用域和闭包
1、作用域和自由变量
- 某个变量合法的使用范围
- 全局作用域、函数作用域、块级作用域(ES6新增)
- 自由变量
- 一个变量在当前作用域没有定义,但被使用了
- 向上级作用域。一层一层依次寻找,直至找到为止
- 如果到全局作用域都还没找到,则报错xx is not defined
- 所有的自由变量的查找,是在函数定义的地方,向上级作用域查找,不是在执行的地方查找
2、闭包
- 实际上是作用域应用的特殊情况,有两种表现
- 1、函数作为参数被传递
- 2、函数作为返回值被返回
- 所有的自由变量的查找,是在函数定义的地方,向上级作用域查找,不是在执行的地方查找
3、this几种赋值情况
- 作为普通函数去调用
- 使用call apply bind
- 作为对象方法去调用
- 在class方法中去调用
- 箭头函数
- 重点:this取何值,是在函数执行的时候确定的,不是在函数定义的时候定义的,以上五种场景都适用
4、this在不同应用场景下,应该如何取值?重要之中的重要
5、手写bind函数
6、闭包在实际开发中的使用场景,举例说明
三、异步和单线程
1、两者区别
为什么会有异步
1、js是单线程语言,只能同时做一件事
2、浏览器和nodejs已经支持JS启动进程,如Web Worker
3、JS和DOM渲染共用同一个进程,因为JS可以修改DOM结构
4、如果遇到等待(如网络请求,定时任务)时,不能卡住,所以需要异步
5、异步是基于callback回调函数形式来调用的
6、基于JS是单线程语言
7、异步不会阻塞代码执行,同步会阻塞代码执行
callback hell 和 Promise
1、callback回调地狱
2、Promise正是解决回调地狱的方法
2、手写Promise加载一张图片
function loadImage(src) {
return new Promise((resolve,reject)=>{
const img = document.createElement('img')
img.onload = () => {
resolve(img)
}
img.onerror = () => {
reject(new Error(`图片加载失败 ${src}`))
}
img.src = src
})
}
const url = 'https://i0.hdslb.com/bfs/banner/01155bdb8cb180cdae85209e1251261727eae95b.jpg@976w_550h_1c.webp'
loadImage(url).then(img => {
console.log(img.width)
return img
}).then(img => {
console.log(img.height)
}).catch(ex => console.error(ex))
3、前端使用异步的场景有哪些
- 网络请求,如Ajax图片加载
- 定时任务,如setTimeout
- 这两个时候,cpu是空闲的,不能浪费资源
四、异步--进阶
1、event loop
1.1 请描述event loop(事件循环/事件轮询)的机制,可画图
- js是单线程运行的
- 异步要基于回调来实现
- event loop就是异步回调的实现原理
- js是从前到后,一行一行执行,如果某一行执行报错,则停止下面的代码的执行
- 先把同步代码执行完,再执行异步
1.11 总结event loop过程1
- 同步代码,一行一行放在Call Stack执行
- 遇到异步,会先“记录”下,等待时机(定时、网络请求等)
- 时机到了,就移动到Callback Queue
1.12 总结event loop过程2
- 如Call Stack为空(即同步代码执行完)Event Loop开始工作
- 轮询查找Callback Queue,如有则移动到Call Stack执行
- 然后继续轮询查找(永动机一样)
2、promise进阶
2.1 Promise有哪三种状态?
pending resolved rejected
pending->resolved或pending->rejected
变化不可逆
2.2 如何表现和变化?
pending状态,不会触发then和catch
resolved状态,会触发后续的then回调函数
rejected状态,会触发后续的catch回调函数
const p1 = Promise.resolve(100);
console.log('p1',p1)
p1.then(data=>{
console.log('data',data);
}).catch(err=>{
console.log('err', err);
})
const p2 = Promise.reject('error');
console.log('p2',p2)
p1.then(data=>{
console.log('data',data);
}).catch(err=>{
console.log('err2', err);
})
2.3 then和catch对状态的影响?
then正常返回resolved,里面有报错则返回rejected
const p1 = Promise.resolve().then(() => {
return 100
})
console.log('p1', p1);
p1.then(()=>{
console.log('123');
})
const p2 = Promise.resolve().then(() => {
throw new Error('then error')
})
console.log('p2', p2);
p2.then(()=>{
console.log('456');
}).catch(err=>{
console.log('err100', err);
})
catch正常返回resolved,里面有报错则返回rejected
const p3 = Promise.reject('my err').catch(err=>{
console.error('err', err);
})
console.log('p3', p3);
p3.then(()=>{
console.log(10000);
})
const p4 = Promise.reject('my err').catch(err=>{
throw new Error('catch error');
})
console.log('p4', p4);
p4.then(()=>{
console.log(20000);
}).catch(()=>{
console.log('some error');
});
2.4 场景题-promise then和catch的连接(返回什么状态,触发后续什么回调函数)
Promise.resolve().then(()=>{
console.log(1);
}).catch(()=>{
console.log(2);
}).then(()=>{
console.log(3);
})
Promise.resolve().then(()=>{
console.log(1);
throw new Error('error');
}).catch(()=>{
console.log(2);
}).then(()=>{
console.log(3);
})
Promise.resolve().then(()=>{
console.log(1);
throw new Error('error');
}).catch(()=>{
console.log(2);
}).catch(()=>{
console.log(3);
})
3、async、await
3.1 背景
异步回调callback hell
开始是用Promise then catch链式调用,但是也是基于回调函数
但是有了async/await是同步语法,彻底消灭回调函数
3.2 场景题-async/await语法
function loadImage(src) {
return new Promise((resolve, reject) => {
const img = document.createElement('img')
img.onload = () => {
resolve(img)
}
img.onerror = () => {
reject(new Error(`图片加载失败 ${src}`))
}
img.src = src
})
}
const src1 = 'https://img.bosszhipin.com/beijin/upload/image/20191225/3f7fda0998317f22ec614bfc392848b9.jpg?x-oss-process=image/format,jpg'
const src2 = 'https://img.bosszhipin.com/beijin/upload/image/20191225/719b5568228bda8229408e1401457f13.jpg?x-oss-process=image/format,jpg';
(async function(){
const img1 = await loadImage(src1)
console.log(img1.width,img1.height)
const img2 = await loadImage(src2)
console.log(img2.width,img2.height)
})()
3.3 async/await和Promise的关系
async/await是消灭异步的终极武器
但是和Promise并不互斥不冲突
反而两者相辅相成
1、执行async函数,返回的是Promise对象
async function fn1() {
return Promise.resolve(200);
}
const res1 = fn1()
console.log('res1', res1);
res1.then(data => {
console.log('data', data);
});
2、await相当于Promise的then,(注)
!(async function(){
const p1 = Promise.resolve(300)
const data = await p1
console.log("data", data);
})()
!(async function () {
const data1 = await 400
console.log("data1", data1);
})()
!(async function () {
const data2 = await fn1()
console.log("data2", data2);
})()
!(async function () {
const p5 = Promise.reject('err222');
const res = await p5
console.log(res);
})()
3、try...catch可捕获异常,代替了Promise的catch
!(async function () {
const p4 = Promise.reject('err111');
try {
const res = await p4
console.log(res);
} catch (ex) {
console.error(ex)
}
})()
3.4 异步的本质
async/await只是一个语法糖
async function async1() {
console.log("async1 start");
await async2()
console.log("async1 end");
}
async function async2() {
console.log("async2");
}
console.log("script start");
async1()
console.log("script end");
async function async1() {
console.log("async1 start");
await async2()
console.log("async1 end");
await async3()
console.log("async1 end 2");
}
async function async2() {
console.log("async2");
}
async function async3() {
console.log("async3");
}
console.log("script start");
async1()
console.log("script end");
3.5 for...of
for...in(以及forEach for)都是常规的同步遍历
for...of常用于异步的遍历
function muti(num) {
return new Promise(resolve => {
setTimeout(() => {
resolve(num * num)
}, 1000)
})
}
const nums = [1, 2, 3];
nums.forEach(async (i) => {
const res = await muti(i)
console.log(res);
})
!(async function () {
for (let i of nums) {
const res = await muti(i);
console.log(res);
}
})()
4、微任务(macroTask)和宏任务(microTask)
4.1 什么是宏任务和微任务?
宏任务:setTimeout,setInterval,Ajax,DOM事件(不是异步,但是也是依赖event loop来执行的,所以也把它放在这了)
微任务:Promise async/await
微任务执行时机比宏任务要早(先记住)
代码演示
console.log(100);
setTimeout(() => {
console.log(200);
})
setTimeout(() => {
console.log(201);
})
Promise.resolve().then(() => {
console.log(300);
})
console.log(400);
4.2 event loop 和 DOM渲染
再次回顾一遍event loop的过程
JS是单线程的,而且和DOM渲染共用一个线程
JS执行的时候,得留一些时机供DOM渲染
每次Call Stack 清空(即每次轮询结束),即同步任务执行完毕,都是DOM重新渲染的机会,DOM结构如有改变则重新渲染
然后再去触发下一次event loop
4.3 两者有什么区别?
宏任务:DOM渲染后触发,如setTimeout
微任务:DOM渲染前触发,如Promise
4.4 从event loop 解释,为什么微任务执行更早
微任务、宏任务、DOM渲染的关系
1、Call Stack清空-->2、执行当前的微任务-->3、尝试DOM渲染-->4、触发event
微任务是ES6语法规定的
宏任务是由浏览器规定的