自用前端面试题梳理(ING)

227 阅读22分钟

计算机网络

1.http/2比http/1.1好在哪里?

答案来源:leetcode.cn/leetbook/re…

  1. 相比于 HTTP/1.X 的文本(字符串)传送, HTTP/2.0 采用二进制传送。客户端和服务器传输数据时把数据分成帧,帧组成了数据流,流具有流 ID 标识和优先级,通过优先级以及流依赖能够一定程度上解决关键请求被阻塞的问题。
  2. HTTP/2.0 支持多路复用。因为流 ID 的存在, 通过同一个 HTTP 请求可以实现多个 HTTP 请求传输,客户端和服务器可以通过流 ID 来标识究竟是哪个流从而定位到是哪个 HTTP 请求。
  3. HTTP/2.0 头部压缩。HTTP/2.0 通过 gzip 和 compress 压缩头部然后再发送,同时通信双方会维护一张头信息表,所有字段都记录在这张表中,在每次 HTTP 传输时只需要传头字段在表中的索引即可,大大减小了重传次数和数据量。
  4. HTTP/2.0 支持服务器推送。 服务器在客户端未经请求许可的情况下,可预先向客户端推送需要的内容,客户端在退出服务时可通过发送复位相关的请求来取消服务端的推送

2.常见前端攻击形式有哪些?各自基本攻击原理是什么?各自如何防范?

SQL注入攻击

原理
SQL注入即是指web应用程序对用户输入数据的合法性没有判断,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息,当应用程序使用输入内容来构造动态SQL语句以访问数据库时,会发生SQL注入攻击。本质是:数据和代码未分离,即数据当做了代码来执行。

防御

  1. 严格限制web应用的数据库访问权限,严格执行最低授权;
  2. 后端代码检查输入的数据是否符合预期,严格限制变量的类型,利用正则进行一些匹配处理;
  3. 对进入数据库的特殊字符进行转义处理或者编码转换;
  4. 所有的查询语句坚实使用数据库提供的参数化查询接口,从而避免直接将前端输入的变量拼接到SQL语句中。

XSS攻击

原理
XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。

防御

  1. Web 页面渲染的所有内容或者渲染的数据都必须来自于服务端;
  2. 尽量不要从 URLdocument.referrerdocument.forms 等这种 DOM API 中获取数据直接渲染。
  3. 尽量不要使用 evalnew Function()document.write()document.writeln()window.setInterval()window.setTimeout()innerHTMLdocument.createElement() 等可执行字符串的方法;
  4. 必须对涉及 DOM 渲染的方法传入的字符串参数做 escape 转义;
  5. 前端渲染的时候对任何的字段都需要做 escape 转义编码;
  6. 在设置cookie时,将其属性设为HttpOnly;
  7. CSP即建立白名单,开发者明确告诉浏览器哪些外部资源可以加载和执行;
  8. 转义输入输出的内容,对于引号、尖括号、斜杠进行转义;
  9. 对于显示富文本,通过白名单过滤的办法过滤script标签。

CSRF攻击

原理
又称为“跨站请求伪造”,是指黑客引诱用户打开黑客的网站,在黑客的网站中利用用户的登录状态发起跨站请求,即通过伪装成来自受信任用户的请求来利用受信任的网站。

  1. 用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;
  2. 在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;
  3. 用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;
  4. 网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问网站A;
  5. 浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以用户C的权限处理该请求,导致来自网站B的恶意代码被执行。

防御

  1. 对Cookie设置SameSite属性。该属性Cookie不随着跨域请求发送;
  2. 验证HTTP Header中的Referer字段。Referer字段记录了http请求的来源地址,通过校验该地址可以阻止其它网站来源的非法请求;
  3. 在请求中添加token验证;
  4. 在HTTP的Header中添加自定义属性并进行验证。
  5. 在网站关键业务步骤添加验证码验证

中间人攻击

原理
是一种“间接”的入侵攻击,这种攻击模式是通过各种技术手段将入侵者控制的一台计算机虚拟放置在网络连接中的两台通信计算机之间,这台计算机就称为“中间人”。
中间人攻击的攻击者与通讯的两端分别创建独立的联系,并交换其所收到的数据,使通讯的两端认为他们正在通过一个私密的连接与对方直接对话,但事实上整个会话都被攻击者完全控制。这种攻击可能被使用在简单的获得访问消息的权利,或者能使得攻击者在转发消息之前先修改消息。

