每天复习10道面试题

192 阅读15分钟

每日10题

每日更新

第一天:2021-12-01

1. HTTP2.0和HTTP1.X相比的新特性

  • 新的二进制格式(Binary Format)取代了HTTP1.x的文本格式,二进制格式解析更高效。HTTP1.x的解析是基于文本。基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合。基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便且健壮
  • 多路复用 所有的相同域名请求都通过同一个TCP连接并发完成。 即连接共享,即每一个请求都是用作连接共享机制的。一个request对应一个id,这样一个连接上可以有多个request,每个连接的request可以随机的混杂在一起,接收方可以根据request的 id将request再归属到各自不同的服务端请求里面。
  • header压缩 HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。
  • 服务端推送(server push) 能把客户端所需要的资源伴随着index.html一起发送到客户端,省去了客户端重复请求的步骤。正因为没有发起请求,建立连接等操作,所以静态资源通过服务端推送的方式可以极大地提升速度。

HTTP1.1和HTTP1.0的特性 最主要的变化就是默认开启了keep-Alive,长连接,不需要在一个请求就发起一次连接,多个请求可以共用一个连接

2. http2.0 做了哪些改进 3.0 呢

HTTP3.0 相对于HTTP2.0 脱胎换骨的改变

  • 弃用TCP改用基于 UDP 协议的 QUIC 协议实现
  • 连接迁移 TCP连接通过源IP、源端口、目的IP、目的端口进行连接,当网络发生改变后连接再次建立时延较长。HTTP/3 通过Connection ID 对连接进行保持,只要ID不变,连接仍可维持。
  • 无队头阻塞 TCP作为面向连接的协议,对每次请求序等到ACK才可继续连接,一旦中间连接丢失将会产生队头阻塞。**HTTP/3基于UDP的传输,不保证连接可靠性,也就没有队头阻塞的后果。**同样传输单元与加密单元为Packet,在TLS下也可避免对头阻塞的问题。
  • 拥塞控制 TCP对于拥塞控制在于传输层,QUIC可在应用层操作改变拥塞控制方法。
  • 前向安全和前向纠错 每发送一组数据之后,就对这组数据进行异或运算(效率高),并将结果也发送出去,那么接收方就有两份数据版本,可以对初始数据进行纠错和校验。以此保证了可靠性。(带宽换时间)

3. http 状态码 204 301 302 304 400 401 403 404 含义

  • 204:无内容,服务器成功处理了请求,但没有返回任何内容
  • 301:永久重定向,请求的网页已经永久移动到新位置,服务器返回此响应时,会自动将请求者转到新位置。
  • 302:临时重定向,服务器目前从不通位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求
  • 304:未修改,命中协商缓存,请求的网页未修改过,服务器返回此响应时,不会返回网页内容。
  • 400:错误请求,服务器不理解请求语法
  • 401:未授权
  • 403:禁止
  • 404:未找到

4. https 加密过程是怎样的

HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL

  • 对称加密 对称就是指两边一样,发送方和接收方都用的同一个密钥,加密解密都是同一个密钥,从始至终只需要保存一个密钥就行
  • 非对称加密 发送方和接收方使用一对密钥,即公钥和私钥。一般私钥是保密不能被泄露的,公钥可以对外传播。我们可以用公钥加密,私钥解密;也可用私钥加密,公钥解密。

5. 浏览器缓存策略是怎样的(强缓存 协商缓存)具体是什么过程?

  1. 强缓存 强缓存是当我们访问URL的时候,不会向服务器发送请求,直接从缓存中读取资源,返回200的状态码。
    第一次进入页面请求服务器时,浏览器会根据响应头的Expires、Cache-Control字段来判断是否对资源进行缓存。
    第二次请求时浏览器判断请求参数,如果符合强缓存条件就直接返回状态码200,从本地缓存拿数据。否则把响应参数存在request header请求头中,看是否符合协商缓存,符合则返回状态码304,不符合则服务器会返回全新资源。

  2. 协商缓存 协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程。
    协商缓存生效,返回304。否则返回200和请求结果。

6. webpack中fullhash、chunkhash和contenthash的区别

  • fullhash 即全量的hash,是整个项目级别的。只要项目中有任何一个的文件内容发生变动,打包后所有文件的hash值都会发生改变。
  • chunkhash 根据不同的入口文件(entry)进行依赖文件解析,构建对应的chunk,生成对应的哈希值。当某个文件内容发生变动时,再次执行打包,只有同一个chunk的文件hash值会变
  • contenthash 是只有当文件自己的内容发生改变时,其打包的 hash 值才会发生变动。

