基础知识
- let const
- var有变量提升,有初始化提升,值可变
- let有变量提升,没有初始化提升,值可变
- const有变量提升,没有初始化提升,值不可变,但如果是定义对象,则属性可变
- let和const有暂时性死区 + 块级作用域
- 普通函数和箭头函数的区别:
- 1、不可作为构造函数,不能使用new。构造函数的new都做了些什么?简单来说,分为四步: ① JS内部首先会先生成一个对象; ② 再把函数中的this指向该对象; ③ 然后执行构造函数中的语句; ④ 最终返回该对象实例。因为箭头函数没有自己的
this,所以它不能作为构造函数 - 2、没有自己的this。它会捕获自己在定义时所处的外层执行环境的
this,并继承这个this值。在定义的时候就决定了this,这个this不会变,也不能通过apply\call\bind改变 - 3、没有arguments对象
- 4、没有原型对象
- 5、不能作为generator函数,不能适用yeild对象
var id = 'GLOBAL';
var obj = {
id: 'OBJ',
a: function(){ // 此处this看代码执行时的this
console.log(this.id);
},
b: () => {// 定义时的this为window
console.log(this.id);
}
};
obj.a(); // 'OBJ'
obj.b(); // 'GLOBAL'
- promise
all等所有成功是成功,一个失败则失败 race返回最快出结果的,无论成功还是失败 any返回最快成功的,所有失败走失败 allSettled 无论成功还是失败,放在一起返回 (1) Promise.all
- 接收一个Promise数组,数组中如有非Promise项,则此项当做成功
- 如果所有Promise都成功,等所有promise执行完,走resolve,返回成功结果数组
- 如果有一个Promise失败,则走reject,返回这个失败结果
// 如果全都为成功
function fn(time) {
return new Promise((resolve, reject) => {
console.log(88)
setTimeout(() => {
resolve(`${time}毫秒后我成功啦!!!`)
}, time)
})
}
Promise.all([fn(2000), fn(3000), fn(1000)]).then(res => {
console.log(res) // 3秒后输出 [ '2000毫秒后我成功啦!!!', '3000毫秒后我成功啦!!!', '1000毫秒后我成功啦!!!' ]
}, err => {
console.log(err)
})
// 如果有一个失败
function fn(time, isResolve) {
return new Promise((resolve, reject) => {
setTimeout(() => {
isResolve ? resolve(`${time}毫秒后我成功啦!!!`) : reject(`${time}毫秒后我失败啦!!!`)
}, time)
})
}
Promise.all([fn(2000, true), fn(3000), fn(1000, true)]).then(res => {
console.log(res)
}, err => {
console.log(err) // 3秒后输出 '3000毫秒后我失败啦!!!'
})
(2)Promise.race
- 接收一个Promise数组,数组中如有非Promise项,则此项当做成功
- 哪个Promise最快得到结果,就返回那个结果,无论成功失败
function fn(time, isResolve) {
return new Promise((resolve, reject) => {
setTimeout(() => {
isResolve ? resolve(`${time}毫秒后我成功啦!!!`) : reject(`${time}毫秒后我失败啦!!!`)
}, time)
})
}
Promise.race([fn(2000, true), fn(3000), fn(1000)]).then(res => {
console.log(res)
}, err => {
console.log(err) // 1秒后输出
})
(3)Promise.allSettled
- 接收一个Promise数组,数组中如有非Promise项,则此项当做成功
- 把每一个Promise的结果,集合成数组,返回
function fn(time, isResolve) {
return new Promise((resolve, reject) => {
setTimeout(() => {
isResolve ? resolve(`${time}毫秒后我成功啦!!!`) : reject(`${time}毫秒后我失败啦!!!`)
}, time)
})
}
Promise.allSettled([fn(2000, true), fn(3000), fn(1000)]).then(res => {
console.log(res)
// 3秒后输出
[
{ status: 'fulfilled', value: '2000毫秒后我成功啦!!!' },
{ status: 'rejected', reason: '3000毫秒后我失败啦!!!' },
{ status: 'rejected', reason: '1000毫秒后我失败啦!!!' }
]
})
(4)Promise.any
E12新增的Promise的方法
- 接收一个Promise数组,数组中如有非Promise项,则此项当做成功
- 如果有一个Promise成功,则返回这个成功结果
- 如果所有Promise都失败,则报错
// 当有成功的时候,返回最快那个成功
function fn(time, isResolve) {
return new Promise((resolve, reject) => {
setTimeout(() => {
isResolve ? resolve(`${time}毫秒后我成功啦!!!`) : reject(`${time}毫秒后我失败啦!!!`)
}, time)
})
}
Promise.any([fn(2000, true), fn(3000), fn(1000, true)]).then(res => {
console.log(res) // 1秒后 输出 1000毫秒后我成功啦
}, err => {
console.log(err)
})
// 当全都失败时
function fn(time, isResolve) {
return new Promise((resolve, reject) => {
setTimeout(() => {
isResolve ? resolve(`${time}毫秒后我成功啦!!!`) : reject(`${time}毫秒后我失败啦!!!`)
}, time)
})
}
Promise.any([fn(2000), fn(3000), fn(1000)]).then(res => {
console.log(res)
}, err => {
console.log(err) // 3秒后 报错 all Error
})
(5)for await of排队执行promise
function fn (time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`${time}毫秒后我成功啦!!!`)
}, time)
})
}
async function asyncFn () {
const arr = [fn(3000), fn(1000), fn(1000), fn(2000), fn(500)]
for await (let x of arr) {
console.log(x)
}
}
asyncFn()
3000毫秒后我成功啦!!!
1000毫秒后我成功啦!!!
1000毫秒后我成功啦!!!
2000毫秒后我成功啦!!!
500毫秒后我成功啦!!!
// 类似于
async function asyncFn () {
const data1 = await fn(3000)
console.log(data1) // 3秒后 3000毫秒后我成功啦!!!
const data2 = await fn(1000)
console.log(data2) // 再过1秒 1000毫秒后我成功啦!!!
const data3 = await fn(2000)
console.log(data3) // 再过2秒 2000毫秒后我成功啦!!!
}
- class
使用
static定义的属性和方法只能class自己用,实例用不了;
class Person {
constructor(name) {
this.name = name
}
static age = 22
static fn() {
console.log('哈哈')
}
}
console.log(Person.age) // 22
Person.fn() // 哈哈
const sunshine_lin = new Person('林')
console.log(sunshine_lin.age) // undefined
sunshine_lin.fn() // fn is not a function
- 数组 (1)数组结构
const arr = [1, 2, 3]
// 默认赋值
const [a, b, c, d = 5] = arr
console.log(a, b, c, d) // 1 2 3 5
// 乱序解构 通过索引进行结构
const { 1: a, 0: b, 2: c } = arr
console.log(a, b, c) // 2 1 3
(2)includes和indexOf
const arr = [1, 2, NaN]
console.log(arr.indexOf(NaN)) // -1 indexOf找不到NaN
console.log(arr.includes(NaN)) // true includes能找到NaN
(3)Array.flat 展平数组 参数为降维的次数;如果传的是一个无限大的数字,那么就实现了多维数组(无论几维)降为一维数组
const arr = [1, 2, 3, [4, 5, 6, [7, 8, 9]]]
console.log(arr.flat(2)) // [1, 2, 3, 4, 5,6, 7, 8, 9]
const arr = [1, 2, 3, [4, 5, 6, [7, 8, 9, [10, 11, 12]]]]
console.log(arr.flat(Infinity)) // [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ]
(4)Array.flatMap
let arr = ["科比 詹姆斯 安东尼", "利拉德 罗斯 麦科勒姆"];
console.log(arr.flatMap(x => x.split(" "))); // [ '科比', '詹姆斯', '安东尼', '利拉德', '罗斯', '麦科勒姆' ]
// 类似于
console.log(arr.map(x => x.split(" ")).flat());
- for of 和 for in
- for in :遍历方法,可遍历对象和数组
- for of :遍历方法,只能遍历数组,不能遍历对象
- Set数组去重 对引用类型,同一指针时可进行去重。NaN不能实现去重,因为NaN !== NaN
const arr = [1, 2, 3, 4, 4, 5, 5, 66, 9, 1]
// Set可利用扩展运算符转为数组哦
const quchongArr = [...new Set(arr)]
console.log(quchongArr) // [1, 2, 3, 4, 5, 66, 9]
- Object.keys(属性集合)、Object.values(值集合)、Object.entries(键值对集合)、Object.fromEntries(将键值对集合转为对象)
const obj = {
name: '林',
age: 2,
gender: '男'
}
const values = Object.values(obj)
console.log(values) // [ '林', 2, '男' ]
const entries = Object.entries(obj)
console.log(entries)
// [ [ 'name', '林' ], [ 'age', 2 ], [ 'gender', '男' ] ]
console.log(Object.fromEntries(entries)) // { name: '林', age: 2, gender: '男' }
9.?.可选链和??空位合并运算符
// 数组可选链
const arr = null
const item = arr?.[1]
// 函数可选链
const fn = null
const res = fn?.()
使用||运算符,只要左边是假值,就会返回右边的数据;而??和||最大的区别是,在??这,只有undefined和null才算假值
const a = 0 ?? '林' // 0
const b = '' ?? '林' // ''
const c = false ?? '林' // false
const d = undefined ?? '林' // 林三心
const e = null ?? '林' // 林三心
- String.trimStart && String.trimEnd字符串去首或者尾空格
- js有多少种数据类型--8 undefined null number bool string object symbol bigInt
Diff算法
- 什么是虚拟Dom
- 它是一个用来表示真实DOM的对象。包含tagName标签名、props属性、children等属性
- 虚拟dom比真实dom快吗?
-
这种说法不严谨。虚拟dom操作真实dom,在性能上优于真实dom
-
Diff算法是一种对比算法。对比新旧虚拟dom,找到
变更节点,将这些变更节点更新为对应的真实节点,而不用更新其他数据没发生改变的节点,实现精准地更新真实DOM,进而提高效率。使用虚拟DOM算法的损耗计算: 总损耗 = 虚拟DOM增删改+(与Diff算法效率有关)真实DOM差异增删改+(较少的节点)排版与重绘直接操作真实DOM的损耗计算: 总损耗 = 真实DOM完全增删改+(可能较多的节点)排版与重绘
- diff算法原理
(1)同层对比,广度优先算法
(2)diff对比的过程:当数据改变时,会触发
setter,并且通过Dep.notify去通知所有订阅者Watcher,订阅者们就会调用patch方法,给真实DOM打补丁,更新相应的视图。
1.在patch方法内部,通过sameVnode方法根据节点key、tagName、isComment等比较新旧节点是否相同
如果`不同`,直接用节点替换旧的。如果`相同`,则进行深层比对patchVnode。
2.在patchVnode进行深度比对时,如果都是`文本节点`,用新的文本替换旧的文本;如果`旧的有子节点,新的没有`,则直接删除子节点;如果`旧的没有新的有`,根据虚拟dom创建真实dom后插入到当前元素中;如果两者`都有子节点`,进行`updateChildren`。
3.在updateChildren子节点比较时,有五种比较情况:
oldS 和 newS
oldS 和 newE
oldE 和 newS
oldE 和 newE
如果以上逻辑都匹配不到,再把所有旧子节点的 `key` 做一个映射到旧节点下标的 `key -> index` 表,然后用新 `vnode` 的 `key` 去找出在旧节点中可以复用的位置。
WeChat5a4b24e6624061c6c63f217f211fdcf8.png
前端异常监控
- 异常监控
- js异常:
try catch和window.onerror - 资源加载异常:addEventListener('
error', callback, true) - promise异常:addeventListener('
unhandledrejection',callback) - vue异常:
errorHandle捕获错误后,不会被抛到控制台;warnHandle,只开发环境;renderError,默认渲染函数遇到错误时,代替进行渲染输出;errorCaptured(err, vm, info),return false阻止错误传播 - react异常:
getDerivedStateFromError,在渲染阶段被调用,适合显示UI错误组件;componentDidCatch在commit阶段被调用,适合进行错误上报
- 前端容灾 定义:服务端挂了,前端页面信息可以完整展示
- LocalStorage:接口路径作为 key,返回的数据作为 value;之后请求时,如果请求失败,就读取 LocalStorage,把上次的数据拿出来展示,并上报错误信息,以获得缓冲时间
- cdn:将数据备份在cdn
- service worker:实现离线缓存,包括接口数据+html静态资源
http
- http和https
- get和post区别 回退、是否被缓存、传参方式大小等限制、安全性、编码方式、产生数据包方式
- 状态码 200 301 302 304 400 401 403 404 500 503
- 短链接和长链接定义,长连接优缺点
- 什么是管道化
- 如何解决http队头阻塞:并发链接 域名分片
- http代理:普通代理 隧道代理 代理有点
- 正向代理和反向代理
- https和http区别 对称加密和不对称加密