防御

  1. 将一些机密信息进行加密后再传输,这样即使被中间人截取也难以破解;
  2. 通过设备或IP异常检测。如用户以前从未使用某个设备或IP访问系统;
  3. 通过设备或IP频率检测。如单一的设备或IP同时访问大量的用户帐号;
  4. 进行带外身份认证,具体过程是:系统进行实时的自动电话回叫,将二次PIN码发送至SMS(短信网关),短信网关再转发给用户,用户收到后,再将二次PIN码发送到短信网关,以确认是否是真的用户。带外认证提供了多种不同的认证方式及认证渠道,它的好处是:所有的认证过程都不会被MITM攻击者接触到。

点击劫持(网页嵌套攻击)

原理
点击劫持是一种视觉欺骗的攻击手段。攻击者将需要攻击的网站通过 iframe 嵌套的方式嵌入自己的网页中,并将 iframe 设置为透明,在页面中透出一个按钮诱导用户点击。
用户在登陆 A 网站的系统后,被攻击者诱惑打开第三方网站,而第三方网站通过 iframe 引入了 A 网站的页面内容,用户在第三方网站中点击某个按钮(被装饰的按钮),实际上是点击了 A 网站的按钮。

防御

  1. X-FRAME-OPTIONS,这是一个HTTP响应头,有三个可选值:DENY,表示页面不允许通过iframe方式展示;SAMEORIGIN,表示页面可以在相同域名下通过iframe方式展示;ALLOW-FROM,表示页面可以在指定来源的iframe中展示;(最有效手段)
  2. 通过JS代码禁止内嵌,top!==windowtop.location!==window.location表示存在iframe内嵌,符合该判断时,通过代码跳出iframe。

3.TCP和UDP有什么区别?

leetcode.cn/leetbook/re…

  1. TCP是面向连接的,UDP则面向无连接
  2. TCP传输具有可靠性,UDP传输不具备可靠性
  3. TCP以字节流形式传输,UCP以数据报文段形式传输
  4. TCP传输效率慢,UDP传输效率快
  5. TCP所需资源多,UCP所需资源少
  6. 主要应用场景不同,TCP在文件传输、邮件传输场景应用广,UDP在即时通讯、域名转换场景应用广
  7. TCP首部字节是20~60,UDP是8

4.什么叫面向连接和面向无连接?

面向连接:是指通信双方在通信时,要事先建立一条通信线路。 其有三个过程:建立连接、使用连接和释放连接。
面向无连接:是指通信双方不需要事先建立一条通信线路,而是把每个带有目的地址的包(报文分组)送到线路上,由系统自主选定路线进行传输。

5.为什么UDP传输效率比TCP要快?

  1. UDP是无连接的,即发送数据之前不需要消耗时间建立连接,而TCP需要
  2. UDP没有TCP的握手、确认、窗口、重传、拥塞控制等机制,而这些机制都是需要消耗时间的

(X)6.什么是有状态协议?什么是无状态协议?

无状态协议:其中客户端将请求发送到服务器,服务器根据给定状态返回响应。它不需要服务器为多个请求保留会话信息或每个通信伙伴的状态。 有状态协议:如果客户端向服务器发送请求,那么它期望某种响应,如果它没有得到任何响应,那么它会重新发送请求。

算法

1.写一下快速排序

leetcode.cn/circle/arti…

2.在一维数组中寻找出现次数最多的数字,并算出你算法的时间和空间复杂度

leetcode.cn/problems/g5…

3.写一下洗牌算法

leetcode.cn/problems/sh…

4.给定一个二叉树, 找到该树中两个指定节点的最近公共祖先

leetcode.cn/problems/er…

5.实现数组的reduce方法

reduce接口文档:www.runoob.com/jsref/jsref…
reduce介绍、使用、自行实现:blog.csdn.net/u013448372/…

JavaScript

1.什么是闭包?闭包有哪些应用场景?闭包有什么缺陷?写一个简单的闭包示例

定义
闭包就是能够读取其他函数内部变量的函数,在本质上,闭包是将函数内部和函数外部连接起来的桥梁。只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数”。
代码示例

function a(){
  const i=0;
  function b(){
    console.log(++i);
  }
  return b;
}
const c = a();
c();

