2023跑路加薪秘籍一

您所在的位置:网站首页 javaweb多线程重放攻击 2023跑路加薪秘籍一

2023跑路加薪秘籍一

#2023跑路加薪秘籍一| 来源: 网络整理| 查看: 265

背景

base 深圳,面试中高级,在舒适圈待久了,加上行情不好,在这待得没啥盼头,直接出去面试都是一些线下面试,请假成本比较高。刚好朋友也有跳槽的想法,直接复习太枯燥了,就结合自己之前当面试官的经验出了一些模拟题(顺带把答案也整理整理)

自我介绍

> > 简单介绍了在上一家公司的工作内容以及自己的技术栈:

熟悉 JavaScript、HTML5、CSS3 掌握 Vue 和 React 全家桶并了解Vue和React的底层原理 熟悉 微信小程序、微信公众号H5、Uni-App项目的开发 熟悉 前端项目工程化,团队管理 熟悉 前端安全 和 性能优化 熟悉 Node.js 和 谷歌浏览器插件 正式开始 HTML5、浏览器、网络、安全

问:简单说说HTML5有哪些新特性吧

答:"主要分为以下几点:新增了一些语义化标签如header、nav、article、footer等,新增了媒体播放器video和audio,新增了一些表单组件如date之类的,新增了一些存储功能如localstorage、sessionstorage等,以及其它功能如canvas、svg、拖放、websocket、web worker等"

那你能说说语义化标签的作用是什么吗?

答:"em...主要是方便维护以及SEO优化吧"

那你能说说有哪些方法有利于网站的SEO吗

答:"那当然是金钱开道啊,哈哈哈,给搜索引擎交广告费!技术方面的话尽量使用语义化标签、用meta添加关键词和描述等、网页链接目录尽量扁平化、合理的设置Robots.txt文件、重要内容不要js输出等。不过我感觉不应该过分SEO,网站毕竟还是以内容为主,我们之前开发大点的需要SEO的项目一般都是使用的SSR技术"

嗯,确实。那你能说说SSR的优缺点吗?

答:"优点主要是有利于SEO和首屏加载速度,缺点的话主要是增加了项目的复杂度、还有就是库和代码的兼容性、其次最主要的是增加了服务器的负担,性能方面需要做优化,比如使用缓存策略、降级策略等,如果是少数页面的话还可以考虑使用预渲染,但是这块我不太熟,之前做调研的时候看到过"

刚你说到了缓存策略,你能简单说说缓存策略吗

答:"浏览器缓存分为强缓存和协商缓存,强缓存使用Expres、Catch-Control控制,通过设置max-age等设置过期时间,没过期的话直接命中缓存,从本地缓存中读取。协商缓存使用Etag、Last-Modified等控制,向服务器发送If-None-Match 和 If-Modified-Since 的请求,服务器根据Etag判断是否一致,一致则返回304状态码,命中缓存,不一致返回新的Etag和文件,返回200"

如果同时存在强缓存和协商缓存,它们的优先级是怎样的

答:"Catch-Control > Expres > Etag > Last-Modified"

刚你提到了状态码,你能简单说说吗?

答:"HTTP状态码主要分为以下几类:1* 表示信息状态码请求中、2* 表示请求成功、3* 表示重定向、4* 表示客户端错误、5* 表示客户端错误。常见的有200、304、400、401、403、404、405、500、502等"

你能说说localstorage、sessionstorage、cookie他们之间的区别吗?

答:"localstorage是本地持久化存储,关闭或者刷新页面也不会清空,大小限制一般在5mb。sessionstorage是页面临时存储,关闭标签页就会被清空,大小限制也在5mb左右。cookie在发送请求的时候一般会自动带在请求头上,大小限制在4kb左右,可以设置存储时间,以前主要用于token存储,后面由于安全原因一般比较少用了,就算使用也会在请求头上添加HttpOnly,一般改用自定义的请求头token"

那如果打开了多个标签页,它们之间的sessionstorage是否共享?

答:"正常来说是共享的,但是我之前看过资料也自己测试过,如果是通过链接或者window.open打开的会共享,但是如果是手动打开的标签页输入url进去是不共享的"

可以的,那再问问你如果一个项目的localstorage被存满了我们该怎么办?

