2022 秋招前端面试题(八)(附答案)
let、const、var 的区别
(1)块级作用域: 块作用域由 { }
包括,let 和 const 具有块级作用域,var 不存在块级作用域。块级作用域解决了 ES5 中的两个问题:
内层变量可能覆盖外层变量
用来计数的循环变量泄露为全局变量
(2)变量提升: var 存在变量提升,let 和 const 不存在变量提升,即在变量只能在声明之后使用,否在会报错。
(3)给全局添加属性: 浏览器的全局对象是 window,Node 的全局对象是 global。var 声明的变量为全局变量,并且会将该变量添加为全局对象的属性,但是 let 和 const 不会。
(4)重复声明: var 声明变量时,可以重复声明变量,后声明的同名变量会覆盖之前声明的遍历。const 和 let 不允许重复声明变量。
(5)暂时性死区: 在使用 let、const 命令声明变量之前,该变量都是不可用的。这在语法上,称为暂时性死区。使用 var 声明的变量不存在暂时性死区。
(6)初始值设置: 在变量声明时,var 和 let 可以不用设置初始值。而 const 声明变量必须设置初始值。
(7)指针指向: let 和 const 都是 ES6 新增的用于创建变量的语法。 let 创建的变量是可以更改指针指向(可以重新赋值)。但 const 声明的变量是不允许改变指针的指向。
深浅拷贝
浅拷贝:只考虑对象类型。
简单版深拷贝:只考虑普通对象属性,不考虑内置对象和函数。
复杂版深克隆:基于简单版的基础上,还考虑了内置对象比如 Date、RegExp 等对象和函数以及解决了循环引用的问题。
什么是中间人攻击?如何防范中间人攻击?
中间⼈ (Man-in-the-middle attack, MITM) 是指攻击者与通讯的两端分别创建独⽴的联系, 并交换其所收到的数据, 使通讯的两端认为他们正在通过⼀个私密的连接与对⽅直接对话, 但事实上整个会话都被攻击者完全控制。在中间⼈攻击中,攻击者可以拦截通讯双⽅的通话并插⼊新的内容。
攻击过程如下:
客户端发送请求到服务端,请求被中间⼈截获
服务器向客户端发送公钥
中间⼈截获公钥,保留在⾃⼰⼿上。然后⾃⼰⽣成⼀个伪造的公钥,发给客户端
客户端收到伪造的公钥后,⽣成加密 hash 值发给服务器
中间⼈获得加密 hash 值,⽤⾃⼰的私钥解密获得真秘钥,同时⽣成假的加密 hash 值,发给服务器
服务器⽤私钥解密获得假密钥,然后加密数据传输给客户端
对浏览器的缓存机制的理解
浏览器缓存的全过程:
浏览器第一次加载资源,服务器返回 200,浏览器从服务器下载资源文件,并缓存资源文件与 response header,以供下次加载时对比使用;
下一次加载资源时,由于强制缓存优先级较高,先比较当前时间与上一次返回 200 时的时间差,如果没有超过 cache-control 设置的 max-age,则没有过期,并命中强缓存,直接从本地读取资源。如果浏览器不支持 HTTP1.1,则使用 expires 头判断是否过期;
如果资源已过期,则表明强制缓存没有被命中,则开始协商缓存,向服务器发送带有 If-None-Match 和 If-Modified-Since 的请求;
服务器收到请求后,优先根据 Etag 的值判断被请求的文件有没有做修改,Etag 值一致则没有修改,命中协商缓存,返回 304;如果不一致则有改动,直接返回新的资源文件带上新的 Etag 值并返回 200;
如果服务器收到的请求没有 Etag 值,则将 If-Modified-Since 和被请求文件的最后修改时间做比对,一致则命中协商缓存,返回 304;不一致则返回新的 last-modified 和文件并返回 200;
很多网站的资源后面都加了版本号,这样做的目的是:每次升级了 JS 或 CSS 文件后,为了防止浏览器进行缓存,强制改变版本号,客户端浏览器就会重新下载新的 JS 或 CSS 文件 ,以保证用户能够及时获得网站的最新更新。
代码输出结果
输出结果: false
这里首先定义了两个变量 f 和 g,我们知道变量是可以重新赋值的。后面是一个匿名自执行函数,在 if 条件中调用了函数 g(),由于在匿名函数中,又重新定义了函数 g,就覆盖了外部定义的变量 g,所以,这里调用的是内部函数 g 方法,返回为 true。第一个条件通过,进入第二个条件。
第二个条件是[] == ![],先看 ![] ,在 JavaScript 中,当用于布尔运算时,比如在这里,对象的非空引用被视为 true,空引用 null 则被视为 false。由于这里不是一个 null, 而是一个没有元素的数组,所以 [] 被视为 true, 而 ![] 的结果就是 false 了。当一个布尔值参与到条件运算的时候,true 会被看作 1, 而 false 会被看作 0。现在条件变成了 [] == 0 的问题了,当一个对象参与条件比较的时候,它会被求值,求值的结果是数组成为一个字符串,[] 的结果就是 '' ,而 '' 会被当作 0 ,所以,条件成立。
两个条件都成立,所以会执行条件中的代码, f 在定义是没有使用 var,所以他是一个全局变量。因此,这里会通过闭包访问到外部的变量 f, 重新赋值,现在执行 f 函数返回值已经成为 false 了。而 g 则不会有这个问题,这里是一个函数内定义的 g,不会影响到外部的 g 函数。所以最后的结果就是 false。
有哪些可能引起前端安全的问题?
跨站脚本 (Cross-Site Scripting, XSS): ⼀种代码注⼊⽅式, 为了与 CSS 区分所以被称作 XSS。早期常⻅于⽹络论坛, 起因是⽹站没有对⽤户的输⼊进⾏严格的限制, 使得攻击者可以将脚本上传到帖⼦让其他⼈浏览到有恶意脚本的⻚⾯, 其注⼊⽅式很简单包括但不限于 JavaScript / CSS / Flash 等;
iframe 的滥⽤: iframe 中的内容是由第三⽅来提供的,默认情况下他们不受控制,他们可以在 iframe 中运⾏JavaScirpt 脚本、Flash 插件、弹出对话框等等,这可能会破坏前端⽤户体验;
跨站点请求伪造(Cross-Site Request Forgeries,CSRF): 指攻击者通过设置好的陷阱,强制对已完成认证的⽤户进⾏⾮预期的个⼈信息或设定信息等某些状态更新,属于被动攻击
恶意第三⽅库: ⽆论是后端服务器应⽤还是前端应⽤开发,绝⼤多数时候都是在借助开发框架和各种类库进⾏快速开发,⼀旦第三⽅库被植⼊恶意代码很容易引起安全问题。
一个 tcp 连接能发几个 http 请求?
如果是 HTTP 1.0 版本协议,一般情况下,不支持长连接,因此在每次请求发送完毕之后,TCP 连接即会断开,因此一个 TCP 发送一个 HTTP 请求,但是有一种情况可以将一条 TCP 连接保持在活跃状态,那就是通过 Connection 和 Keep-Alive 首部,在请求头带上 Connection: Keep-Alive,并且可以通过 Keep-Alive 通用首部中指定的,用逗号分隔的选项调节 keep-alive 的行为,如果客户端和服务端都支持,那么其实也可以发送多条,不过此方式也有限制,可以关注《HTTP 权威指南》4.5.5 节对于 Keep-Alive 连接的限制和规则。
而如果是 HTTP 1.1 版本协议,支持了长连接,因此只要 TCP 连接不断开,便可以一直发送 HTTP 请求,持续不断,没有上限; 同样,如果是 HTTP 2.0 版本协议,支持多用复用,一个 TCP 连接是可以并发多个 HTTP 请求的,同样也是支持长连接,因此只要不断开 TCP 的连接,HTTP 请求数也是可以没有上限地持续发送
li 与 li 之间有看不见的空白间隔是什么原因引起的?如何解决?
浏览器会把 inline 内联元素间的空白字符(空格、换行、Tab 等)渲染成一个空格。为了美观,通常是一个<li>
放在一行,这导致<li>
换行后产生换行字符,它变成一个空格,占用了一个字符的宽度。
解决办法:
(1)为<li>
设置 float:left。不足:有些容器是不能设置浮动,如左右切换的焦点图等。
(2)将所有<li>
写在同一行。不足:代码不美观。
(3)将<ul>
内的字符尺寸直接设为 0,即 font-size:0。不足:<ul>
中的其他字符尺寸也被设为 0,需要额外重新设定其他字符尺寸,且在 Safari 浏览器依然会出现空白间隔。
(4)消除<ul>
的字符间隔 letter-spacing:-8px,不足:这也设置了<li>
内的字符间隔,因此需要将<li>
内的字符间隔设为默认 letter-spacing:normal。
介绍下 promise 的特性、优缺点,内部是如何实现的,动手实现 Promise
1)Promise 基本特性
1、Promise 有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)
2、Promise 对象接受一个回调函数作为参数, 该回调函数接受两个参数,分别是成功时的回调 resolve 和失败时的回调 reject;另外 resolve 的参数除了正常值以外, 还可能是一个 Promise 对象的实例;reject 的参数通常是一个 Error 对象的实例。
3、then 方法返回一个新的 Promise 实例,并接收两个参数 onResolved(fulfilled 状态的回调);onRejected(rejected 状态的回调,该参数可选)
4、catch 方法返回一个新的 Promise 实例
5、finally 方法不管 Promise 状态如何都会执行,该方法的回调函数不接受任何参数
6、Promise.all()方法将多个多个 Promise 实例,包装成一个新的 Promise 实例,该方法接受一个由 Promise 对象组成的数组作为参数(Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例),注意参数中只要有一个实例触发 catch 方法,都会触发 Promise.all()方法返回的新的实例的 catch 方法,如果参数中的某个实例本身调用了 catch 方法,将不会触发 Promise.all()方法返回的新实例的 catch 方法
7、Promise.race()方法的参数与 Promise.all 方法一样,参数中的实例只要有一个率先改变状态就会将该实例的状态传给 Promise.race()方法,并将返回值作为 Promise.race()方法产生的 Promise 实例的返回值
8、Promise.resolve()将现有对象转为 Promise 对象,如果该方法的参数为一个 Promise 对象,Promise.resolve()将不做任何处理;如果参数 thenable 对象(即具有 then 方法),Promise.resolve()将该对象转为 Promise 对象并立即执行 then 方法;如果参数是一个原始值,或者是一个不具有 then 方法的对象,则 Promise.resolve 方法返回一个新的 Promise 对象,状态为 fulfilled,其参数将会作为 then 方法中 onResolved 回调函数的参数,如果 Promise.resolve 方法不带参数,会直接返回一个 fulfilled 状态的 Promise 对象。需要注意的是,立即 resolve()的 Promise 对象,是在本轮“事件循环”(event loop)的结束时执行,而不是在下一轮“事件循环”的开始时。
9、Promise.reject()同样返回一个新的 Promise 对象,状态为 rejected,无论传入任何参数都将作为 reject()的参数
2)Promise 优点
①统一异步 API
Promise 的一个重要优点是它将逐渐被用作浏览器的异步 API ,统一现在各种各样的 API ,以及不兼容的模式和手法。
②Promise 与事件对比
和事件相比较, Promise 更适合处理一次性的结果。在结果计算出来之前或之后注册回调函数都是可以的,都可以拿到正确的值。 Promise 的这个优点很自然。但是,不能使用 Promise 处理多次触发的事件。链式处理是 Promise 的又一优点,但是事件却不能这样链式处理。
③Promise 与回调对比
解决了回调地狱的问题,将异步操作以同步操作的流程表达出来。
④Promise 带来的额外好处是包含了更好的错误处理方式(包含了异常处理),并且写起来很轻松(因为可以重用一些同步的工具,比如 Array.prototype.map() )。
3)Promise 缺点
1、无法取消 Promise,一旦新建它就会立即执行,无法中途取消。
2、如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。
3、当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
4、Promise 真正执行回调的时候,定义 Promise 那部分实际上已经走完了,所以 Promise 的报错堆栈上下文不太友好。
4)简单代码实现最简单的 Promise 实现有 7 个主要属性, state(状态), value(成功返回值), reason(错误信息), resolve 方法, reject 方法, then 方法
5)面试够用版
6)大厂专供版
说一说 SessionStorage 和 localStorage 还有 cookie
二分查找--时间复杂度 log2(n)
题目描述:如何确定一个数在一个有序数组中的位置
实现代码如下:
对 CSSSprites 的理解
CSSSprites(精灵图),将一个页面涉及到的所有图片都包含到一张大图中去,然后利用 CSS 的 background-image,background-repeat,background-position 属性的组合进行背景定位。
优点:
利用
CSS Sprites
能很好地减少网页的 http 请求,从而大大提高了页面的性能,这是CSS Sprites
最大的优点;CSS Sprites
能减少图片的字节,把 3 张图片合并成 1 张图片的字节总是小于这 3 张图片的字节总和。
缺点:
在图片合并时,要把多张图片有序的、合理的合并成一张图片,还要留好足够的空间,防止板块内出现不必要的背景。在宽屏及高分辨率下的自适应页面,如果背景不够宽,很容易出现背景断裂;
CSSSprites
在开发的时候相对来说有点麻烦,需要借助photoshop
或其他工具来对每个背景单元测量其准确的位置。维护方面:
CSS Sprites
在维护的时候比较麻烦,页面背景有少许改动时,就要改这张合并的图片,无需改的地方尽量不要动,这样避免改动更多的CSS
,如果在原来的地方放不下,又只能(最好)往下加图片,这样图片的字节就增加了,还要改动CSS
。
两栏布局的实现
一般两栏布局指的是左边一栏宽度固定,右边一栏宽度自适应,两栏布局的具体实现:
利用浮动,将左边元素宽度设置为 200px,并且设置向左浮动。将右边元素的 margin-left 设置为 200px,宽度设置为 auto(默认为 auto,撑满整个父元素)。
利用浮动,左侧元素设置固定大小,并左浮动,右侧元素设置 overflow: hidden; 这样右边就触发了 BFC,BFC 的区域不会与浮动元素发生重叠,所以两侧就不会发生重叠。
利用 flex 布局,将左边元素设置为固定宽度 200px,将右边的元素设置为 flex:1。
利用绝对定位,将父级元素设置为相对定位。左边元素设置为 absolute 定位,并且宽度设置为 200px。将右边元素的 margin-left 的值设置为 200px。
利用绝对定位,将父级元素设置为相对定位。左边元素宽度设置为 200px,右边元素设置为绝对定位,左边定位为 200px,其余方向定位为 0。
New 操作符做了什么事情?
Canvas 和 SVG 的区别
(1)SVG: SVG 可缩放矢量图形(Scalable Vector Graphics)是基于可扩展标记语言 XML 描述的 2D 图形的语言,SVG 基于 XML 就意味着 SVG DOM 中的每个元素都是可用的,可以为某个元素附加 Javascript 事件处理器。在 SVG 中,每个被绘制的图形均被视为对象。如果 SVG 对象的属性发生变化,那么浏览器能够自动重现图形。
其特点如下:
不依赖分辨率
支持事件处理器
最适合带有大型渲染区域的应用程序(比如谷歌地图)
复杂度高会减慢渲染速度(任何过度使用 DOM 的应用都不快)
不适合游戏应用
(2)Canvas: Canvas 是画布,通过 Javascript 来绘制 2D 图形,是逐像素进行渲染的。其位置发生改变,就会重新进行绘制。
其特点如下:
依赖分辨率
不支持事件处理器
弱的文本渲染能力
能够以 .png 或 .jpg 格式保存结果图像
最适合图像密集型的游戏,其中的许多对象会被频繁重绘
注:矢量图,也称为面向对象的图像或绘图图像,在数学上定义为一系列由线连接的点。矢量文件中的图形元素称为对象。每个对象都是一个自成一体的实体,它具有颜色、形状、轮廓、大小和屏幕位置等属性。
iframe 有那些优点和缺点?
iframe 元素会创建包含另外一个文档的内联框架(即行内框架)。
优点:
用来加载速度较慢的内容(如广告)
可以使脚本可以并行下载
可以实现跨子域通信
缺点:
iframe 会阻塞主页面的 onload 事件
无法被一些搜索引擎索识别
会产生很多页面,不容易管理
画一条 0.5px 的线
采用 transform: scale()的方式,该方法用来定义元素的 2D 缩放转换:
采用 meta viewport 的方式
这样就能缩放到原来的 0.5 倍,如果是 1px 那么就会变成 0.5px。viewport 只针对于移动端,只在移动端上才能看到效果
display:inline-block 什么时候会显示间隙?
有空格时会有间隙,可以删除空格解决;
margin
正值时,可以让margin
使用负值解决;使用
font-size
时,可通过设置font-size:0
、letter-spacing
、word-spacing
解决;
评论