7. 写过 webpack 的 loader 和 plugin 么;

Loader 用于对模块源码的转换,loader 描述了 webpack 如何处理非 javascript 模块,并且在 build 中引入这些依赖。loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或者将内联图像转换为 data URL。比如说:CSS-Loader,Style-Loader 等。
loader 本质上就是导出一个 JavaScript 函数,

Plugin 目的在于解决 loader 无法实现的其他事,它直接作用于 webpack,扩展了它的功能。在 webpack 运行的生命周期中会广播出许多事件,plugin 可以监听这些事件,在合适的时机通过 webpack 提供的 API 改变输出结果。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。

Plugin开发,一个 plugin 由以下部分组成:

  • 导出一个 JavaScript 具名函数或 JavaScript 类。
  • 在插件函数的 prototype 上定义一个 apply 方法。
  • 指定一个绑定到 webpack 自身的事件钩子。
  • 处理 webpack 内部实例的特定数据。
  • 功能完成后调用 webpack 提供的回调。

8. webpack 处理 image 是用哪个 loader,限制成 image 大小的是.

url-loader 使用参数limit限制大小

9. webpack 的摇树对 commonjs 和 es6 module 都生效么,原理是;

Tree Shaking只支持ES module的使用,不支持require这种动态引入模块的方式。因为ES6 module是静态引入,commonjs的require引入是动态引入,在实际运行代码之前无法确定需要哪些模块。 原理:

  1. 先标记出模块导入值中哪些没有被用过,标记过程大致可划分为三个步骤:
  • Make 阶段,收集模块导出变量并记录到模块依赖关系图 ModuleGraph 变量中
  • Seal 阶段,遍历 模块依赖关系图 标记模块导出变量有没有被使用
  • 生成产物时,若变量没有被其它模块使用则删除对应的导出语句
  1. 删掉这些没被用到的导入语句。

10. 实现一下「模版字符串」功能

let obj = { type1: "警察", type2: "卧底", action: '逮捕'} ;
function tem(str, data) {
  const reg = /\$\{(\w*)\}/;
  let res = reg.exec(str);
  // res[0]是完整的结果${type1}  res[1]是括号内的结果 type1
  if(res) {
    str = str.replace(res[0], data[res[1]]);
    return tem(str,data);
  } else {
    return str
  }
}
// 测试用例
const m = tem("对不起,我是${type1},你被${action}了", obj);
const n = tem("我是${type2},但是我想做${type2}", obj);
console.log(m) // 对不起,我是警察,你被逮捕了
console.log(n) // 我是卧底,但是我想做卧底

第二天:2021-12-02

1. 实现一下 Promise.all 和 Promise.race

function all(promiseArr) {
  const n = promiseArr.length
  const result = new Array(n);
  let count = 0;
  return new Promise((resolve, reject) => {
    for(let i = 0; i < n; i++) {
      Promise.resolve(promiseArr[i]).then(res => {
        result[i] = res;
        count++;
        if(count === n) {
          resolve(result)
        }
      }).catch(err => {
        reject(err)
      })
    }
  })
}

function race(promiseArr) {
  return new Promise((resolve, reject) => {
    for(let i = 0; i < promiseArr.length; i++) {
      Promise.resolve(promiseArr[i]).then(res => {
        resolve(result)
      }).catch(err => {
        reject(err)
      })
    }
  })
}

2. 怎么实现响应式布局的;

  1. 使用媒体查询 @media screen
  2. 使用百分比布局或flex布局
  3. 使用rem单位
  4. 使用视口单位vw和vh

3. babel 是什么,原理了解吗

Babel 是一个javascript 编译器,它把最新版的javascript编译成当下可以执行的版本。
原理:有三个步骤解析、转换、生成

  • 解析 将代码解析成AST(抽象语法树),babel 通过Babylon 实现。 解析过程有两个阶段:词法分析和语法分析,词法分析把字符串形式的代码转换为令牌流,而语法分析阶段则会把一个令牌流转换成 AST 的形式,同时这个阶段会把令牌中的信息转换成 AST 的表述结构。
  • 转换 阶段接收AST,并进行深度优先遍历,对节点进行添加更新和移除。遍历通过babel-traverse 实现
  • 生成 将经过转换的AST通过babel-generator再转换成js代码