答:"存满了的话继续往里面存就会报错,我觉得处理方法有以下几种,首先检查一下项目的代码,跨页面传值之类的尽量使用url或者vuex、redux等传递,如果还是满的话一般来说单个项目不会有这么多,一是划分域名,二是规范化命名我们项目中用到的localstorage存储比如添加项目前缀,然后清掉别人项目的数据。其中划分域名是最佳解决方案,清掉别人项目的数据是兜底方案"

能简单说说canvas和svg的区别和使用场景吗?

答:"svg相对于png之类的来说不受像素限制,可以更好的保真,它支持事件处理器,复杂度高会渲染比较慢,不适合大型游戏,一般常见于谷歌地图、iconfont等场景。canvas依赖像素,文本渲染能力较弱,不支持事件处理器,可以输出图片和添加图片,适合于游戏绘制和其它交互,像我们之前做过的项目里面就用来绘制各种图形等"

那你了解什么是canvas污染吗?能简单说下吗?

答:"将一张跨域的图片放进canvas,canvas就被污染了无法读取数据,这个应该是浏览器考虑到用户安全做出的限制"

能具体讲讲吗?

答:"应该是浏览器的同源策略,应该是为了避免第三方网站读取其他网站的图片数据Canvas(渲染第三方图片请求不受 CORS 限制),造成其他网站的信息泄露"

如果我们自己网站要用的话有什么解决方案吗

答:"嗯,这个要看能否控制图片的响应了。如果可以控制的话,利用 cors 跨域,并在图片请求发起时增加 crossOrigin = "Anonymous" 设置;否则只能自己的网站做个代理,让网站与图片同源。"

那浏览器为什么会有同源策略呢?

答:"为了防止第三方网站用iframe嵌套目标网站随意访问目标网站中的密码和账号等私密信息,造成用户的财产损失和隐私泄露,没有同源限制的话第三方请求就可以获取到目标网站中的私密信息如token,非法者可以利用 img/form 等支持跨域的标签,请求会自动带上 cookie,这就是所谓的 csrf 攻击,可以通过添加请求头httpOnly使其攻击失败,同源策略需要注意的是只拦截响应不拦截请求"

为什么浏览器不直接拦截非同源请求?

答:"浏览器需要通过响应才能判断是否拦截,否则无法使用cors跨域请求了"

很好,那你能简单讲讲XSS、CSRF吗?

答:"XSS跨站脚本攻击页面的 DOM 结构,是纯粹发生在客户端的攻击。反射型:反射型 XSS 只是简单地把用户输入的数据 “反射” 给浏览器。存储型:恶意代码在服务器中,当浏览器请求时脚本从服务器返回并执行。CSRF跨站请求伪造主要指第三方网站恶意获取用户cookie去向目标网站发送恶意请求。"

那我们作为开发者该怎样防范XSS攻击和CSRF攻击呢?

答:"我们可以通过对用户的输入和后端数据的输出以及URL那些做转义,外加设置HttpOnly,使用CSP配置白名单(通过HTTP头部的Content-Security-Policy或者meta标签设置)去防范XSS攻击。对于CSRF我们一般可以通过验证码、Referer Check来源访问检查、自定义Token处理"

你说对于CSRF的防御可以通过判断Referer来实现,请问这个判断是否靠谱,Referer可以修改吗?如果可以我们怎么实现修改?

答:"虽然在浏览器中JavaScript无法直接修改Referer,但是这个只能作为辅助判断,因为我们可以利用使用一些插件或者PHP、Nodejs等伪造Referer。不过也不是每个用户都会伪造Referer的,所以我们可以在一些不那么机密的地方使用,比如图片和其他资源的防盗链。对于像身份验证之类的非常重要的地方我们还是使用验证码自定义Token这两种结合来判断更靠谱一些"

很好,看来你对安全这方面研究的还比较多,那你知道XST(跨站式追踪攻击)、HPP(HTTP 参数污染)、会话重放攻击吗?

