promise
回调地狱
setTimeout(() => {
console.log('first floor')
setTimeout(() => {
console.log('secode floor')
setTimeout(() => {
console.log('thrid floor')
},1000)
},1000)
},1000)
//Promise 方法
new Promise((resolve, reject) => {
console.log('1')
setTimeout(()=>{
resolve('2')
},1000)
}).then(res => {
console.log(res)
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve( {msg:'3'})
},1000)
})
},err=>{
console.log(err)
}).then(res=>{
console.log(res.msg)
}
)
静态方法: Promise.resolve() Promise.reject() Promise.all() Promise.race()
(1) Promise.resolve()
static resolve(value) {
if (value instanceof Promise) {
// 如果是Promise实例,直接返回
return value;
} else {
// 如果不是Promise实例,返回一个新的Promise对象,状态为FULFILLED
return new Promise((resolve, reject) => resolve(value));
}
}
(2) Promise.reject()
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason);
})
}
(3) promiss.all Promise.all是支持链式调用的,本质上就是返回了一个Promise实例,通过resolve和reject来改变实例状态。
Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。
【返回一个promise对象,只有当所有promise都成功时返回的promise状态才成功,需要注意的点是:
1所有的promise状态变为FULFILLED,返回的promise状态才变为FULFILLED。
2一个promise状态变为REJECTED,返回的promise状态就变为REJECTED。
3数组成员不一定都是promise,需要使用Promise.resolve()处理。】
Promise.myAll = function(promiseArr) {
return new Promise((resolve, reject) => {
const ans = [];
let index = 0;
for (let i = 0; i < promiseArr.length; i++) {
promiseArr[i]
.then(res => {
ans[i] = res;
index++;
// 如果全部执行完,返回promise的状态就可以改变了
if (index === promiseArr.length) {
resolve(ans);
}
})
.catch(err => reject(err));
}
})
}
(4) promiss.race Promse.race就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。
Promise.race = function(promiseArr) {
return new Promise((resolve, reject) => {
promiseArr.forEach(p => {
// 如果不是Promise实例需要转化为Promise实例
Promise.resolve(p).then(
val => resolve(val),
err => reject(err),
)
})
})
}
同步异步
async function a(cb) {
console.log(1)//2
let a = await cb()
console.log(a)
console.log(2)//6
}
function b() {
return new Promise((resolve,reject) => {
console.log(3)//3
resolve(6)//5
})
}
console.log(4) //1
a(b)
console.log(5)//4
async| await |then
async function log() {
setTimeout(function () {
console.log(1)//4
},1000)
await console.log(2) //1
}
log().then(() =>{
console.log(3)//3
});
console.log(4)//2
Ajax
const getJSON = function(url) {
return new Promise((resolve, reject) => {
const xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Mscrosoft.XMLHttp');
xhr.open('GET', url, false);
xhr.setRequestHeader('Accept', '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();
})
}
跨域
- 1 同源策略:协议(http、https),域名,端口号(8080)必须完全一致
- 2 跨域:违背同源策略
- 3 解决跨域:jsonp (前端+后端),CORS(后端),服务器代理(后端)
- jsonp 利用script标签天然可跨域的属性
//创建script标签
var script = document.createElement('script')
//设置回调函数
function getData(data) {
//数据请求回来会被触发的函数
console.log(data)
}
//设置script 的scr属性,设置请求地址
script.scr = 'http:///localhost:3000?callback=getData'
//让script生效,添加到document.body的appendChild
document.body.appendChild(script)
4.服务器中路由的处理
router.get("/testAJAX" , function (req , res) {
console.log("收到请求");
var callback = req.query.callback; var obj = {name:"孙悟空",
age:18 }
res.send(callback+"("+JSON.stringify(obj)+")"); });
CORS CORS 全称是 跨域资源共享(Cross-Origin Resource Sharing),是官方的跨域解决方案, CORS 是通过设置一个响应头来告诉浏览器,该请求允许跨域,主要是服务器端的设置:
router.get("/testAJAX" , function (req , res) {
//通过 res 来设置响应头,来允许跨域请求
//res.set("Access-Control-Allow-Origin", "http://127.0.0.1:3000");
res.set("Access-Control-Allow-Origin","*");
res.send("testAJAX 返回的响应");
});
浏览器收到该响应 以后就会对响应放行。
1 当使用 XMLHttpRequest 发送请求时,浏览器发现该请求不符合同源策略,会给该请求加一个响应头:Origin;(浏览器无需做设置 只需发送ajax) 2 后台服务器收到请求后,会进行一系列处理,如果确定接受请求,则在返回结果中加入一个响应头:Access-Control-Allow-Origin (换言之,服务器允许的域url,会加入此响应头,相当于一个凭证); 3 浏览器判断该相应头中是否包含 Origin 的值,如果有,则浏览器会处理响应,我们就可以拿到响应数据。
webpack
webpack的loader和plugin区别
对于loader,它是一个转换器,将A文件进行编译形成B文件,这里操作的是文件,比如将A.scss转换为A.css,单纯的文件转换过程
plugin是一个扩展器,它丰富了webpack本身,针对是loader结束后,webpack打包的整个过程,它并不直接操作文件,而是基于事件机制工作,会监听webpack打包过程中的某些节点,执行广泛的任务
webpack的loader顺序 loader的执行顺序和配置中的顺序是相反的
图片懒加载(问了好几次)
可以给img标签统一自定义属性data-src='default.png',当检测到图片出现在窗口之后再补充src属性,此时才会进行图片资源加载。
function lazyload() {
const imgs = document.getElementsByTagName('img');
const len = imgs.length;
// 视口的高度
const viewHeight = document.documentElement.clientHeight;
// 滚动条高度
const scrollHeight = document.documentElement.scrollTop || document.body.scrollTop;
for (let i = 0; i < len; i++) {
const offsetHeight = imgs[i].offsetTop;
if (offsetHeight < viewHeight + scrollHeight) {
const src = imgs[i].dataset.src;
imgs[i].src = src;
}
}
}
// 可以使用节流优化一下
window.addEventListener('scroll', lazyload);
HTTP
tcp为什么可以按序到达 请求头(请求方式的请求头 缓存的请求头)
HTTP缓存
浏览器解析渲染页面
url到页面渲染完成过程: 1.DNS 解析:将域名地址解析为ip地址
浏览器DNS缓存->系统DNS缓存->路由器DNS缓存->网络运营商DNS缓存
->递归搜索(blog.baidu.com):.com域名下查找DNS解析->.baidu域名下查找DNS解析->blog域名下查找DNS解析->出错了
2.TCP连接:TCP三次握手
- 第一次握手:由浏览器发起,告诉服务器我要发送请求了
- 第二次握手:由服务器发起,告诉服务器我准备接受了,你块发送吧
- 第三次握手:由浏览器发送,告诉服务器,我马上就发了,准备接受吧
3.发送请求:请求报文:HTTP协议的通信内容
4.接受响应:响应报文
5.渲染页面:
- 遇见HTML标记,浏览器调用HTML解析器成Token并构建dom数
- 遇见style、link标记,浏览器调用css解析器,处理css标记并构建cssom数
- 遇见script标记,浏览器调用JavaScript解析器,处理script代码(绑定事件,修改dom树,cssom数)
- 将dom树和cssom树合并为一个渲染树
- 根据渲染树来计算布局,并计算每个节点的几何信息(布局)
- 将各个节点的颜色绘制到屏幕上(渲染) 注:这5个步骤不一定按顺序执行,如果dom树和cssom树被修改,可能会执行多次布局和渲染。往往在实际页面中这些步骤都会多次执行
- 断开连接:TCP四次挥手
- 第一次挥手,由浏览器发起,发给服务器,我的请求报文发送完了,你准备关闭吧
- 第二次挥手,由服务器发起,发给浏览器,我的请求报文接受完了,我准备关闭了,你也准备吧
- 第三次挥手,由服务器发起,发给浏览器,我的响应报文发送完了,你准备关闭吧
- 第四次挥手,由浏览器发起,发给服务器,我的响应报文接受完了,我准备关闭了,你也准备吧
reflow(回流)和repain(重绘)。DOM节点中的各个元素都是以盒模型的形式存在,这些都需要浏览器去计算其位置和大小等,这个过程称为relow;当盒模型的位置,大小以及其他属性,如颜色,字体,等确定下来之后,浏览器便开始绘制内容,这个过程称为repain。页面在首次加载时必然会经历reflow和repain。reflow和repain过程是非常消耗性能的,尤其是在移动设备上,它会破坏用户体验,有时会造成页面卡顿。所以我们应该尽可能少的减少reflow和repain。
JS的解析是由浏览器中的JS解析引擎完成的,事件循环(Event loop)。
浏览器在解析过程中,如果遇到请求外部资源时,如图像,iconfont,JS等。浏览器将重复1-6过程下载该资源。请求过程是异步的,并不会影响HTML文档进行加载,但是当文档加载过程中遇到JS文件,HTML文档会挂起渲染过程,不仅要等到文档中JS文件加载完毕还要等待解析执行完毕,才会继续HTML的渲染过程。原因是因为JS有可能修改DOM结构,这就意味着JS执行完成前,后续所有资源的下载是没有必要的,这就是JS阻塞后续资源下载的根本原因。CSS文件的加载不影响JS文件的加载,但是却影响JS文件的执行。JS代码执行前浏览器必须保证CSS文件已经下载并加载完毕。
6 断开连接:TCP 四次挥手
- 发起方向被动方发送报文,Fin、Ack、Seq,表示已经没有数据传输了。并进入 FIN_WAIT_1 状态。 (第一次挥手:由浏览器发起的,发送给服务器,我请求报文发送完了,你准备关闭吧)
- 被动方发送报文,Ack、Seq,表示同意关闭请求。此时主机发起方进入 FIN_WAIT_2 状态。 (第二次挥手:由服务器发起的,告诉浏览器,我请求报文接受完了,我准备关闭了,你也准备吧)
- 被动方向发起方发送报文段,Fin、Ack、Seq,请求关闭连接。并进入 LAST_ACK 状态。 (第三次挥手:由服务器发起,告诉浏览器,我响应报文发送完了,你准备关闭吧)
- 发起方向被动方发送报文段,Ack、Seq。然后进入等待 TIME_WAIT 状态。被动方收到发起方的报文段以后关闭连接。发起方等待一定时间未收到回复,则正常关闭。 (第四次挥手:由浏览器发起,告诉服务器,我响应报文接受完了,我准备关闭了,你也准备吧)
node
nodejs的事件循环机制:
借助libuv的库实现的
- 1 timer 定时器阶段:计时和执行到点的定时器回调函数
- 2 pending callback :某些系统操作(例如TCP错误类型)的回调函数
- 3 idle,prepare 准备阶段
- 4 poll 轮询阶段(轮询队列):如果轮询队列不为空,依次同步取出轮询队列中的第一个回调函数执行,直到轮询队列为空,或达到系统最大的限制,如果轮询队列为空,并且事前设置过setImmediate函数,直接进入下一个check阶段;如果没有设置setImmediate函数,直接进入下一个check阶段,在当前poll阶段等待,直到轮询队列添加回调函数,就去第一个情况执行,如果定时器到点了,就去下一个阶段。
- 5 check阶段 执行setImmediate设置的回调函数
- 6 close callbacks 关闭阶段:执行close事件回调函数
//process.nextTick 优先执行
setTimeout(function () {
console.log('setTimeout()')//2
},0)
setImmediate(function () {
console.log('setImmediate()')//3
})
process.nextTick(function () {
console.log('process.nextTick()')//1
})
node的写入文件操作 node写入文件分为同步跟异步有什么区别?
二面
深挖项目(哭了,挖到不知道说啥了,提到了优化) 3、你刚刚说到了图片懒加载,怎么实现的?有用过懒加载的插件吗? 4、怎么判断元素是否在可视范围内?
7、做过登录功能吗?怎么实现的? 8、怎么记录用户登录的状态?具体的? 9、为什么有了cookie还要有token 11、最近有看什么新的技术文章吗,给我讲讲(说了web安全) 12、反问,你刚刚说xss的时候说到了给cookie设置httponly,为什么要操作cookie?要配合什么来用?
14、扫码登录怎么实现的?结合token怎么做?你刚刚说最后会返回给用户浏览器,返回之后会做什么? 15、ts怎么扩展类?能写一下吗
- 问项目
- 前端工程化 => 如何解决webpack打包慢。
- 前端前沿技术了解 balabala...