记录一下自己 “被打” 的过程😂

面试记录

2021.2.28 从前公司离职,开始了复习知识和面试的日子。记录一下自己面试的经历以及题目,常看常新,补全知识。

富途一面

  1. Vue 组件间通信

    • 如果是父子组件,那么可以直接通过 props 进行通信,也可以使用 provide/inject

    • Event Bus 通过新建一个 Vue 实例使两个组件能够进行通信

    • Vuex

      拓展:Vuex 和 Vue Router 在 install 的时候发生了什么?(其实是 Vue 插件机制)
      其实 Vue 通过 use() 安装插件,就是通过 mixin 混入了方法。Vuex 和 Router 都是通过在 beforeCreate 这个生命周期里把 $routerstore 挂载到了 Vue 的实例上。

  2. Vue2 和 Vue3 的生命周期和区别

    Vue 2.x Vue 3.x Details
    beforeCreate beforeCreate 在实例初始化之后,数据观测和 event/watcher 事件配置之前被调用
    created created 在实例创建完成之后立即被调用。$el property 不可用
    beforeMount beforeMount 在挂载开始之前被调用:相关的 render 函数首次被调用
    mounted mounted 实例被挂载后调用,此时可以使用 nextTick 和进行 DOM 操作
    beforeUpdate beforeUpdate 数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新前访问现有 DOM,手动移除事件监听器
    updated updated 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
    activated activated keep-alive 缓存的组件激活时调用
    deactivated deactivated keep-alive 缓存的组件停用时停用
    beforeDestroy beforeUmount 在销毁(卸载)组件实例之前调用。此时实例还是完全正常的
    destroyed unmounted 实例销毁(卸载)后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。
    errorCaptured errorCaptured 当捕获一个来自子孙组件的错误时被调用。
    - renderTracked 跟踪虚拟 DOM 重新渲染时调用。钩子接收 debugger event 作为参数。此事件告诉你哪个操作跟踪了组件以及该操作的目标对象和键。
    - renderTriggered 当虚拟 DOM 重新渲染为 triggered.Similarly 为 renderTracked,接收 debugger event 作为参数。此事件告诉你是什么操作触发了重新渲染,以及该操作的目标对象和键。

    拓展1: Vue 父子组件的渲染顺序

    父beforeCreate => 父created => 父beforeMount => 子beforeCreate => 子created => 子beforeMount => 子mounted => 父mounted

    拓展2: 子组件更新过程

    父beforeUpdate => 子beforeUpdate => 子updated => 父updated

    拓展3: 销毁过程

    父beforeDestroy => 子beforeDestroy => 子destroyed => 父destroyed

  3. 浏览器渲染

    浏览器渲染进程主要有 GUI 渲染线程,JS 引擎线程,事件触发线程,定时触发器线程,异步 HTTP 请求线程。

    其中 GUI 渲染线程和 JS 引擎线程是互斥的。GUI 渲染线程负责渲染浏览器界面,解析 HTML 和 CSS,在负责处理 JavaScript 脚本程序的 JS 引擎线程执行时,GUI 渲染线程会暂时挂起,直到 JS 引擎空闲。

    延伸1:

    • 浏览器工作流程: 构建 DOM => 构建 CSSOM => 构建渲染树 => 布局 => 绘制。
    • CSSOM 会阻塞渲染,只有当 CSSOM 构建完毕之后才会进入下一阶段的构建树。
    • 通常情况下 DOM 和 CSSOM 是并行构建的,但是当浏览器遇到 script 标签时,DOM 构建将暂停,直到脚本执行完成。但由于 JavaScript 可以修改 CSSOM,所以需要等 CSSOM 构建完毕后再执行 JS。
    • 所以如果想要首屏渲染时间缩短,就需要把 script 标签放在 body 标签底部,或者直接不要在首屏加载 JS 文件。
  4. 浏览器缓存

    基本的网络请求就是三个步骤:请求,处理,响应。后端主要就是”处理”这个步骤,所以其实前端缓存就在“请求”和“响应”中进行。

    当浏览器要请求资源时:

    1. 调用 Service Worker 的 fetch 事件
    2. 查看 memory cache
    3. 查看 disk cache:
      • 如果有强制缓存且未失效,则使用强制缓存,不请求服务器。状态码全部是200
      • 如果有强制缓存但已失效,使用对比缓存,比较后确定是304还是200
    4. 发送网络请求,等待响应
    5. 把响应内容存入 disk cache(如果 HTTP 头信息配置可以缓存)
    6. 把响应内容的引用存入 memory cache(无视 HTTP 头信息的配置)
    7. 把响应内容存入 Service Worker 的 Cache Storage
  1. this 指向问题

    可以从 Reference 类型来判断 this 的指向。

    首先判断出 MemberExpression 赋值给 ref,然后判断 ref 是不是一个 Reference 类型。

    Reference = {
    // 属性所在的对象或 EnvironmentRecord,它的值只可能是 undefined, Object, Boolean, String, Number, environment record
    base,
    // 属性名
    name,
    // 是否为 strict 模式
    strict
    }

    // 举个栗子
    var value = 1;
    var foo = {
    value: 2,
    bar: function () {
    return this.value;
    }
    }

    console.log(foo.bar()); // 2
    /*
    MemberExpression 为 foo.bar

    Renference = {
    base: foo,
    name: 'bar',
    strict: false
    }

    所以 this = foo,该表达式输出 2.
    */
    // 有操作符,逻辑运算符或逗号运算符,ref 的就不是 Reference类型,this 就是 undefined,最终的 this 会隐式转换为全局对象,所以该表达式输出 1.
    console.log((foo.bar = foo.bar)()); // 1
    console.log((foo.bar || foo.bar)()); // 1
    console.log((foo.bar, foo.bar)()); // 1

    拓展1: 将 foo.bar 改成 箭头函数会怎样?

    拓展2: 将 var 换成 let 或 const 又会怎样?

  2. 箭头函数和普通函数的区别