简单说明
上文代码中,变量c实际上是指向了函数b,再执行c()后就会打印i的值,函数a之外的变量c引用了函数a内的函数b。也就是说,当函数a的内部函数b被函数a外的一个变量引用的时候,就创建了一个闭包。闭包使得js的垃圾回收机制不会收回a所占用的资源,因为函数a的内部函数b的执行需要依赖函数a中的变量i。
底层原理
从JS解释器层面描述闭包的产生和原理,需要引入几个概念:函数的执行环境(execution context)、活动对象(call object)、作用域(scope)、作用域链(scope chain)。然后以函数a从定义到执行的过程为例阐述这一下闭包的原理:

  1. 当定义函数a的时候,JS解释器会将函数a的作用域链(scope chain)设置为定义函数a时其所在的“环境”,假设此时a是一个全局函数,则其scope chain中只有window对象;
  2. 当函数a执行的时候,a会进入相应的执行环境(execution context),接下来需要为函数a创建执行环境;
  3. 在创建函数a执行环境的过程中,首先会为函数a添加一个scope属性,即函数a的作用域,其值就为第1步中的scope chain。即a.scope=a的作用域链。
  4. 然后执行环境会创建一个活动对象(call object),活动对象也是一个拥有属性的对象,但它不具有原型而且不能通过JS代码直接访问。创建完活动对象后,把活动对象添加到函数a的作用域链的最顶端。此时a的作用域链包含了两个对象:a的活动对象和window对象。
  5. 下一步是在活动对象上添加一个实参(arguments)属性,它保存着调用函数a时所传递的实际参数。
  6. 最后把所有函数a的形参(parameter)和内部的函数b的引用也添加到函数a的活动对象上。在这一步中,完成了函数b的的定义,因此如同第3步,函数b的作用域链被设置为b所被定义的环境,即a的作用域。

到此,整个函数a从定义到执行的步骤就完成了。此时a返回函数b的引用给外部变量c,又因为函数b的作用域链包含了对函数a的活动对象的引用,也就是说函数b可以访问到a中定义的所有变量和函数。函数b被变量c引用,函数b又依赖函数a,因此函数a在返回后不会被GC回收。

当函数b执行的时候亦会像以上步骤一样,执行时b的作用域链包含了3个对象:b的活动对象、a的活动对象和window对象。

内部函数是如何访问到起外部函数的变量的
当在内部函数b中访问一个变量的时候,搜索顺序是先搜索自身的活动对象,如果存在则返回,如果不存在将继续搜索函数a的活动对象,依次查找,直到找到为止。如果整个作用域链上都无法找到,则返回undefined。如果函数b存在原型对象(prototype),则在查找完自身的活动对象后,先查找自身的原型对象,再继续查找。这就是JS中的变量查找机制。
闭包情况下,内部函数b的作用域链包含了对外部函数a的活动对象的引用,而外部函数a的活动对象中保存着a所有的变量和函数,所以按照JS中的变量查找机制,内部函数b自然可以访问到外部函数a的变量。

应用场景

  1. 保护函数内的变量安全。以上文闭包代码为例,函数a中的变量i只有函数b才能访问,其他途径不能访问,因此保护了i的安全性;
  2. 需要在内存中维持一个变量。以上文闭包代码为例,因为闭包,js的垃圾回收机制不会收回函数a所占用的资源,所以函数a中的变量i会一直存在于内存中;
    使用实例

缺点
由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,甚至会造成内存溢出或者内存泄漏。
解决方法是:在退出函数之前,将不使用的局部变量全部删除(赋值为null)。

2.什么是函数柯里化(Currying)?函数柯里化作用?优缺点有哪些?

定义
leetcode.cn/leetbook/re…
实现柯里化的核心是闭包。
作用
leetcode.cn/leetbook/re…
缺点

  1. 函数嵌套多;
  2. 占内存,有可能导致内存泄漏(因为本质是配合闭包实现的);
  3. 效率差(因为使用递归);
  4. 变量存取慢,访问性很差(因为使用了arguments);

3.CommonJS和ESM是什么?CommonJS和ESM有什么区别?

定义
CommonJS(CJS)是前端主要的模块化方案之一,其目标是为 JavaScript 在网页浏览器之外创建模块约定。模块在运行JavaScript 脚本的常规网页浏览器所提供的不同的环境下可以重复使用。
ESModules(ESM)是用于处理模块的ECMAScript标准,是ES6原生支持。
区别
答案来源

  1. CJS 模块输出的是一个值的拷贝,ESM 模块输出的是值的引用。
  2. CJS 模块是运行时加载,ESM 模块是编译时输出接口。
  3. CJS 模块的require()是同步加载模块,ESM 模块的import命令是异步加载,有一个独立的模块依赖的解析阶段。
  4. CJS运行依赖Node,不能直接运行于浏览器上,而ESM在浏览器和Node上均可以运行。

4.什么是深拷贝和浅拷贝?如何实现深拷贝