答:"em...这块略微有点了解,XST看过相关的文章,指的是客户端可以通过Trace、Track请求获取到完整的请求头信息,这种方式可以绕过HttpOnly的限制,从而获取到一些机密信息如cookie等。防御方法是后端不支持Trace、Track请求即可。HPP主要是通过传输相同的key不同的value绕过后端一些限制,这块主要也是后端处理,我不是很熟悉。会话重放攻击主要指攻击者发送一个目的主机已经接收过的包,特别是在认证的过程中,用于认证用户身份所接收的包,来达到欺骗系统的目的,主要用于身份认证过程,破坏认证的安全性。也可利用系统中POST请求数据包未针对单个请求设置有效的验证参数,导致会话请求可以重放,无限制的向数据库中插入海量数据,或无限制的上传文件到系统中,造成资源浪费。之前后端那块我不是很清楚,前端这块主要是请求接口的时候,CURTIME(将当前的时间戳加上后端系统时间)、NONCE(加密的uuid)、SIGNATURE(用sha256将会话密钥、CURTIME、uuid生成的一个加密字符)传给后端"

你刚说到用sha256库加密,你知道它的实现原理吗?

答:"em...这个我没太了解过,只是知道它是类似于md5的一种hash加密算法,理论上来说是不可逆的,相对于md5来说更安全一些,暴力碰撞破解的几率更小。"

好的,那除了不可逆的加密算法之外,对称加密和非对称加密你有了解吗?

答:"对称加密是指加密解密都只有一个密钥,非对称加密是指总共两个密钥,一个是公钥一个是私钥,公钥加密的数据需要用私钥来解密,私钥加密的数据需要公钥来解密,公钥放在前端私钥放在后端,这种加密方式更安全更难破解"

那假设我们需要对用户登录时候的账号和密码都进行加密,你会选择用什么加密呢?为什么?

答:"如果是少量数据和地方需要加密,而且机密程度比较高我们首选非对称加密的方式,因为这种加密方式安全性能更好。"

那假设我们整个系统都需要使用加密呢?但是我又不想让登录之类的地方容易破解,你会怎么做呢?

答:"我觉得有两种方案,一种是登录等地方使用非对称加密,其他地方使用对称加密。第二种是将对称加密的密钥通过非对称加密一遍,然后用这个被非对称加密过的密钥去进行对称加密。"

嗯,不错,刚聊到HTML5新特性有Web Worker,能简单说说它的用处和为什么要设计该API吗

答:"em...这块我只是简单的了解和使用过,Worker主要有三种,一种是专用Worker,只能由主线程创建和使用。一种是共享Worker,可以被同一域下的访问。一种是服务 Worker,它是完全独立于它正在处理或服务的网页,它们充当了 web 应用程序、浏览器和网络之间的代理服务器。它可以用来做缓存处理、数据预获取、大量数据计算之类的,我在之前的项目中有简单用它来处理过地图数据经纬度转换等。我个人觉得这个API设计的意义在于提高JS执行方面的性能,因为在 HTML 页面中,如果在执行脚本时,页面的状态是不可响应的,直到脚本执行完成之后页面才会响应。由于JS是一门单线程语言,所以遇到比如大量复杂数据计算的时候,可能会造成页面有点卡顿,这个时候可以使用Worker开辟线程去后台默默计算,不影响主线程的渲染等,从而优化用户体验。"

那JS在浏览器中是单线程执行的话,它的异步是怎么实现的

答:"这个就涉及到事件循环Event Loop了,在JS中,异步任务被分为两种,一种是宏任务,一种是微任务。JS在执行过程中按照从上到下,从左到右的顺序依次执行,如果遇到异步任务就会把该任务塞进任务队列中,执行顺序是同步任务 --> 微任务 --> 宏任务"

很好,那你能说说在浏览器环境中哪些是宏任务,哪些是微任务吗?Node环境中呢?

答:"浏览器环境:宏任务script、setTimeout、setInterval、I/O操作、UI渲染等,微任务MutationObserver、Promise。Node环境中微任务多了一个process.NextTick,且优先级高于其它微任务"

那你说说Node中的事件循环和浏览器中的事件循环有什么区别

答:"除了process.NextTick之外要看Node版本,好像10之前的和浏览器表现一致,10之后的应该主要是执行时机不同吧,microtask 在事件循环的各个阶段之间执行,microtask 在事件循环的 macrotask 执行完之后执行"

我看你说setTimeout是宏任务,那么请问setTimeout是否精准?为什么?

