知识回顾

185 阅读7分钟

基础知识

  1. let const
  • var有变量提升,有初始化提升,值可变
  • let有变量提升,没有初始化提升,值可变
  • const有变量提升,没有初始化提升,值不可变,但如果是定义对象,则属性可变
  • let和const有暂时性死区 + 块级作用域
  1. 普通函数和箭头函数的区别:
  • 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' 

  1. 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毫秒后我成功啦!!!
}

  1. 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. 数组 (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());
  1. for of 和 for in
  • for in :遍历方法,可遍历对象和数组
  • for of :遍历方法,只能遍历数组,不能遍历对象
  1. 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]
  1. 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 ?? '林' // 林三心
  1. String.trimStart && String.trimEnd字符串去首或者尾空格
  2. js有多少种数据类型--8 undefined null number bool string object symbol bigInt

Diff算法

  1. 什么是虚拟Dom
  • 它是一个用来表示真实DOM的对象。包含tagName标签名、props属性、children等属性
  1. 虚拟dom比真实dom快吗?
  • 这种说法不严谨。虚拟dom操作真实dom,在性能上优于真实dom

  • Diff算法是一种对比算法。对比新旧虚拟dom,找到变更节点,将这些变更节点更新为对应的真实节点,而不用更新其他数据没发生改变的节点,实现精准地更新真实DOM,进而提高效率

    使用虚拟DOM算法的损耗计算: 总损耗 = 虚拟DOM增删改+(与Diff算法效率有关)真实DOM差异增删改+(较少的节点)排版与重绘

    直接操作真实DOM的损耗计算: 总损耗 = 真实DOM完全增删改+(可能较多的节点)排版与重绘

  1. 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

前端异常监控

  1. 异常监控
  • js异常:try catchwindow.onerror
  • 资源加载异常:addEventListener('error', callback, true)
  • promise异常:addeventListener('unhandledrejection',callback)
  • vue异常:errorHandle捕获错误后,不会被抛到控制台;warnHandle,只开发环境;renderError,默认渲染函数遇到错误时,代替进行渲染输出;errorCaptured(err, vm, info),return false阻止错误传播
  • react异常:getDerivedStateFromError,在渲染阶段被调用,适合显示UI错误组件;componentDidCatch在commit阶段被调用,适合进行错误上报
  1. 前端容灾 定义:服务端挂了,前端页面信息可以完整展示
  • 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区别 对称加密和不对称加密