定义
深拷贝:创建一个新的对象,将原对象的各项属性的“值”(数组的所有元素)拷贝过来,是“值”而不是“引用”,新对象跟原对象不共享内存,修改新对象不会改到原对象。
浅拷贝: 将原对象引用直接赋给新对象、新对象只是原对象的一个引用,新对象跟原对象共享内存,修改新对象会改到原对象。
实现深拷贝

  1. 通过递归调用层层拷贝,loadash工具库中的cloneDeep就是通过递归方式,并针对不同类型的对象进行拷贝处理,还兼容处理了循环引用的情况;
  2. 利用串行化,例如JSON.stringify + JSON.parse,但是不能处理循环引用的情况;
  3. 当被拷贝对象没有嵌套对象时,直接使用Object.assgin实现深拷贝;
  4. 使用structuredClone API,该api较新,注意各浏览器兼容版本;
  5. jQuery.extend()

5.Map和WeakMap有什么区别?Map和WeakMap的key有什么不同?

  1. WeakMap仅接受对象作为key,而Map除对象外还接受原始数据类型作为key,例如字符串,数字等;
  2. Map是可以被迭代的,而WeakMap是不可以被迭代的,所以WeakMap不支持keys、values、entries这些方法也没有size属性;
  3. WeakMap每个key对自己所引用对象的引用都是弱引用,在没有其他引用和该key引用同一对象时,这个对象将会被垃圾回收,而Map的key所引用的对象则不会被垃圾回收,即便没有其他引用。

6.什么是基本类型和引用类型?基本类型和引用类型各有哪些?

定义
leetcode.cn/leetbook/re…
举例
基本数据类型有undefined、null、number、string、boolean、bigint、symbol。
引用数据类型有:Object、Array、Function等。

7.Map和Object有什么区别?有Object了为什么还要有Map?

区别

  1. Map是顶级对象Object的一个子类;
  2. Map需要通过其内置函数Map()来创建,Object可以通过new Object()来创建,也可以通过字面量的方法来声明对象;
  3. Map的key可以是任何类型,Object的key必须是string或者symbol;
  4. Map针对键值对的增删改查提供了属性和接口,但Object则不具备如此丰富的接口;
  5. Map可以直接被迭代,Object不可以
  6. 频繁增删键值对的情况下,Map比Object性能更好;
  7. Map不可被JSON序列化,但是Object可以。

为什么还要有Map

  1. 对于Object,可能通过原型链访问到未定义的属性,例如constructor属性;
  2. Object的key只能是string或者symbol,不能满足很多情况在以其他类型为key进行存储的需求;
  3. Object并没有针对数据存储进行属性和接口的优化,对数据增删改查、迭代、复制以及合并等操作并不方便;
  4. Obejct不能记录键值对的插入顺序,而Map则可以确保键值对的遍历的顺序和插入顺序一致
  5. 给定同样内存大小的情况下,Map可以比Object多存储约50%的键值对;
  6. 对键值对进行大量增删查操作,Object性能表现不佳,Map表现更好。

8.Object.create()、new Obejct()和{}有什么区别?

  1. {}是JS对象字面量创建形式,和new Obejct()在本质上没有区别,都继承了Object原型链(Obejct.prototype)的属性和方法;
  2. Object.create(obj,propertiesObject)方法支持接受两个参数,obj是一个对象,是必传的,会作为新创建的对象的原型,propertiesObject是可选参数,propertiesObject的属性名称将是新创建的对象的属性名称,propertiesObject属性的值应该是属性描述符,可见Object.create()不会和new Obejct()和{}一样默认继承 Object原型链的属性和方法,而是将入参指定对象继承到原型链上,
  3. Object.create(null)创建的对象没有任何属性和方法,new Obejct()和{}创建的对象则有Object原型链上的属性和方法
  4. Object.create(Object.prototype)则是指定了Object原型链作为新创建对象的原型,效果和new Obejct()和{}就是一样的了

9.如何获取某一对象的key和value?

  1. 如果是Map或者Array等可以通过自身接口获取;
  2. 对于一般对象,可以通过Object.keys(obj)来获取包含所有key的数组,通过Object.values(obj)获取包含所有value的数组;
  3. 通过for循环来遍历来获取:
for(const key in obj){ }

10.判断对象类型的方法有哪些?

  1. typeof,但是有局限性:对于数组、函数、对象和null,typeof都会统一返回object;对NaN返回是number;
  2. Object.prototype.toString.call(obj),可以精准区分出各种类型。

HTML+CSS

Vue

1.VDOM是什么?有什么作用?

定义
vdom是一个使用javascript模拟DOM结构的树形结构,这个树结构包含整个DOM结构的信息。
作用
使用原生js或者jquery写页面时,频繁地直接操作DOM非常麻烦,而且每次改变都会引起DOM的渲染十分消耗性能,而通过vdom将DOM的对比放在js层,通过对比不同之处来选择性渲染DOM节点,从而提高渲染效率。