答:"setTimeout不一定精准,主要有两点原因,第一点是因为最小值为4毫秒,第二点是因为它是宏任务,会被丢到宏任务队列中延期执行。"

那我们应该怎么解决setTimeout不精准的问题呢?

答: "有两种方案,一种主要就是计算一下setTimeout代码开始前和执行时间的差值,然后动态修正时间。第二种可以用web worker去开辟新线程去实现。"

为什么 setTimeout 有最小时延 4ms ? 如何实现一个 0ms 的 setTimeout?

答: "厂商和W3C标准互相制衡弄出来的结果,每个厂商的实现可能有细微差异。所以不一定最小延迟是4ms,W3C定义好像是超过五层setTimeout最小延迟为4ms。可以用postMessage模拟实现0延迟的setTimeout"

CSS3

ok,那考察你一些CSS基础吧,简单说下BFC

答:"BFC的意思是块级文档作用域,可以通过设置overflow: 不为visible、display: flex/inline-block/table-cell/table-caption、设置元素浮动、或者根元素自带。主要作用一般用作解决高度塌陷问题、解决margin重叠问题"

那BFC可以清除浮动吗?

答:"准确点来说应该不叫清除浮动吧,只是两个BFC互不影响,看起来像清除了浮动。清除浮动一般用after、before结合clear: both清除"

简单聊聊浏览器渲染过程和原理吧

答:"HTML被HTML解析器解析成DOM Tree --> CSS被CSS解析器解析成CSSOM Tree --> DOM Tree 和 CSSOM Tree附加到一起形成渲染树 Render Tree --> 布局,根据渲染树计算每个节点的几何信息生成布局树Layout Tree --> 对布局树进行分层,并生成分层树 --> 为每个图层生成绘制列表 --> 渲染绘制,根据计算好的绘制信息绘制整个页面,并将其提交到合成线程 --> 合成线程将信息发给浏览器进程,浏览器进程根据相关信息生成页面并显示"

能详细说说DOM Tree的生成过程吗?

答:"应该是浏览器渲染引擎执行的时候先获取相关字节(0、1),转成字符串(我们写的代码),再将这些字符串转换成Token(html、body等),然后生成节点对象并构建DOM Tree。"

那如果浏览器在渲染绘制过程中遇到了JS文件,会发生什么情况呢?为什么会发生这种情况?我们的优化和解决方式是什么?

答:"会阻塞渲染,会先去执行JS的逻辑,执行完之后再去重新处理渲染逻辑。这是因为浏览器GUI渲染进程和JS引擎线程是两个线程,并且由于可以用JS去干扰GUI渲染进程的结果,所以为了防止渲染异常以及不可预测,所以做了互斥的设计。我们可以把script脚本放在前面,这种优点是兼容性比较好。还有一种方案是使用async或者defer去异步加载。"

async 和 defer有啥区别?

答:"async加载的话就是异步随机加载,加载完立刻执行,适合不需要操作DOM的脚本。defer就是先异步加载,等渲染树构建完成之后再执行"

浏览器渲染特别消耗性能,我们有什么优化方案吗?

答: "尽量减少重排,因为重排一定会触发重绘。尽量减少对DOM的操作,比如像Vue和React一样使用虚拟DOM将多次操作合并为一次。尽量在操作频繁的地方使用防抖和节流来优化性能,比如resize事件。尽量少用CSS表达式和内联样式。尽量使用transform开启硬件加速,让浏览器做优化。尽量使用类名修改样式以及使用requestAnimationFrame处理动画之类的"

那假设后端给我们一次性返回十万条数据,让我们在列表上渲染,你会怎么做?

答:"我觉得一般来说,这种情况不太合理。我会和后端先沟通,沟通无果的话就给leader反映,让leader找后端leader去沟通评审,最后从根源上解决这个不合理的要求。不过从技术实现角度上来看的话,我觉得有好几种解决方案,分割数据setTimeout定时去渲染、DocumentFragement文档碎片、虚拟列表、前端实现分页组件、下拉触底前端处理数据并渲染、Worker等,简单做且没有特定滚动加载交互的话就搞个前端分页组件实现,复杂点效果好点就是用虚拟列表实现"

怎么实现一个虚拟列表?