4. css flex 的各个属性值;

  • flex-grow 定义项目的放大比例,默认0
  • flex-shrink 定义项目的缩小比例,默认1,即如果空间不足,该项目将缩小
  • flex-basis 分配多余空间前,项目占据的主轴空间

5. ES6 Proxy 如何使用以及使用场景,说说 Reflect;

Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义。
用法:通过new 实例化,并传入两个参数,第一个参数是target需要包装的目标对象,第二个是handler对象,里面定义各种代理行为。然后使用实例化后的对象进行特定操作时触发handler里面定义好的方法

const target = {}
const proxy = new Proxy(target, {
  get: (obj, prop) => {
    console.log('设置 get 操作')
    return obj[prop];
  },
  set: (obj, prop, value) => {
    console.log('set 操作')
    obj[prop] = value;
  }
});
proxy.a = 2  // set 操作
proxy.a  // 设置 get 操作

Reflect对象提供拦截 JavaScript 操作的方法,一共有 13 个静态方法。这些方法与proxy handlers的方法相同,比如

  • Reflect.get(target, name, receiver) 取出target[name]的数据,第三个参数receiver为 getter 调用时的 this 值
  • Reflect.set(target, name, value, receiver) 设置target[name] = value
  • Reflect.has(target, name)in操作符一致 name in target
  • Reflect.deleteProperty(target, name) 删除属性 delete target[name]
  • Reflect.apply(target, thisArg, args) 通过指定的参数列表发起对目标(target)函数的调用。 等等
    为什么需要 Reflect:正确的上下文引用、和 Object 的方法略有区别,丰富使用场景

6. 层合成

DOM树中每个节点都对应一个渲染对象,当他们的渲染对象处于相同z轴时就形成了渲染层。渲染层保证页面元素以正确的顺序堆叠,这时候就出现层合成。

7. 浏览器输入URL到完成渲染的整个过程

  1. 输入URL回车
  2. 浏览器查找URL是否有缓存
  3. DNS解析,将URL解析成IP
  4. 根据IP建立TCP连接(三次握手)
  5. 发送HTTP请求
  6. 服务器处理请求并响应
  7. 浏览器接收响应进行解析并渲染页面
  8. 关闭TCP连接(四次挥手)

8. 说一说事件代理

不给每个节点单独设置事件监听,而是设置在其父节点上,然后利用冒泡原理设置每个节点。
优点:

  1. 减少内存消耗和DOM操作
  2. 动态绑定事件,新增加的元素也可以触发同样的事件

9. 实现一个方块的拖拽;

<style>#box { width: 100px;height: 100px;background: red;position: absolute;left: 100px;top: 100px;}</style>
<body>
<div id="box"></div>
<script type="text/javascript">
const box = document.getElementById('box');
function drag(el) {
  el.style['cursor'] = 'grab';
  el.onmousedown = function(e) {
    el.style['cursor'] = 'grabbing';
    const l = e.clientX - el.offsetLeft; // 元素左边到鼠标点击的距离
    const t = e.clientY - el.offsetTop; // 元素上边到鼠标点击的距离
    document.onmousemove = function(event) {
      const left = event.clientX - l;
      const top = event.clientY - t;
      el.style.left = left + 'px';
      el.style.top = top + 'px';
    }
    document.onmouseup = function() {
      document.onmousemove = null;
      document.onmouseup = null;
      el.style['cursor'] = 'grab';
    }
  }
}
drag(box)
</script>

10. ES6 symbol 如何使用以及使用场景;

Symbol 是JS基本类型之一,Symbol 代表了一个独一无二的值

const flag = Symbol();

使用场景:使用Symbol来作为对象属性名、使用Symbol来替代常量、使用Symbol定义类的私有属性/方法 等等

第三天:2021-12-03

1. 常用设计模式

  • 单例模式:保证一个类只有一个实例,例子:vuex 和 vue-router 的插件注册方法 install 判断如果系统存在实例就直接返回掉
  • 工厂模式:传入参数即可创建实例,例子:虚拟DOM根据参数的不同返回基础标签的Vnode和组件的Vnode
  • 发布/订阅模式:例子:vue 事件机制
  • 观察者模式:例子:响应式数据原理
  • 装饰器模式:可以为原对象绑定新的行为。
 // o为已有对象
