前端经典面试题

基础知识
说一下 http与https
1 | 回答: |
知识点:
当谈到HTTP(Hypertext Transfer Protocol)和HTTPS(Hypertext Transfer Protocol Secure)时,这两者都是用于在计算机网络上传输数据的协议。
HTTP 是一种无状态的协议,它使用TCP作为传输层协议,用于在Web浏览器和Web服务器之间传输信息,常用于页面请求和响应。HTTP的常见端口号是80。
然而,HTTPS是在HTTP上加密和身份验证的安全版本。它使用公钥加密算法(如SSL / TLS)来保护数据的传输,并确保通信的机密性和完整性。HTTPS的常见端口号是443。
HTTP和HTTPS的主要区别在于安全性。HTTPS通过加密和数字证书验证确保了数据的安全性和真实性。在HTTPS中,服务器和浏览器之间的通信是通过使用SSL / TLS协议来进行加密和保护的。
对于前端开发而言,了解HTTP和HTTPS的基本概念非常重要。当我们在开发网站时,需要考虑使用HTTPS来加强对数据的保护,特别是在进行用户登录、交易或传输敏感信息时。同时,还要了解如何在前端页面中正确地处理HTTP和HTTPS请求,以确保数据的安全性和正确性。https 协议的缺点
https 握手阶段比较费时,
会使页面加载时间延长 50%,增加 10%~20%的耗电。
https 缓存不如 http 高效,会增加数据开销。 SSL 证书也需要钱,功能越强大的证书费用越高。
SSL 证书需要绑定 IP,不能再同一个 ip 上绑定多个域名,ipv4 资源支持不了这种消耗
TCP的三次握手四次挥手
1 | 回答: |
1 | 目标: 关闭连接(四次挥手) |
知识点:TCP三次握手是在建立TCP连接时的一种握手过程。它确保了通信双方的可靠性和同步性。以下是TCP三次握手的步骤:
- 第一次握手(SYN):客户端向服务器发送一个SYN报文,请求建立连接。报文中包含随机生成的初始序列号(ISN)。
- 第二次握手(SYN + ACK):服务器收到客户端的SYN报文后,确认接收,并发送一个带有自己的SYN和确认号(ACK)的报文作为回应。服务器还会生成自己的初始序列号(ISN)并发送给客户端。
- 第三次握手(ACK):客户端收到服务器的确认后,发送一个ACK报文作为回应。该报文中的确认号是服务器发送的SYN + ACK报文的序列号加1。
通过这个三次握手过程,两个端点(客户端和服务器)可以确认彼此都已准备好进行数据传输。在握手完成后,TCP连接就建立起来了,双方可以开始传输数据。
TCP 和 UDP 的区别
1 | 回答: |
https常见状态码
1 | 回答: |
输入URL解析过程
1 | 1.DNS解析:将URL中的域名解析为对应的IP地址。 |
GET 和 POST 的区别
1 | 回答: |
- 知识点:
- 数据传递方式:GET请求将数据通过URL的查询参数进行传递,数据会附加在URL的后面,例如:http://example.com/path?key1=value1&key2=value2。而POST请求将数据包含在请求体中,不会直接暴露在URL中。
- 数据长度限制:GET请求的传输数据长度有限制,通常在几千个字符左右。而POST请求没有严格的长度限制,可以传输较大的数据。
- 数据安全性:GET请求的参数会暴露在URL中,可能被浏览器历史记录、服务器日志记录等所记录,不适合传输敏感信息。POST请求的参数不会直接暴露在URL中,因此相对来说更安全。
- 缓存:GET请求通常可以被浏览器缓存,因为GET请求无副作用,多次请求同一URL可以从缓存中获取响应。POST请求默认是不可缓存的,因为POST请求可能对服务器端产生副作用。
- 幂等性:GET请求是幂等的,多次重复请求对服务器产生的结果是一样的,不会产生副作用。而POST请求一般不是幂等的,多次请求可能会产生不同的结果,可能对服务器产生副作用。
浏览器常见存储方式
1 | 回答: |
HTML基础
对 HTML 语义化标签的理解
1 | 回答: |
常见语义化标签:
1
2
3
4
5
6
7
8
9
10<header>:定义文档或节的页眉,通常包含网站的logo、导航栏和页面的标题。
<nav>:用于定义页面的导航部分,包含页面的主要导航链接。
<main>:定义文档的主要内容,每个文档只能有一个<main>标签。
<article>:用于定义独立的文章或内容块,如博客文章、新闻报道等。
<section>:用于将相关内容组织在一起,如页面的不同章节、主题内容等。
<aside>:定义页面的附属内容,通常是侧边栏、辅助栏或广告等。
<footer>:定义文档或节的页脚,通常包含版权信息、联系方式等。
<figure>和<figcaption>:<figure>用于包含媒体内容(如图片、音频、视频等),而<figcaption>则用于给媒体内容添加标题或说明。
<time>:用于标记时间,可以表示具体的日期、时间或日期时间。
<mark>:用于标记文本中的突出部分,通常用黄色背景突出显示。
Doctype 的作用是什么
1 | 回答: |
如何优化网页的加载性能
1 | 回答: |
meta标签的作用
1 | 回答: |
知识点:
1
2
3
4
5
6
7
8
9
10
11charset: 指定文档的字符编码,如 <meta charset="UTF-8">。
name 和 content: 用于指定元数据的名称和值,如 <meta name="description" content="网页描述">。
http-equiv 和 content:用于提供与HTTP相关的元数据,如<meta http-equiv="refresh" content="5;url=example.com">
用于设置页面的自动刷新。
viewport:用于定义可视区域的设置,特别用于响应式设计,如 <meta name="viewport" content="width=device-width, initial-scale=1.0">
用于确保适应不同设备的屏幕宽度。
name="robots":用于控制搜索引擎爬虫的行为,如<meta name="robots" content="index,follow"> 用于指示允许搜索引擎索引页面并跟踪链接。
name="author":用于指定网页作者。
name="keywords":用于指定网页关键词。
http-equiv="X-UA-Compatible":用于控制浏览器的兼容性模式,如 <meta http-equiv="X-UA-Compatible" content="IE=edge">
用于告诉IE使用最新版本的渲染引擎。
a 标签的 href 属性和 target 属性
1 | 回答: |
img标签的常用属性及其作用
1 | 回答: |
HTML5 对比 4 有哪些不同之处?
contenteditable添加这个属性后可以直接进行修改标签内容选项
draggable这个属性必须写全等于true 使用后的标签可以进行拖拽
hidden属性相当于display:none进行隐藏使用
CSS基础
CSS选择器优先级
1 |
|
隐藏元素方法
1 | display: none;:这是最常见的一种隐藏元素的方法。通过将元素的显示属性设置为 none, |
让元素居中的方式
1 | 1.水平垂直居中(Flexbox):使用 Flexbox 布局可以很方便地实现元素的水平和垂直居中。 |
定位
1 |
|
css中的大小单位
1 |
|
css清除浮动
1 | 造成原因:浮动脱离标准流,不占位置(父盒子不会被撑开) |
谈谈你对BFC的理解
1 | BFC,即块级格式化上下文(Block Formatting Context),是CSS中一个非常重要的概念。BFC是页面上一个独立的渲染区域,决定了其中元素的布局及相互影响。 |
什么是CSS Sprites以及它的好处
1 | 精灵图/雪碧图 |
你对盒子模型的理解
1 | 所有元素都表示为一个个矩形的盒子,再用 CSS 去决定这些盒子的大小尺寸、显示位置、以及其他属性(如颜色、背景、边框等)。 |
你对Flex布局的理解
1 | 可查看我的专题 移动端布局 查看flex具体理解 |
javaScript基础
解释下什么是变量声明提升
1 | 变量提升(hoisting),是负责解析执行代码的 JavaScript 引擎的工作方式产生的一个特性。 |
JS 的参数是以什么方式进行传递的
1 | 基本数据类型和复杂数据类型的数据在传递时,会有不同的表现。 |
JS垃圾回收是怎么做的
JS中内存的分配和回收都是自动完成的,内存在不使用的时候会被垃圾回收器自动回收。
正因为垃圾回收器的存在,许多人认为JS不用太关心内存管理的问题,
但如果不了解JS的内存管理机制,我们同样非常容易成内存泄漏(内存无法被回收)的情况。
内存的生命周期
内存分配:当我们声明变量、函数、对象的时候,系统会自动为他们分配内存
内存使用:即读写内存,也就是使用变量、函数等
内存回收:使用完毕,由垃圾回收自动回收不再使用的内存
全局变量一般不会回收, 一般局部变量的的值, 不用了, 会被自动回收掉
垃圾回收算法说明
所谓垃圾回收, 核心思想就是如何判断内存是否已经不再会被使用了, 如果是, 就视为垃圾, 释放掉
下面介绍两种常见的浏览器垃圾回收算法: 引用计数 和 标记清除法
引用计数
IE采用的引用计数算法, 定义“内存不再使用”的标准很简单,就是看一个对象是否有指向它的引用。
如果没有任何变量指向它了,说明该对象已经不再需要了。
但它却存在一个致命的问题:循环引用。
如果两个对象相互引用,尽管他们已不再使用,垃圾回收器不会进行回收,导致内存泄露。
标记清除算法
现代的浏览器已经不再使用引用计数算法了。
现代浏览器通用的大多是基于标记清除算法的某些改进算法,总体思想都是一致的。
标记清除法:
标记清除算法将“不再使用的对象”定义为“无法达到的对象”。
简单来说,就是从根部(在JS中就是全局对象)出发定时扫描内存中的对象。
凡是能从根部到达的对象,都是还需要使用的。那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收。
从这个概念可以看出,无法触及的对象包含了没有引用的对象这个概念(没有任何引用的对象也是无法触及的对象)。
参考文章:JavaScript内存管理
谈谈你对 JS作用域链的理解
JavaScript 在执⾏过程中会创建一个个的可执⾏上下⽂。 (每个函数执行都会创建这么一个可执行上下文)
每个可执⾏上下⽂的词法环境中包含了对外部词法环境的引⽤,可通过该引⽤来获取外部词法环境中的变量和声明等。
这些引⽤串联起来,⼀直指向全局的词法环境,形成一个链式结构,被称为作⽤域链。
简而言之: 函数内部 可以访问到 函数外部作用域的变量, 而外部函数还可以访问到全局作用域的变量,
这样的变量作用域访问的链式结构, 被称之为作用域链
js全局有全局可执行上下文, 每个函数调用时, 有着函数的可执行上下文, 会入js调用栈
每个可执行上下文, 都有者对于外部上下文词法作用域的引用, 外部上下文也有着对于再外部的上下文词法作用域的引用
=> 就形成了作用域链
谈谈你对闭包的理解
1 | 闭包=内层函数+外层函数的变量 |
谈谈你对原型链的理解
要讲清楚这个问题,主要着重这几个方面:
什么是原型对象
构造函数, 原型对象, 实例的三角关系图
原型链如何形成
利用原型解决构造函数方法内存浪费问题
1.prototype 原型对象 每一个构造函数都有一个prototype属性 指向另一个对象
我们可以吧不变的方法直接定义再prototype对象上 这样所有对象实例就可以共享
2.原型对象里面的函数this指向还是实例对象
3.prototype中的constructor用来指向是哪一个函数
如果我们修改了原来的原型对象,给原型对象赋值的是一个对象
则必须手动利用constructor指回原来的构造函数
4.对象原型__proto__指向构造函数的prototype原型对象
注意__proto__非js标准[[prototype]]意义相同 只读属性 也有一个指向创建该实例的counstructor
谈谈对于继承的理解
原型继承
原型继承: 通过改造原型链, 利用原型链的语法, 实现继承方法!定义Person构造函数
定义Student构造函数
原型继承: 利用原型链, 继承于父级构造函数, 继承原型上的方法
语法: 子构造函数.prototype = new 父构造函数()
组合继承
组合继承有时候也叫伪经典继承,指的是将原型链 和 借用构造函数 call 技术组合到一块,
从而发挥二者之长的一种继承模式,其背后的思路: 是使用原型链实现对原型属性和方法的继承 (主要是方法),
而通过借用构造函数来实现对实例属性构造的继承。这样既通过在原型上定义方法实现了函数复用,又能保证每个实例都有它的自己的属性。
1 | // 1. 定义Person构造函数 |
寄生组合继承
student实例上有 name age, 而原型 __proto__上不需要再有这些属性, 所以利用 Object.create 改装下
Object.create(参数对象),
- Object.create 会创建一个新对象,
- 并且这个新对象的
__proto__会指向传入的参数对象
es6 - class 实现继承 extends
1 | // 继承关键字 => extends |
如何判断是否是数组?
方法一:使用 toString 方法
1 | function isArray(arg) { |
方法二:使用 ES6 新增的 Array.isArray 方法
1 | let arr = [1,2,3] |
谈谈你对this的理解
this 是一个在运行时才进行绑定的引用,在不同的情况下它可能会被绑定不同的对象。
默认绑定
默认绑定 (指向window的情况) (函数调用模式 fn() )
默认情况下,this 会被绑定到全局对象上,比如在浏览器环境中就为window对象,在node.js环境下为global对象。
隐式绑定
隐式绑定 (谁调用, this指向谁) (方法调用模式 obj.fn() )
如果函数的调用是从对象上发起时,则该函数中的 this 会被自动隐式绑定为对象
显式绑定
显式绑定 (又叫做硬绑定) (上下文调用模式, 想让this指向谁, this就指向谁)
硬绑定 => call apply bind
1 | 改变this指向 |
new 绑定
new 绑定 (构造函数模式)
另外,在使用 new 创建对象时也会进行 this 绑定
当使用 new 调用构造函数时,会创建一个新的对象并将该对象绑定到构造函数的 this 上
箭头函数中的this指向什么
箭头函数不同于传统函数,它其实没有属于⾃⼰的 this,
它所谓的 this 是, 捕获其外层 上下⽂的 this 值作为⾃⼰的 this 值。
并且由于箭头函数没有属于⾃⼰的 this ,它是不能被 new 调⽤的。
Promise 的静态方法
promise的三个状态: pending(默认) fulfilled(成功) rejected(失败)
- resolve函数被执行时, 会将promise的状态从 pending 改成 fulfilled 成功
- reject函数被执行时, 会将promise的状态从pending 改成 rejected 失败
Promise.reject()
1 | new Promise((resolve, reject) => { |
Promise.resolve()
1 | new Promise((resolve, reject) => { |
Promise.all([promise1, promise2, promise3]) 等待原则, 是在所有promise都完成后执行, 可以用于处理一些并发的任务
1 | // 后面的.then中配置的函数, 是在前面的所有promise都完成后执行, 可以用于处理一些并发的任务 |
Promise.race([promise1, promise2, promise3]) 赛跑, 竞速原则, 只要三个promise中有一个满足条件, 就会执行.then(用的较少)
宏任务 微任务 是什么
EventLoop事件循环队列
javascript是一门单线程执行的编程语言(同时只能做一件事) 同步任务和异步任务
同步任务:非耗时任务值的是在主线程上排队执行的任务
异步任务:耗时任务,异步任务由javascript委托给宿主环境进行执行 当异步任务执行完成后,会通知javascript主线程执行异步回调函数
宏任务和微任务
宏任务: 主线程代码, setTimeout 等属于宏任务, 上一个宏任务执行完, 才会考虑执行下一个宏任务
微任务: promise .then .catch的需要执行的内容, 属于微任务, 满足条件的微任务, 会被添加到当前宏任务的最后去执行
注意:async 函数只有从 await 往下才是异步的开始
async/await是什么?
ES7 标准中新增的 async 函数,从目前的内部实现来说其实就是 Generator 函数的语法糖。
它基于 Promise,并与所有现存的基于Promise 的 API 兼容。
async 关键字
async关键字用于声明⼀个异步函数(如async function asyncTask1() {...})async会⾃动将常规函数转换成 Promise,返回值也是⼀个 Promise 对象async函数内部可以使⽤await
await 关键字
await用于等待异步的功能执⾏完毕var result = await someAsyncCall()await放置在 Promise 调⽤之前,会强制async函数中其他代码等待,直到 Promise 完成并返回结果await只能与 Promise ⼀起使⽤await只能在async函数内部使⽤
相较于 Promise,async/await有何优势?
- 同步化代码的阅读体验(Promise 虽然摆脱了回调地狱,但 then 链式调⽤的阅读负担还是存在的)
- 和同步代码更一致的错误处理方式( async/await 可以⽤成熟的 try/catch 做处理,比 Promise 的错误捕获更简洁直观)
- 调试时的阅读性, 也相对更友好
深拷贝 浅拷贝
直接复制对象时应为复杂数据类型复制的是地址指向所以修改直接将原先的也修改了进而衍生出来的拷贝
浅拷贝
拷贝对象 Object.assgin() / 展开运算符{…obj}
拷贝数组 Array.protoype.concat() / […arr]
深拷贝
1.递归实现深拷贝
1 | function deepCopy(newObj, oldObj) { |
2.lodash/cloneDeep 使用库
1 | 引入js库实现深拷贝 |
3.通过JSON.stringify()实现
1 | const o= JSON.parse(JSON.stringify(obj)) |
DOM
DOM的事件流是什么
事件流
⼜称为事件传播,是⻚⾯中接收事件的顺序。DOM2级事件规定的事件流包括了3个阶段:
- 事件捕获阶段(capture phase)
- 处于⽬标阶段(target phase)
- 事件冒泡阶段(bubbling phase)
- 事件捕获阶段,为截获事件提供了机会
- 实际的⽬标元素接收到事件
- 事件冒泡阶段,可在这个阶段对事件做出响应
说说什么是事件委托
事件委托,就是利用了事件冒泡的机制,在较上层位置的元素上添加一个事件监听函数,
来管理该元素及其所有子孙元素上的某一类的所有事件。
1 | <ul id="list"> |
在绑定大量事件的时候,可以选择事件委托
优点
- 事件委托可以减少事件注册数量,节省内存占⽤!
- 当新增⼦元素时,⽆需再次做事件绑定,因此非常适合动态添加元素 (vue解析模板时, 会对新创建的元素, 额外进行绑定的)
浏览器底层原理
浏览器是如何解析CSS选择器的
在生成渲染树的过程中,渲染引擎会根据选择器提供的信息来遍历 DOM 树,找到对应的 DOM 节点后将样式规则附加到上面。
从左往右:.mod-nav => h3 => span
遍历所有的元素, 找有 .mod-nav 类的节点
从
.mod-nav开始遍历所有的⼦孙节点header、div、h3、ul….遍历所有的后代元素后, 知道了, 整个子孙后代只有一个 h3
找到
h3, 还要继续重新遍历h3的所有子孙节点, 去找span…
问题: 会进行大量树形结构子孙节点的遍历, 这是非常消耗成本的!
这在真实页面中⼀棵 DOM 树的节点成百上千的情况下,这种遍历方式的效率会非常的低,根本不适合采用。
从右往左:span => h3 => .mod-nav
先找到所有的
span节点 ,然后基于每⼀个span再向上查找h3由
h3再向上查找.mod-nav的节点最后触及根元素
html结束该分⽀遍历…
从右向左的匹配规则, 只有第一次会遍历所有元素找节点, 而剩下的就是在看父辈祖辈是否满足选择器的条件, 匹配效率大大提升!
因此,浏览器遵循 “从右往左” 的规则来解析 CSS 选择器!
浏览器是如何进行界面渲染的
获取 HTML ⽂件并进⾏解析,生成一棵 DOM 树(DOM Tree)
解析 HTML 的同时也会解析 CSS,⽣成样式规则(Style Rules)
根据 DOM 树和样式规则,生成一棵渲染树(Render Tree)
进行布局(Layout)(重排),即为每个节点分配⼀个在屏幕上应显示的确切坐标位置
进⾏绘制(Paint)(重绘),遍历渲染树节点,调⽤ GPU(图形处理器) 将元素呈现出来
重绘(repaint)和重排(回流reflow)是什么
重排
重排是指部分或整个渲染树需要重新分析,并且节点的尺⼨需要重新计算。
表现为 重新⽣成布局,重新排列元素。
重绘
重绘是由于节点的⼏何属性发⽣改变,或由于样式发⽣改变(例如:改变元素背景⾊)。
表现为某些元素的外观被改变。或者重排后, 进行重新绘制!
两者的关系
重绘不⼀定会出现重排,重排必定会触发重绘。
每个页面至少需要一次回流+重绘。(初始化渲染)
重排和重绘的代价都很⾼昂,频繁重排重绘, 会破坏⽤户体验、让界面显示变迟缓。
我们需要尽可能避免频繁触发重排和重绘, 尤其是重排
何时会触发重排
重排什么时候发生?
1、添加或者删除可见的DOM元素;
2、元素位置改变;
3、元素尺寸改变——边距、填充、边框、宽度和高度
4、内容改变——比如文本改变或者图片大小改变而引起的计算值宽度和高度改变;
5、页面渲染初始化;
6、浏览器窗口尺寸改变——resize事件发生时;
浏览器对重绘重排的优化
如果每句JS操作都去重排重绘的话,浏览器可能就会受不了!
所以浏览器会优化这些操作,浏览器会维护1个队列,把所有会引起重排、重绘的操作放入这个队列,
等队列中的操作到了一定的数量或者到了一定的时间间隔,浏览器就会flush队列,进行一个批处理。
这样就会让多次的重排、重绘变成了一次重排重绘。
虽然有了浏览器的优化,但有时候我们写的一些代码可能会强制浏览器提前flush队列,这样浏览器的优化可能起不到作用了。
比如当你请求向浏览器获取一些样式信息的时候(保证获取结果的准确性),就会让浏览器flush队列
- offsetTop, offsetLeft, offsetWidth, offsetHeight
- scrollTop/Left/Width/Height
- clientTop/Left/Width/Height
- 请求了getComputedStyle()
前端如何实现即时通讯
基于Web的前端,存在以下几种可实现即时通讯的方式:(详细可自行百度)
短轮询 (历史方案)
开个定时器, 每隔一段时间发请求 (实时性不强)
Comet - ajax长轮询(历史方案)
发送一个请求, 服务器只要数据不更新, 就一直阻塞 (服务器压力过大)
SSE
(利用了http协议, 流数据的传输, 并不是严格意义的双向通信, 无法复用连接)
WebSocket (主流)
性能和效率都高!
什么是浏览器同源策略
首先,同源是指资源地址的 “协议 + 域名 + 端⼝” 三者都相同,即使两个不同域名指向了同⼀ IP 地址,也被判断为⾮同源。
下面是一些地址的同源判断示例:
以下不同地址的页面, 去请求一个接口: http://store.company.com/getInfo
同源策略是 浏览器 的一种⽤于隔离潜在恶意⽂件的重要安全保护机制 !!! (服务器没有这个策略限制)
在浏览器中,⼤部分内容都受同源策略限制,除了以下三个资源获取类型的标签:
<img><link>script
如何实现跨域获取数据
JSONP:出现早,兼容性好 临时想出来的方案,缺点是只支持get请求不支持post请求
CORS:出现晚 w3c标准,属于ajax根本方案