答:"确定可视区域item显示的条数limit,向上滑动的当前位置起始位与最后位置,确定显示元素范围,确定每个元素的top,当向上滑动时,确定当前的位置与最后元素的位置索引,根据当前位置与最后元素位置,渲染可视区域。可以使用"

能聊聊重排、重绘、合成吗?

答:"重排:当通过js或者css改变了元素的高度、宽度、位置等,修改了元素的几何位置属性,会触发重排。重绘:修改了元素的背景颜色等,没有修改元素的几何位置属性,会触发重绘,触发重排一定会触发重绘。合成是一种将页面的各个部分分离成层,分别将它们栅格化,然后在称为“合成线程”的中组合为页面的技术。效率比重绘和重排都高, z-index、transform等会触发合成。"

解决1px问题

答:"属性选择器来命中 devicePixelRatio 为某一值的情况(兼容性没那么好)、伪元素先放大后缩小、viewport 缩放来解决(有时候不合适)"

设置小于12px的字体

答:"-webkit-transform:scale(0.5) 会整体收缩、用图片实现"

画一条0.5px的线

答:"transform: scale(0.5,0.5);"

水平垂直居中

答:"flex、position、margin、transform等"

单行、多行文本溢出隐藏

答:"单行:overflow: hidden; text-overflow: ellipsis; white-space: nowrap; 多行的多加了display:-webkit-box; -webkit-box-orient:vertical;-webkit-line-clamp:2;"

JavaScript

简单概述一下JS数据类型吧

答:"JS数据类型分为引用类型和基本类型,基本数据类型有number、string、null、boolean、undefined、symbol、bigint,引用数据类型object。基本数据类型存在堆中赋值的时候是直接拷贝内容,引用数据类型存在栈中赋值的时候是取的地址,所以我们平时开发的时候需要注意引用数据类型,该深克隆的时候就深克隆"

我看你说到了Symbol 和 Bigint,你有用过这两种数据类型吗?知道它们的应用场景吗?

答:"这两种数据类型都是ES6之后新推出的,Symbol我有用过,用来做全局唯一公共变量之类的,或者key值,这个可以避免重复。Bigint这个可以绕过最大安全值MAX_SAFE_INTEGER的限制,去做一些大整数的操作,这块我没有在实际业务中使用过。"

嗯,好的,那我们平常开发一般用什么方式去实现深克隆?

答:"一般用的是Lodash之类的工具库来实现,或者简单点的数据类型就用JSON方法处理也是可以的,JSON方法处理的话不支持函数、undefined、日期那些"

说到Lodash库,现在有两种引入方式分别是import { cloneDeep } from 'lodash'; import cloneDeep from 'lodash/cloneDeep';,你会选择哪一种引入方式,为什么?

答:"选第二种吧,之前有做过项目的体积压缩和优化方案,当时发现Lodash库有点大,看了下源码之后发现用第一种是相当于引入了整个Lodash库,第二种才是按需加载"

那你有什么办法可以让用户既用第一种方式引入又达到Tree Shaking的目的吗?

答:"em...这块没有研究,目前能想到的一个骚操作就是写个插件编译的时候转换语法。"

好的,如果让你实现一个cloneDeep,你的思路是什么?

答:"写个递归函数,判断数据类型,基本数据类型直接拷贝,引用数据类型继续用递归函数递归,需要注意的是需要判断各种特殊数据比如日期、正则、Set、Map、Symbol、BigInt等,还有用hasOwnProperty判断是否是原型链的属性啥的"

知道尾调用和尾递归吗?有什么优点?

答:"尾调用就是说在函数的尾部调用另一个函数,尾递归就是在函数的尾部调用自身。尾递归不但能在严格模式下优化内存,还可以避免更改递归函数自身"

聊聊V8内存回收机制和相应代码优化

答:"js运行时,需要分配内存空间来储存变量和值,当变量不再参与运行时,就需要回收被占用的内存空间。js具有自动回收机制,会定期对那些不再使用的变量、对象等进行释放。js存在两种变量,全局变量和局部变量,全局变量会一直存在,局部变量执行完就会释放(闭包除外)。主要方式有标记清除、引用计数、GC算法,V8用的是GC算法。代码优化方案一般是把不用的数组设置length为0,对象设置为null,函数中可复用的表达式在循环的时候最好放在外部。"

