一、Http相关
1. 状态码分类
- 1xx - 服务器收到请求。
- 2xx - 请求成功,如 200。
- 3xx - 重定向,如 302。
- 4xx - 客户端错误,如 404。
- 5xx - 服务端错误,如 500。
2. 常见状态码
- 200 - 成功。
- 301 - 永久重定向(配合 location,浏览器自动处理)。
- 302 - 临时重定向(配合 location,浏览器自动处理)。
- 304 - 资源未被修改。
- 403 - 没权限。
- 404 - 资源未找到。
- 500 - 服务器错误。
- 504 - 网关超时。
3. HTTP1.0、HTTP1.1 和 HTTP2.0 的区别
HTTP1.1 v HTTP1.0
- 默认长连接
- 请求头增加host
- 请求头增加range 支持断点续传
- 增加一些方法(OPTIONS,PUT, DELETE, TRACE, CONNECT)
- 增加24个状态码 100等
- 增加cache-control等缓存处理
HTTP2 v HTTP1.1
- 报文头部压缩,差异更新HTTP头部,减少少头部流量
- 多路复用,多个请求在同一个TCP上完成
- 服务端推送
4. http和https区别是什么
- 传输信息安全性不同
http是明文传输,https是ssl or TLS 加密传输
- 连接方式不同
http连接简单是无状态的,https是由ssl+http协议构建的加密传输 身份认证的网络协议
- 端口不同(80 443)
- https的服务器需要到CA申请证书
5. http缓存
5.1 强制缓存
Cache-Control:
- 在 Response Headers 中。
- 控制强制缓存的逻辑。
- 例如 Cache-Control: max-age=3153600(单位是秒)
Cache-Control 有哪些值:
- max-age:缓存最大过期时间。
- no-cache:可以在客户端存储资源,每次都必须去服务端做新鲜度校验,来决定从服务端获取新的资源(200)还是使用客户端缓存(304)。
- no-store:永远都不要在客户端存储资源,永远都去原始服务器去获取资源。
5.2 协商缓存(对比缓存)
- 服务端缓存策略。
- 服务端判断客户端资源,是否和服务端资源一样。
- 一致则返回 304,否则返回 200 和最新的资源。
资源标识:
- 在 Response Headers 中,有两种。
- Last-Modified:资源的最后修改时间。
- Etag:资源的唯一标识(一个字符串,类似于人类的指纹)。
Last-Modified: 服务端拿到 if-Modified-Since 之后拿这个时间去和服务端资源最后修改时间做比较,如果一致则返回 304 ,不一致(也就是资源已经更新了)就返回 200 和新的资源及新的 Last-Modified。
Etag: 其实 Etag 和 Last-Modified 一样的,只不过 Etag 是服务端对资源按照一定方式(比如 contenthash)计算出来的唯一标识,就像人类指纹一样,传给客户端之后,客户端再传过来时候,服务端会将其与现在的资源计算出来的唯一标识做比较,一致则返回 304,不一致就返回 200 和新的资源及新的 Etag。
两者比较:
- 优先使用 Etag。
- Last-Modified 只能精确到秒级。
- 如果资源被重复生成,而内容不变,则 Etag 更精确。
5.3 综述
5.4 三种刷新操作对 http 缓存的影响
- 正常操作:地址栏输入 url,跳转链接,前进后退等。
- 手动刷新:f5,点击刷新按钮,右键菜单刷新。
- 强制刷新:ctrl + f5,shift+command+r。
正常操作:强制缓存有效,协商缓存有效。 手动刷新:强制缓存失效,协商缓存有效。 强制刷新:强制缓存失效,协商缓存失效。
6. 从输入URL到浏览器显示页面过程中都发生了什么?
- 域名解析
- 判断是否有
expires&cache control强缓存 有则本地获取资源;否则向服务器发起请求(先进行第三步Tcp连接)服务器通过Etag&last-modify来进行协商缓存,无更改则返回304 Not Modified, 浏览器取本地缓存- TCP三次握手建立连接
- 浏览器发送HTTP请求Html
- 服务器响应请求返回Html
- 浏览器解析html成一个Dom 树,解析Css规则树,将其合并构造渲染树(期间ajax请求可同步进行)
- 回流:在渲染树的基础上进行布局,计算每个节点的几何结构
- 重绘:重新绘制界面发生变化的部分。
- 连接结束:TCP四次挥手
二、JS基础
1. 继承有哪些方式
1.1 原型链继承
原理:使用父类实例重写子类原型
缺点:父类引用类型会被所有子类共享,无法向父类构造函数传递参数
function Father(){
this.name = '父亲'
}
Father.prototype.say = function(){
return this.name
}
function Son(){}
Son.prototype = new Father()
let instance = new Son()
console.log(instance.say())
1.2 借用构造函数继承
原理:改变this指向,使父类属性重写到子类
缺点:父类原型上的方法无法复用
function Father(name='父亲'){
this.name = name
this.arr = [1,2,3]
}
Father.prototype.say = function(){
console.log(this.name)
}
function Son(name){
Father.call(this,name)
}
let instance = new Son('儿子')
instance.arr.push(4)
console.log(instance) // { arr: [1, 2, 3, 4], name: "儿子" }
let instance1 = new Son()
console.log(instance1) //{ arr: [1, 2, 3], name: "父亲" }
console.log(instance1.say()) //instance1.say is not a function
1.3 组合继承
缺点:调用了两次父类型构造函数
function Father(name) {
this.name = name
}
Father.prototype.say = function () {
console.log(this.name)
}
function Son(name, age) {
// 继承属性
Father.call(this, name)
this.age = age
}
// 继承方法
Son.prototype = new Father()
// Son.prototype.constructor = Son
Son.prototype.getAge = function () {
console.log(this.age)
}
let instance = new Son('儿子', 18)
instance.say() // "儿子"
instance.getAge() // "18"
1.4 原型式继承&寄生式继承
缺点:父类引用类型会被所有子类共享
function object(obj){
function F(){}
F.prototype = obj
return new F()
}
function createNewObj(obj){
let newObj = object(obj)
newObj.say = function(say){
console.log('hi '+say)
}
return newObj
}
let person = {
name:'张三',
age:18,
arr:[2,3]
}
let instance = createNewObj(person)
instance.say('eee') //"hi eee"
instance.arr.push(4)
instance.name = '李四'
let instance1 = createNewObj(person)
console.log(instance1.arr) //[2, 3, 4] 共享引用属性会被篡改
1.5 寄生组合式继承
function Father(name = '父亲') {
this.name = name
this.arr = [1, 3]
}
Father.prototype.say = function () {
console.log(this.name)
}
function Son(name) {
Father.call(this, name)
}
Son.prototype = Object.create(Father && Father.prototype, {
constructor: { value: Son, writable: true, configurable: true }
})
let instance = new Son('儿子')
instance.say() // "儿子"
instance.arr.push(5)
let instance1 = new Son('儿子1')
console.log(instance1.arr) // [1, 3]
2. 原型链
经典原型链图
Object.prototype.__proto__ === null Object.prototype是Object构造函数的原型,处于原型链的顶端
Object.__proto__ === Function.prototype (Object是构造函数,即也是函数,所以Object也是函数对象,相当于Function的实例)
Function.prototype === Function.__proto__ (最顶级)
Function.prototype.__proto__ === Object.prototype (函数的原型是一个普通object实例)
function User(){}
var u1 = new User()
var u2 = new User()
console.log(u1.__proto__ === User.prototype) // true
原因:实例的隐式原型指向构造函数的prototype
console.log(User.prototype.__proto__ === Function.prototype.__proto__) // true
原因:均指向Object.protoType,函数的原型是一个普通object实例
console.log(User.__proto__ === Function.__proto__)// true
原因:实例的隐式原型指向构造函数的prototype `User.__proto__ === Fuction.protoType`
console.log(Function.__proto__ === Object.__proto__) // true
原因:`Function.prototype === Function.__proto__`
3. Promise then 第二个参数和catch的区别是什么?
主要区别:.then第一个参数函数内的错误,then第二个参数的err无法捕捉,catch均可以捕捉,建议处理逻辑放在catch,不使用then第二个函数
4. async/await原理, generator, promise这三者的关联和区别是什么?
generator+promise 实现 async/await
async只是generator的一个语法糖,他相当于*,await相当于yield
async和 await,比起星号和 yield,语义更清楚了
yield命令后面只能是 Thunk函数或 Promise对象,而 async函数的 await命令后面,可以跟 Promise对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操
参考:www.jianshu.com/p/5adc35c8b…
5. Promise finally 怎么实现的
finally和catch都是then函数的语法糖
Promise.prototype.finally = function (callback) {
return this.then(data => {
return Promise.resolve(callback()).then(() => data)
},
err => {
return Promise.resolve(callback()).then(() => { throw err })
})
};
6. 宏任务(Macrotask)和微任务(Microtask)
宏任务包括:script(整体代码), setTimeout, setInterval, requestAnimationFrame, I/O,setImmediate。
其中setImmediate只存在于Node中,requestAnimationFrame只存在于浏览器中。
微任务包括: Promise, MutationObserver(html5新特性),process.nextTick。
其中process.nextTick只存在于Node中,MutationObserver只存在于浏览器中。
注意:
执行方式:执行一个宏任务,过程中遇到微任务时,将其放到微任务的事件队列里,当前宏任务执行完成后,会查看微任务的事件队列,依次执行里面的微任务。如果还有宏任务的话,再重新开启宏任务……
测试题
console.log('a');
setTimeout(function() {
console.log('b');
process.nextTick(function() {
console.log('c');
})
new Promise(function(resolve) {
console.log('d');
resolve();
}).then(function() {
console.log('e')
})
})
process.nextTick(function() {
console.log('f');
})
new Promise(function(resolve) {
console.log('g');
resolve();
}).then(function() {
console.log('h')
})
setTimeout(function() {
console.log('i');
process.nextTick(function() {
console.log('j');
})
new Promise(function(resolve) {
console.log('k');
resolve();
}).then(function() {
console.log('l')
})
})
三、手写代码系列
1 实现new操作符
function new1(Fn,...rest){
let obj = {}
obj.__proto__ = Fn.protoType
let res = Fn.apply(obj,[...rest])
return res instanceof Object?res:obj
}
function A(name){
this.a= name
}
let b = new1(A,'w飞机呃呃')
console.log(b) //{ a: "w飞机呃呃" }
2. 实现call & apply & bind
Function.protoType.call1=function(ctx,...rest){
ctx.fn = this
let res = ctx.fn(...rest)
delete ctx.fn
return res
}
Function.protoType.apply1=function(ctx){
ctx.fn = this
let res =arguments(1)? ctx.fn(...arguments(1)):ctx.fn()
delete ctx.fn
return res
}
Function.protoType.mybind = function(ctx,...rest){
let fn = this
function a(){
return fn.apply(this instanceof fn? this:ctx,[...rest,...arguments])
// 此处判断this instanceof fn 原因是因为: let fn1 = fn.bind(obj);let fn2 = new fn1()这种情况,需要使用构造函数fn1当做this,而不是obj
}
a.protoType = Object.creat(fn.protoType)
return a
}
fuction a(name){
this.name = name
}
let obj1 = {age: 34}
let f1 = a.mybind(obj1)('wo')
console.log(obj1) // { age: 34, name: "wo" }
let newData = new f1('new')
console.log(newData) //{ name: "new" }
3. 实现Promise
class Mypromise {
constructor(fn) {
this.status = 'pengding'
this.value = undefined
this.msg = undefined
this.resolveArr = []
this.rejectArr = []
let resolve = value => {
if (this.status === 'pengding') {
this.status = 'resolved'
this.value = value
this.resolveArr.forEach(fn=>fn())
}
}
let reject = value => {
if (this.status === 'pengding') {
this.status = 'rejected'
this.value = value
this.rejectArr.forEach(fn=>fn())
}
}
try {
fn(resolve, reject)
} catch (e) {
reject(e)
}
}
then(fn1, fn2) {
if (this.status === 'resolved') {
fn1(this.value)
}
if (this.status === 'rejected') {
fn2(this.value)
}
if(this.status === 'pengding'){
this.resolveArr.push(()=>fn1(this.value))
this.rejectArr.push(()=>fn2(this.value))
}
}
}
new Mypromise((resolve, reject) => {
setTimeout(()=>{
resolve(6)
},1000)
}).then(res => {
console.log('success',res)
}, res2 => {
console.error('222', res2)
})
4. 实现防抖和节流
function debounce(fn, delay = 0) {
let timer = null
return function () {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = null
}, delay)
}
}
function debounce(fn, wait, immediate) {
let timer = null
return function () {
let doNow = !timer && immediate
if (doNow) {
fn.apply(this, arguments)
}
clearTimeout(timer)
let fn1 = function () {
timer = null
if (!immediate) {
fn.apply(this, arguments)
}
}
timer = setTimeout(fn1, wait)
}
};
function throttle(fn, delay = 0) {
let timer = null
return function () {
if (timer) {
return
}
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = null
},delay)
}
}
document.addEventListener('keyup', throttle(function () {
console.log(2)
}, 600))
5. 实现深拷贝
function deepClone(obj){
if(typeof obj === 'object'&&obj!==null){
var result = Array.isArray(obj)?[]:{}
for(let key in obj){
result[key]=typeof obj[key] === 'object'?deepClone( obj[key]):obj[key]
}
}else{
var result = obj
}
return result
}
6. 实现instanceof
function instance(left, right) {
if (typeof left !== 'object' && typeof left !=='function' || typeof left === null) {
return false
}
if (typeof right !== 'function') {
throw Error('right is not a function')
}
let proto = left.__proto__
let prototype = right.prototype
while (true) {
if(proto === null){return false}
if (proto === prototype) { return true }
proto = proto.__proto__
}
}
function a(){
this.a = '2'
}
let b = new a()
console.log(instance(b, a))
7. 实现curry函数
function toCurry(fn,len){
var len = len || fn.length
return function curry(){
var args = [...arguments]
if(len<=args.length){
return fn.apply(null,args)
}else{
return function(){
return curry.apply(null,[...args,...arguments])
}
}
}
}
function add (x,y,z){
return x+y+z
}
var a = toCurry(add,3)
console.log(a(1,2,3))
console.log(a(1)(2,3))
8. 递归实现数组转树结构
const arr = [{
id: 2,
name: '部门B',
parentId: 0
},
{
id: 3,
name: '部门C',
parentId: 1
},
{
id: 1,
name: '部门A',
parentId: 2
},
{
id: 4,
name: '部门D',
parentId: 1
},
{
id: 5,
name: '部门E',
parentId: 2
},
{
id: 6,
name: '部门F',
parentId: 3
},
{
id: 7,
name: '部门G',
parentId: 2
},
{
id: 8,
name: '部门H',
parentId: 4
}
]
function toTree(arr,parentId){
return arr.filter(v=>v.parentId === parentId).map(v=>{
return {...v,children:toTree(arr,v.id)}
})
}
console.log(toTree(arr,0))
9. 去除字符串出现次数最少的字符,不改变字符顺序
var str = 'abccddde'
function del(str) {
let obj = {}
for (let key of str) {
obj[key] ? obj[key]++ : obj[key] = 1
}
let temp
// for (let key in obj) {
// if (!temp || obj[key] <= temp) {
// temp = obj[key]
// }
// }
temp = Math.min(...Object.values(obj))
for (let key in obj) {
if (obj[key] === temp) {
let reg = new RegExp(key, 'g')
str = str.replace(reg, '')
}
}
return str
}
console.log(del(str))
10. 数组扁平化
var arr = [1, 3, [4, 5, [6, 7]]]
arr.flat(Infinity)
function flat(arr) {
return arr.reduce((pre, current) => {
return pre.concat(Array.isArray(current) ? flat(current) : current)
}, [])
}
function flat1(arr) {
while (arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr)
}
return arr
}
// console.log(flat(arr))
// console.log(flat1(arr))
11. 实现Promise限制并发请求,并且最快速度请求完成,返回的按顺序排列
function creq(urls = [], limit = 6) {
return new Promise((resolve) => {
if (urls.length == 0) {
resolve([])
}
let i = 0; // 执行的请求的下标
let result = []; // 存储执行完的结果
let count = 0; //执行完的函数的个数
function request() {
if (i === urls.length) return
let url = urls[i]
let index = i;
i++
new Promise(resolve => {
setTimeout(() => {
resolve(url)
},Math.random()*5*1000)
}).then(res => {
console.log('ressss',res)
result[index] = res
count++
if (count === urls.length) {
resolve(result)
}
request()
}).catch(err => {
if (count === urls.length) {
resolve(result)
}
result[index] = err
count++
request()
})
}
const times = Math.min(limit, urls.length);
for (let i = 0; i < times; i++) {
request();
console.log(i)
}
})
}
const urls = [];
for (let i = 1; i <= 20; i++) {
urls.push(`https:------${i}`);
}
creq(urls, 6).then(res => {
console.log(res);
})
12. 实现sleep函数
const sleep1 = function (time) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(), time)
})
}
let do1 = async function () {
console.log(0)
await sleep1(2000)
console.log(1)
await sleep1(2000)
console.log(2)
}
do1()
13. 快速排序
这里对快排思想不太明白的同学可以看下这个讲解的很清晰的视频:快速排序算法。
function sortArray(nums) {
quickSort(0, nums.length - 1, nums);
return nums;
}
function quickSort(start, end, arr) {
if (start < end) {
const mid = sort(start, end, arr);
quickSort(start, mid - 1, arr);
quickSort(mid + 1, end, arr);
}
}
function sort(start, end, arr) {
const base = arr[start];
let left = start;
let right = end;
while (left !== right) {
while (arr[right] >= base && right > left) {
right--;
}
arr[left] = arr[right];
while (arr[left] <= base && right > left) {
left++;
}
arr[right] = arr[left];
}
arr[left] = base;
return left;
}
四、 代码题
1. 自执行函数输出什么
//
var b = 10;
(function b(){
b = 20;
console.log(b); // function b 自执行函数赋值无效
// var b = 10; // 如果加了这句 那么输出 20 10
console.log(b)
})();
2. 虚拟dom转真实dom
const vnode = {
tag: 'DIV',
attrs: {
id: 'app'
},
children: [{
tag: 'SPAN',
children: [{
tag: 'A',
children: []
}]
},
{
tag: 'SPAN',
children: [{
tag: 'A',
children: []
},
{
tag: 'A',
children: []
}
]
}
]
}
function render(vnode) {
}
3. this指向题
var name = '123';
var obj = {
name: '456',
print: function() {
function a() {
console.log(this.name);
}
a();
}
}
obj.print(); // 123
//对象方法中嵌套了方法,那么内部方法的this指向会混乱,指向全局对象,而不是期望的当前对象:
var a=3;
function c(){
alert(a);
}
(function(){
var a=4;
c();
})();
// 3 js 中变量的作用域链与定义时的环境有关,与执行时无关。执行环境只会改变 this、传递的参数、全局变量等
function Foo(){
Foo.a = function(){
console.log(1);
}
this.a = function(){
console.log(2)
}
}
Foo.prototype.a = function(){
console.log(3);
}
Foo.a = function(){
console.log(4);
}
Foo.a();
let obj = new Foo();
obj.a();
Foo.a();
Foo.a() 这个是调用 Foo 函数的静态方法 a,虽然 Foo 中有优先级更高的属性方法 a,但 Foo 此时没有被调用,所以此时输出 Foo 的静态方法 a 的结果:4
let obj = new Foo(); 使用了 new 方法调用了函数,返回了函数实例对象,此时 Foo 函数内部的属性方法初始化,原型链建立。
obj.a() ; 调用 obj 实例上的方法 a,该实例上目前有两个 a 方法:一个是内部属性方法,另一个是原型上的方法。当这两者都存在时,首先查找 ownProperty ,如果没有才去原型链上找,所以调用实例上的 a 输出:2
Foo.a() ; 根据第2步可知 Foo 函数内部的属性方法已初始化,覆盖了同名的静态方法,所以输出:1
原文链接:https://blog.csdn.net/MFWSCQ/article/details/105148289
const obj = {
fn1: () => console.log(this),
fn2: function() {console.log(this)}
}
obj.fn1();
obj.fn2();
const x = new obj.fn1();
const y = new obj.fn2();
五、 Vue相关
1. keep-alive 原理
created & destroyed
- created钩子会创建一个cache对象,用来作为缓存容器,保存vnode节点。
- keys用来缓存虚拟dom的键集合
- destroyed钩子则在组件被销毁的时候清除cache缓存中的所有组件实例。
mounted
在mounted这个钩子中对include和exclude参数进行监听,然后实时地更新(删除)this.cache对象数据。
render
- 获取keep-alive包裹着的第一个子组件对象及其组件名;
- 根据设定的黑白名单(如果有)进行条件匹配,决定是否缓存。不匹配,直接返回组件实例(VNode),否则执行第三步;
- 根据组件ID和tag生成缓存Key,并在缓存对象中查找是否已缓存过该组件实例。如果存在,直接取出缓存值并更新该key在this.keys中的位置(更新key的位置是实现LRU置换策略的关键),否则执行第四步;
- 在this.cache对象中存储该组件实例并保存key值,之后检查缓存的实例数量是否超过max的设置值,超过则根据LRU置换策略删除最近最久未使用的实例(即是下标为0的那个key); 最后将该组件实例的keepAlive属性值设置为true;
六、 错误捕捉方式
1. 线上监控 对于crashed这种怎么监控?
基于 Service Worker 心跳的方案: zhuanlan.zhihu.com/p/40273861
2.错误信息是最基础也是最重要的数据,错误信息主要分为下面几类:
- JS 代码运行错误、语法错误等
- 异步错误等
- 静态资源加载错误
- 接口请求报错
1. try/catch
只能捕获代码常规的运行错误,语法错误和异步错误不能捕获到
2. window.onerror
window.onerror 可以捕获常规错误、异步错误,但不能捕获资源错误
3. error
当静态资源加载失败时,会触发 error 事件。
window.addEventListener('error', (error) => { console.log('捕获到异常:', error); }, true)
4. Promise中抛出的错误
Promise中抛出的错误,无法被 window.onerror、try/catch、 error 事件捕获到,可通过 unhandledrejection 事件来处理
window.addEventListener("unhandledrejection",
function(e) {
console.log("捕获到异常", e); // preventDefault阻止传播,不会在控制台打印
e.preventDefault();
}
);
5. vue 异常监控
Vue.config.errorHandler = (err, vm, info) => { console.log('进来啦~', err); }
七、安全问题
1. XSS & CSRF攻击
八、性能优化
代码层面:
- 防抖和节流(resize,scroll,input)。
- 减少回流(重排)和重绘。
- 事件委托。
- 减少 DOM 操作。
- 按需加载
构建方面:
- 压缩代码文件,在 webpack 中使用
terser-webpack-plugin压缩 Javascript 代码;使用css-minimizer-webpack-plugin压缩 CSS 代码;使用html-webpack-plugin压缩 html 代码。 - 开启 gzip 压缩,webpack 中使用
compression-webpack-plugin,node 作为服务器也要开启,使用compression。 - 常用的第三方库使用 CDN 服务,在 webpack 中我们要配置 externals,将比如 React, Vue 这种包不打倒最终生成的文件中。而是采用 CDN 服务。
其它:
- 使用 http2。因为解析速度快,头部压缩,多路复用,服务器推送静态资源。
- 使用服务端渲染。
- 图片压缩。
- 使用 http 缓存,比如服务端的响应中添加
Cache-Control / Expires。
二 : CSS篇
1. BFC是什么? 哪些属性可以构成一个BFC呢?
BFC指块级格式化上下文,创建了一个独立的渲染区域,隔绝内外环境,计算BFC高度时,浮动元素也要参与计算
常见构成BFC属性:
overflow: hidden;
display: flex;
display: inline-flex;
display: inline-block;
position: absolute;
position: fixed;
应用场景:
- 解决浮动元素无法撑起父盒子高度问题
- 子元素设置margin-top与父元素边距塌陷问题
2. postion属性大概讲一下, static是什么表现? static在文档流里吗?
postion:static inherit relative absolute fixed
static 和relative都在文档流里,区别是static设置top等不生效, relative会生效
待看常见问题
- vue3
- websocket连接原理
- Set和Map
- 前端稳定性监控
- 给一个字符串, 找到第一个不重复的字符