箭头函数不会创建自己的 this,它只会从自己的作用域链的上一层继承 this。

  • 箭头函数没有自己的 this,它会捕获定义时自己所处的外层执行环境的 this,并继承这个 this。
  • 箭头函数继承来的 this 指向永远不变
  • call,apply,bind 无法改变箭头函数中 this 的指向
  • 箭头函数不能作为构造函数使用
  • 箭头函数没有自己的 arguments 对象
  • 箭头函数没有原型 prototype
  • 箭头函数不能用作 Generator 函数,不能使用 yeild 关键字
  1. 斐波那契函数,以及缓存优化
// 动态规划
function fib1(n: number): number {
let a = 0, b = 1, sum;

for (let i = 0; i < n; i++) {
sum = (a + b) % 1000000007;
a = b;
b = sum;
}
return a;

// 花里胡哨
let a = 0, b= 1;

while(n--) {
[a, b] = [b, (a + b) % 1000000007];
}

return a
}
// 缓存 闭包
function fib2(n: number): number {
const map = new Map<number, number>();

function sub(n: number): number {
if (n < 2) {
return n
}

if (map.get(n)) {
return map.get(n)
}

let sum = sub(n - 1) + sub(n - 2);
map.set(n, sum);

return sum % 1000000007;
}

return sub(n)
}
  1. Vue 数据量大的时候,如何优化

    • 按需加载局部数据,虚拟列表,无限下拉刷新(可视区域)
    • 大量纯展示的数据,可以使用 Object.freeze 冻结
    • 还可以进行分页
  2. 图片网站 优化

    首先我们分析一下图片加载存在的问题和原因:

    1. 启动页面时加载过多图片

      首屏图片优先加载,等首屏图片加载完成再去加载非首屏图片

      可以进行域名切分,来提升并发的请求数量,或者使用 HTTP/2 协议

    2. 部分图片体积过大

      单位像素优化 - 改变图片格式,使用 webp 格式

      图片像素总数优化 - 裁剪大图片,减少图片体积,减少网络开销,加快下载速率

  3. 防抖节流

// 防抖 触发高频事件 n 秒内只会执行一次函数,n 秒内事件再次触发,则重新计算时间 如果你在期间一直触发,则不会触发
function debounce(fn, ms) {
let timeout = null;

return function() {
clearTimeout(timeout);
timeout = setTimeout(() => {
fn.apply(this, arguments)
}, ms)
}
}
// 节流 触发高频事件,但在 n 秒内只会执行一次,所以节流会稀释函数的执行效率 一定会触发
function throttle(fn, ms) {
let timeout;

return function() {
if (!timeout) {
timeout = setTimeout(() => {
timeout = null
fn.apply(this, arguments)
}, ms)
}
}
}
  1. [事件循环机制](详解JavaScript中的Event Loop(事件循环)机制 - 知乎 (zhihu.com)

  2. var,let,const 的区别

let/const 是使用块级作用域;var 使用的是函数作用域,且存在变量提升。

在函数外部使用 var 进行声明都会自动成为 window 对象上的一个属性。

  1. 暂时性死区(TDZ)

只要块级作用域内存在 let 命令,它所声明的变量就”绑定”这个区域,不再受外部的影响。

ES6 中规定,如果区块中存在 letconst 命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明前使用这些变量,就会报错。

  1. 闭包

能够访问自由变量的函数被称为闭包。

自由变量:在函数中使用的既不是函数参数也不是局部变量的变量。

  1. HTTPS 和 HTTP 的区别
  • HTTPS 是 HTTP 协议的安全版本,HTTP 协议的数据传输是明文的,是不安全的,HTTPS 使用了 SSL/TLS 协议进行加密处理。
  • HTTP 和 HTTPS 使用的链接方式不同,默认端口也不一样,前者是80,后者是443.

引申: 对称加密和非对称加密 使用不同密钥进行加密和解密就是非对称加密,相对的加密解密使用同一密钥的就是对称加密。

  1. HTTP2.0 和 HTTP1.1 的区别?多个请求在这两个协议之间的区别?如果请求的是不同主域,那么区别在哪?

HTTP2.0

  • 多路复用
  • header 压缩
  • 服务端推送

HTTP1.1

  • keepalive 可以让 HTTP 重用 TCP 链接,就是所谓的长链接。

HTTP2.0 会采用多路复用,一次 TCP 连接即可,采用 HTTP 的话,如果没有设置 keepalive 那么一次请求就要建立一个 TCP 连接。

探迹一面

  1. 事件机制
  2. 事件循环
  3. Vue 响应式原理
  4. react 优化
  5. setState
  6. 数组去重
  7. 深浅拷贝
  8. get post 区别
  9. 前端性能优化
  10. call的实现
  11. node require
  12. node 怎么读文件

评论