GC算法实现原理

答:"实现了准确式GC,GC算法采用了分代式垃圾回收机制。因此,v8将内存分为新生代和老生代两部分。新生代算法:新生代中的对象一般存活时间较短,使用 Scavenge GC 算法,分为from空间和to空间,每当from存满的时候,就会判断from空间的对象是否存活,存活就存到to空间,失活则销毁,当复制完成之后to和from互换。老生代算法:老生代中的对象一般存活时间较长且数量也多,使用了两个算法,分别是标记清除算法和标记压缩算法"

为什么TO超过25%要晋升老生代?

答:"第一个问题是为了不影响后续FORM空间的分配"

有了解其他垃圾回收的算法吗?

答:"引用计数,设置引用计数,判断引用计数是否为0,为0自动回收,优点是回收快,减少程序卡顿,缺点是无法回收循环引用对象,且消耗比较大。标记清除算法,遍历对象把未标记活动的对象删除,优点是可以回收引用对象,缺点是空间碎片化、回收不及时。标记整理算法,标记算法的增强,标记的操作阶段和标记清除一致,清除整理阶段会先执行整理,移动对象位置,使地址是连续的,优点是减少碎片空间,缺点是回收没那么及时"

怎样避免内存泄露

答:"别使用意外的全局变量、setInterval定时器那些要及时清理、DOM 元素删除的时候应该处理相关引用、使用闭包要合理"

ok, 数据类型检测的方式有哪些

答:"typeof:数组、对象、null都会被判断为object,其它判断都正常、instanceof:instanceof只能正确判断引用数据类型,而不能判断基本数据类型, instanceof运算符可以用来测试一个对象在其原型链中是否存在一个构造函数的prototype属性、constructor: 可以很好的判断数据的类型(如果创建了一个对象改变了对象的原型,此时constructor判断不准)、Object.prototype.toString.call() 可以很好的判断数据的类型"

instanceof原理?

function myInstanceof(left, right) { // 获取对象的原型 let proto = Object.getPrototypeOf(left) // 获取构造函数的prototype对象 let prototype = right.prototype // 判断构造函数的prototype 对象是否在对象的原型链上 while (true) { if (!proto) return false; if (proto === prototype) return true; // 如果没找到,就继续从原型上找 proto = Object.getPrototypeOf(proto); } } 复制代码

判断数组的方式有哪些?

1. Object.prototype.toString.call():`Object.prototype.toString.call(obj).slice(8,-1) === 'Array'` 2. 原型链做判断:`obj.__proto__ === Array.prototype` 3. Array.isArray():`Array.isArrray(obj)` 4. instanceof判断:`obj instanceof Array` 5. Array.prototype.isPrototypeOf:`Array.prototype.isPrototypeOf(obj)` 复制代码

有了解过装箱拆箱吗?

答:"装箱是指将基本数据类型通过基本包装类型转为对应的引用数据类型(String()、Number()等),有隐式和显示之分。拆箱是指将引用类型数据转为基本的数据类型(用valueOf()、toString()方法来实现),有隐式和显示之分。"

new操作符的实现原理

new的操作符实现步骤如下: 创建一个新的空对象 将构造函数的作用域赋给新对象(也就是将对象的__proto__属性指向构造函数的prototype属性) 指向构造函数中的代码,构造函数中的this指向该对象(为这个对象添加属性和方法) 判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象 具体实现: function objectFactory() { let newObject = null; let constructor = Array.prototype.shift.call(arguments); let result = null; // 判断参数是否是一个函数 if (typeof constructor !== "function") { console.error("type error"); return; } // 新建一个空对象,对象的原型为构造函数的 prototype 对象 newObject = Object.create(constructor.prototype); // 将 this 指向新建对象,并执行函数 result = constructor.apply(newObject, arguments); // 判断返回对象 let flag = result && (typeof result === "object" || typeof result === "function"); // 判断返回结果 return flag ? result : newObject; } // 使用方法 objectFactory(构造函数, 初始化参数); 复制代码 剩余的JS基础、框架、小程序、移动端、项目架构、性能优化、团队管理、手写、算法

下次再说,先去准备面试了



【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3