var M20 = function(o){    // 这里定义一个装饰类
    var str = '20多岁的时候,';
    // o是传入的对象,调用传入对象的方法,加以装饰
    this.eat = function(){
        return str + o.eat()+",肥得很!";
    };
    this.drink = function(){
        return str + o.drink()+",就是个水桶!";
    };
    this.coding = function(){
        return str + o.coding()+",代码又写得撇!";
    };
}
alert(new M20(david).eat());    // 20多岁的时候,大卫是个大胖子,一天只晓得吃,肥得很!
alert(new M20(david).drink());    // 20多岁的时候,大卫除了吃就是喝,就是个水桶!
alert(new M20(david).coding());    // 20多岁的时候,写代码吧,大卫,代码又写得撇!
  • 代理模式:能够提供对象的替代品或其占位符。例子:访问控制在代理方法进行一些判断、

2. 怎样理解 Vue 的单向数据流?

父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。

3. computed 和 watch 的区别和运用的场景?

  • computed的是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;
  • watch: 更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;

4. Vue 响应式原理

对象内部通过 defineReactive 方法,使用 Object.defineProperty 将属性进行劫持,数组则是通过重写数组方法来实现。当页面使用对应属性时,每个属性都拥有自己的 dep 属性,存放他所依赖的watcher,当属性变化后会通知自己对应的watcher去更新。

5. Vue nextTick 原理

里面有个callbacks数组存放nextTick的回调,然后进行判断,如果浏览器支持Promise的话,就使用Promise运行,如果不支持Promise,则分别 判断MutationObserver、setImmediate,如果都不支持,最后会使用setTimeout

6. 路由原理 history 和 hash 两种路由方式的特点

  • hash模式利用监听 hashchange 事件监听hash变化进行跳转
  • history 根据pushState和replaceState两个方法只会改变url但浏览器不会刷新页面的原理

7. 谈谈前端的安全知识?XSS、CSRF,以及如何防范。

前端比较常见的攻击方式就是XSS(跨站脚本攻击),CSRF(跨站请求伪造)

  • XSS:攻击者在目标网站注入恶意代码,一般通过输入框等方式,防范:
    • 对用户输入进行转译
    • 在使用 .innerHTML、.outerHTML、document.write() 时要特别小心,不要把不可信的数据作为 HTML 插到页面上
  • CSRF:攻击者诱导用户进入第三方网站,第三方网站向被攻击网站发送伪造请求,利用受害者获取到攻击网站的凭证。防范:
    • 添加验证码
    • 判断请求的来源:检测Referer(并不安全,Referer可以被更改)
    • 使用Token(主流)
    • Samesite Cookie属性

8. 讲讲你对cookie的理解?包括SameSite属性。

cookie 是一段不超过 4KB 的小型文本数据,一般用来保存登录凭证。
SameSite 属性可以让 Cookie 在跨站请求时不会被发送,从而可以阻止跨站请求伪造攻击(CSRF)。
SameSite 可以有下面三种值:

  • Strict 仅允许一方请求携带 Cookie,即浏览器将只发送相同站点请求的 Cookie
  • Lax 允许部分第三方请求携带 Cookie
  • None 无论是否跨站都会发送 Cookie

9. webpack代码分割是怎么做的

通过optimization.splitChunks实现代码分割,常用的几个属性

  • chunks 指定哪些类型的chunk参与拆分
  • name 提取出来的公共模块将会以这个来命名
  • cacheGroups cacheGroups是splitChunks配置的核心,对代码的拆分规则全在cacheGroups缓存组里配置。

10. 什么是BFC

块级格式化上下文,BFC它决定了元素如何对其内容进行定位。
规则:

  • BFC就是一个块级元素,块级元素会在垂直方向一个接一个的排列
  • BFC就是页面中的一个隔离的独立容器,容器里的元素不会影响到外部元素
  • 属于同一个BFC的两个相邻的元素外边距会发生重叠
  • 计算BFC的高度时,浮动元素也参与计算 触发:
  • overflow: hidden
  • display: inline-block
  • position: absolute 和 fixed
  • display: table-cell 和 flex BFC解决了什么问题:
  • 使用Float脱离文档流,高度塌陷,清除浮动 clear:both;
  • Margin边距重叠