2.vuex和localStorage有什么区别?如何实现一个响应式的localStorage?

区别

  1. 存储的数据和位置,vuex存储的是状态,存储在内存中,localStorage存储的是字符串类型数据,以文件形式存储在本地;
  2. 持久性,当刷新页面时,vuex存储的值会丢失,而localStorage不会丢失;
  3. 应用场景,vuex是用于组件之间通信的,localStorage则主要用于页面之间的传值;
  4. 响应式,vuex是vue的状态管理机制,是响应式的,一个组件的数据源变化,可以使另一个组件发生响应式变化,而localStorage是单纯数据存储,不支持响应式;

实现响应式localStorage
具体解析

// LocalStorage项目键与依赖它的Vue实例列表之间的映射
const storeItemSubscribers = {};

// 当前正在初始化的Vue实例
let target = undefined;

const getItem = window.localStorage.getItem;
localStorage.getItem = (key) => {
  console.info("Getting", key);

  // 收集依赖的Vue实例
  if (!storeItemSubscribers[key]) storeItemSubscribers[key] = [];
  if (target) storeItemSubscribers[key].push(target);

  // 调用原始函数 
  return getItem.call(localStorage, key);
};

const setItem = window.localStorage.setItem;
localStorage.setItem = (key, value) => {
  console.info("Setting", key, value);

  // 更新相关Vue实例中的值
  if (storeItemSubscribers[key]) {
    storeItemSubscribers[key].forEach((dep) => {
      if (dep.hasOwnProperty(key)) dep[key] = value;
    });
  }
  
  // 调用原始函数 
  setItem.call(localStorage, key, value);
};

Vue.mixin({
  beforeCreate() {
    console.log("beforeCreate", this._uid);
    target = this;
  },
  created() {
    console.log("created", this._uid);
    target = undefined;
  }
});

3.vue的响应式的原理是什么?

原理
Vue的响应式是通过 Object.defineProperty 对数据进行劫持,并结合发布订阅者模式实现。 Vue 利用 Object.defineProperty 创建一个 observe 来劫持监听所有的属性,把这些属性全部转为 getter 和 setter。Vue 中每个组件实例都会对应一个 watcher 实例,它会在组件渲染的过程中把使用过的数据属性通过 getter 收集为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。

webpack

1.什么是Tree shaking?Tree shaking原理是什么?

定义
Tree-Shaking 是一种基于 ES Module规范的清除未使用代码(Dead Code Elimination)的技术,它会在运行过程中静态分析模块之间的导入导出,确定 ESM 模块中哪些导出值未被其它模块使用,并将其删除,以此实现打包产物的优化。
原理

  1. ESM规定所有的导入导出语句只能出现在模块顶层,且导入导出的模块名必须为字符串常量,所以ESM下模块之间的依赖关系是高度确定的,与运行状态无关,编译工具只需要对ESM模块做静态分析,就可以从代码字面量中推断出哪些模块值未曾被其它模块使用;
  2. 基于1的前提,Webpack中通过以下几个阶段来实现Tree-Shaking:
    Make阶段,收集模块导出变量并记录到模块依赖关系图 ModuleGraph 变量中;
    Seal阶段,遍历ModuleGraph,标记出各个模块的导出列表中,哪些导出值有被其它模块用到,哪些没有;
    生成代码,webpack会根据ModuleGraph中记录的每个模块的导出值和导出值被使用的情况,来生成不同的的代码,最终模块导出列表中未背使用的值都不会被定义在__webpack_exports__ 对象中,形成了一段不可能被执行的代码;
    最后删除无用代码,一般借助Terser、UglifyJS等DCE工具来实现。

正则

1.匹配模板字符串的${}中括号里的内容

正则语法
本题答案

其他框架

用过koa么?没用过的话可以大概说一下它是什么?有什么显著特点?

koa是由Express原班人马打造的,致力于成为一个更小、更富有表现力、更健壮的Web框架。详细介绍
主要特点

  1. 使用koa编写web应用,通过组合不同的generator,可以免除重复繁琐的回调函数嵌套,并极大地提升错误处理的效率。
  2. koa不在内核方法中绑定任何中间件,它仅仅提供了-个轻量优雅的函数库,使得编写Web应用变得得心应手

思维题

小白鼠试毒问题:有100瓶药水,其中一瓶是毒药,只要1小滴,就足以让小白鼠24小时内死亡,请问怎么在1天内用最少的老鼠找出这瓶毒药?

经典腾讯面试题:小白鼠试毒问题