文章目录
- 一面
- 1.自我介绍
- 2.URL输入后发生了什么?
- 3.DNS解析
- 4.数据包的一层层封装
- 5.浏览器渲染过程
- 6.TCP
- 7.tcp报文有哪些内容(说了一些字段,和为了实现可靠传输的一些字段)
- 8.怎么实现可靠传输的?
- 9.http版本更新变化
- 10.https介绍,与http区别
- 11.https的tls层握手的过程
- 12.http缓存,怎么实现控制的各自的字段
- 13.事件处理机制,事件捕获,目标,冒泡,可以改变事件在哪个阶段执行吗?怎么改?
- 14.事件委托,怎么拿到具体的子元素
- 15.BFC
- 16.tihs指向
- 二面
- 1.自我介绍
- 2.大二就去实习了,你感觉实习都有哪些收获?谈到最大的收获就是建立起了前端的知识框架,增长了视野
- 3.说说你理解的前端
- 4.前端工程化,webpack的作用,如何处理兼容性
- 5.cdn服务器,我们通信的过程中,中间代理转发的服务器叫啥
- 6.http缓存有了解过吗max-age和s-maxage的作用和区别?谁的优先级更高?no-cache和no-store区别
- 7.Vue用的那个版本?2/3都用过,版本区别?(先说哪些方面不同,再谈具体的)
- 8.TypeScript也用过?学到什么程度?说下TS的一些关键词(提到类型,泛型,类型推断,类型断言,接口)infer有用过吗?(比较深入的点,没用过)
- 9.flex布局了解过吗flex-basis什么意思?默认值是?
- 10.层叠上下文有了解过吗?说下作用
- 11.事件循环了解过吗?讲下事件循环
- 12.从事件循环的角度谈下异步和同步
- 13.UMD了解过吗?这里没深入了解过,说自己了解过ESModule和CommonJs,讲下这两个的区别(说了两点:1.同步加载和异步加载的区别;2.导入值是否可修改的差别)
- 14.算法:分苹果:1000个苹果,10个盒子
- 15.算法:“10+2+12-3-10”计算最终结果
- 三面
- 1.自我介绍
- 2.项目中的难点(简历强相关)
- 3.大二就去实习了,为什么后来没有实习,是感觉实习学不到东西吗?
- 4.你平时是怎么学习前端的?(这个问题抖音三面都问了)
- 5.承接上问,平时写过哪些内容的博客?有没有自己去实践做一些东西?(讲了下自己写过的博客内容,讲了下正在做的一个远程控制软件)
- 6.你觉得自己哪里学的比较好(说到vue框架和js的一些核心原理理解得比较好)
- 7.vue的响应式原理,vue3与vue2的区别?
- 8.浏览器为什么要设置两个队列而不是一个队列
- 9.为什么要有事件循环这个机制,其他语言有的也没有这个机制不是也活得好好吗
- 10.你觉得自己的优缺点有哪些?
- 11.有没有系统的看过哪本书(这里给了一些建议)
- 12.除了写博客,有没有自己去做一些小东西(说到自己在做一个远程控制软件)
- 13.远程控制怎么实现的呢,这里讲到webRTC
- 14.算法题:一道手写题实现接口Array.prototype.getReader = function () {...}
- 15.你对未来的一个职业规划是怎样的,最近的2/3年?(先成长为一个能够独当一面的工程师吧hhh)
- 16.你有什么想实现的但还未实现的想法吗
一面
1.自我介绍
当在前端面试中进行自我介绍时,你可以按照以下结构进行组织:
- 介绍自己:
开始时,简要介绍自己的姓名和背景。你可以提及自己的学历、专业背景以及工作经验。
- 技术专长:
提及你在前端领域的技术专长和经验。列举你熟悉的编程语言、前端框架、库和工具等。可以着重强调你在哪些方面特别擅长,例如前端开发、响应式设计、用户界面设计、性能优化等。
- 项目经验:
介绍你在过去的项目中所承担的角色和贡献。可以选择其中几个有代表性的项目来详细介绍,包括项目的规模、技术栈、解决的问题以及你在项目中的具体工作。
- 学习与成长:
提及你的学习态度和成长经历。你可以分享你如何持续学习新技术、参与开发社区、阅读技术文章和书籍等。强调你对于前端领域的持续关注和追求,以及你在学习过程中遇到的挑战和如何克服它们。
- 团队合作与沟通能力:
强调你在团队合作中的角色和能力。你可以提及你在过去的团队合作中的贡献,以及你如何与其他团队成员协作、解决问题和有效沟通的能力。
- 总结与展望:
总结你的自我介绍,并表达你对前端领域的热情和对未来的展望。你可以提及你对前端技术的热爱和对新技术的追求,以及你希望在未来的工作中继续学习和成长。
自我介绍需要简明扼要地表达你的关键信息,同时展现你的技术实力、学习能力和团队合作能力。在介绍过程中,注意保持清晰的表达、自信的姿态和积极的态度。记得准备并练习自我介绍,以确保你能在面试中自信地展现自己。
2.URL输入后发生了什么?
当在浏览器中输入URL并按下回车后,以下是详细的步骤:
1.URL解析:
- 提取协议:浏览器解析URL以确定使用的协议(如HTTP、HTTPS)。
- 解析域名:如果URL包含域名,浏览器将解析该域名以获取对应的IP地址。
2.DNS解析:
- 浏览器向本地DNS缓存查找域名对应的IP地址。如果有匹配,则跳至步骤 4。
- 如果本地缓存中没有找到对应的IP地址,则浏览器向操作系统发送DNS查询请求。
- 操作系统将查询发送到配置的DNS服务器,以获取域名对应的IP地址。
- DNS服务器返回IP地址给操作系统,然后操作系统将其返回给浏览器。
3.建立TCP连接:
- 使用获取到的IP地址,浏览器与服务器之间建立TCP连接。
- 这涉及通过三次握手来确保连接的可靠性和完整性。
4.发送HTTP请求:
- 浏览器向服务器发送HTTP请求。
- 请求包括请求方法(如GET、POST)、路径、请求头和可选的请求体。
5.服务器处理请求:
- 服务器接收到请求后,根据请求的路径和其他信息来处理请求。
- 这可能涉及执行服务器端的代码、访问数据库等操作。
6.服务器发送HTTP响应:
- 服务器根据请求的处理结果生成HTTP响应。
- 响应包括响应状态码、响应头和响应体等。
7.接收并解析响应:
- 浏览器接收到服务器发送的HTTP响应。
- 浏览器解析响应头,获取响应的元数据,如状态码、内容类型等。
- 浏览器开始逐步接收和解析响应体,获取实际的响应内容。
8.渲染页面:
- 如果响应是HTML文档,浏览器开始解析HTML,并构建DOM(文档对象模型)树。
- 浏览器解析CSS样式表,构建CSSOM(CSS对象模型)树,并将其与DOM树合并形成渲染树。
- 浏览器执行JavaScript代码,可能会修改DOM树和CSSOM树,以及处理事件等。
- 浏览器根据渲染树计算布局和绘制,将页面的可视化表示生成为位图。
9.显示页面:
- 浏览器将渲染好的页面显示给用户。
- 页面上的文本、图像、链接和其他元素可见并交互。
- 如果页面包含其他资源(如样式表、图像、脚本文件),浏览器将并行下载这些资源并进行相应的处理。
3.DNS解析
1.查找本地DNS缓存:
- 浏览器首先会查找自己的本地DNS缓存,该缓存保存了最近解析的域名和对应的IP地址。
- 如果在本地缓存中找到了对应的域名和IP地址,浏览器会直接使用该IP地址进行连接。
2.发送DNS查询请求:
- 如果在本地缓存中没有找到对应的IP地址,浏览器会向操作系统发送DNS查询请求。
- 操作系统会根据配置的DNS服务器信息,选择一个可用的DNS服务器进行查询。
3.与DNS服务器通信:
- 操作系统将DNS查询请求发送给所选择的DNS服务器,使用UDP或TCP协议进行通信。
- DNS服务器一般由Internet服务提供商(ISP)或其他网络设备提供。
4.迭代或递归查询:
- DNS服务器接收到查询请求后,它可以执行两种类型的查询:迭代查询或递归查询。
- 迭代查询:如果DNS服务器支持迭代查询,它会向其他DNS服务器发送进一步的查询请求,以获取最终的IP地址。这个过程中,DNS服务器从根域名服务器开始,依次向下查询,直到找到对应的IP地址。
- 递归查询:如果DNS服务器不支持迭代查询,它会尝试在自己的缓存中查找域名对应的IP地址。如果找到,则返回结果;否则,它会向其他DNS服务器发送递归查询请求,并等待结果。
5.获取IP地址:
- DNS服务器最终会找到域名对应的IP地址。
- 它将IP地址作为响应发送回操作系统,并由操作系统传递给浏览器。
6.缓存DNS解析结果:
浏览器收到IP地址后,会将该解析结果缓存在本地,以备将来的访问使用。
这样在下一次访问相同的域名时,就可以直接使用缓存的IP地址,而无需进行DNS解析。
通过这些步骤,浏览器能够将域名解析为对应的IP地址,以便建立与服务器的连接。DNS解析过程中可能会涉及多个DNS服务器之间的交互,以找到最终的IP地址。
4.数据包的一层层封装
数据包在网络通信中的传输过程中会被逐层进行封装,每一层都有自己的特定作用和协议。下面是数据包在一层层封装中的介绍:
1.物理层(Physical Layer):
物理层是网络通信的最底层,负责传输比特流,将数据转换为电子信号或光信号,通过物理媒介进行传输,如电缆、光纤等。
2.数据链路层(Data Link Layer):
数据链路层负责将数据包划分为帧(Frame),并添加起始和结束标识,进行透明传输。还负责错误检测和纠正,确保数据传输的可靠性。常见的协议有以太网(Ethernet)、Wi-Fi等。
3.网络层(Network Layer):
网络层负责实现数据包在网络中的路由和转发。它使用IP地址来标识网络中的设备,并通过路由选择算法将数据包从源地址传输到目标地址。常见的协议有IP协议、ICMP协议等。
4.传输层(Transport Layer):
传输层提供端到端的可靠数据传输服务。它负责分段、传输控制、流量控制和错误恢复等功能。常见的协议有TCP协议、UDP协议等。
5.应用层(Application Layer):
应用层是最接近用户的层级,负责提供特定的应用服务。它使用特定的协议实现不同应用的通信需求,如HTTP协议用于Web通信,SMTP协议用于电子邮件等。
数据包在传输过程中,从应用层逐渐被封装到较低层,每一层都添加了自己的头部信息,形成一个新的数据包,然后通过下一层进行传输。在接收端,数据包会逐层解封装,从物理层逐层还原到应用层,使得数据能够被正确地传输和解析。
通过这一层层的封装和解封装,网络通信可以实现不同层级的功能,如可靠传输、路由选择、错误检测和纠正等,从而保证了数据的可靠性和完整性。这种分层的设计使得网络通信可以灵活且高效地工作
5.浏览器渲染过程
1.渲染过程
当浏览器在渲染过程中遇到 JavaScript 脚本或图片等资源时,会对渲染过程进行一些调整和优化。以下是更详细的浏览器渲染过程:
1.解析 HTML:
浏览器从服务器获取 HTML 文件,并逐行解析 HTML 代码。当浏览器遇到 <script>
标签时,加载js会停止解析 HTML,暂停构建 DOM 树,开始下载并解析 JavaScript 脚本。(也就是说,js加载和解析是会阻塞浏览器的渲染过程的,所以建议将script
标签放在body
标签底部。)
2.JavaScript 解析和执行:
当浏览器遇到 JavaScript 脚本时,会下载并解析脚本文件。如果脚本文件较大或需要较长时间执行,浏览器可能会继续解析 HTML 并渲染页面的其余部分,以提高页面的加载速度。一旦脚本解析完成,浏览器会执行脚本中的代码,可能会对 DOM 结构进行修改。
3.解析 CSS:
浏览器解析 CSS 文件,并构建 CSSOM 树。当浏览器遇到 或
- css放在头部加载-不会阻塞html(dom)解析,但会阻塞页面渲染,也就是会阻塞页面加载,但页面内容会等待css的加载完成,即使加载需要很长时间。
- css放在尾部加载-会阻塞紧跟其后的dom解析,但**不会阻塞页面加载(渲染)。**但因为
First Plain
机制,css前已经被解析的页面内容会被渲染。加载到尾部css后,会造成重绘
,造成FOUC(Flash of Unstyled Content)样式闪烁。
4.合并 DOM 和 CSSOM:
浏览器将构建好的 DOM 树和 CSSOM 树合并为渲染树(Render Tree)。渲染树只包含需要显示的元素和样式信息。
5.布局计算
根据渲染树的信息,浏览器进行布局计算,确定每个元素在屏幕中的位置和大小。这个过程称为布局(Layout)或回流(Reflow)。如果 JavaScript 修改了元素的样式或尺寸,布局过程可能会被触发多次。
6.图片加载:
当浏览器遇到 标签时,会开始下载并加载图片。图片加载是异步进行的,当图片下载完成后,浏览器会触发重绘(Paint)阶段,将图片绘制到对应的位置上。
7.绘制和显示:
根据渲染树的信息和布局计算的结果,浏览器使用绘图引擎将网页内容绘制到屏幕上。绘制过程是将文本、图像等元素转换为像素的过程。最后,绘制好的图像会发送给显示设备进行显示。
需要注意的是,JavaScript 和图片加载都会对渲染过程产生影响,可能导致页面加载的延迟。为了提高性能,可以使用一些优化技术,如将 JavaScript 放置在页面底部或使用异步加载脚本的方式,以及对图片进行懒加载或使用适当的图片压缩等。
2.阻塞情况
当文档中没有脚本时,浏览器解析完文档便能触发 DOMContentLoaded 事件;如果文档中包含脚本,则脚本会阻塞文档的解析,而脚本需要等 CSSOM 构建完成才能执行。在任何情况下,DOMContentLoaded 的触发不需要等待图片等其他资源加载完成。
1.头部包含css和js脚本:
- 若js在前,js会阻塞dom解析,先执行
script
内容,不会等待css加载。 - 若js在后,因为有css,css加载会阻塞js加载。js需要等待css加载完成才能够执行。因为js可能需要获取到css中设置好的样式属性,比如说宽度高度等。所以,后面的dom就不能解析了。所以,这类情况会使得css加载阻塞js加载,js阻塞dom解析,从而造成css间接阻塞dom解析。
2.尾部包含css和js脚本:
- 若js在前,无需等待css加载,但是DOMContentLoaded需要等待CSS加载。
- 若css在前,js需要等待CSS的加载,但是DOMContentLoaded需要等待CSS的加载。
6.TCP
TCP(Transmission Control Protocol)是一种面向连接的、可靠的传输层协议,用于在网络上可靠地传输数据。它是互联网协议套件(TCP/IP)中的重要组成部分,广泛应用于各种网络通信场景,如网页浏览、文件传输、电子邮件等。
TCP 提供以下主要特性和机制:
1.可靠性:
TCP 提供可靠的数据传输机制,通过使用确认、重传和超时机制来确保数据的可靠交付。它会跟踪每个数据包的状态,并确保数据包按正确的顺序到达目的地。
2.面向连接:
在进行数据传输之前,TCP 需要在通信的两端建立连接。连接建立过程中,双方会进行握手,以建立起可靠的通信通道。数据传输完成后,连接会被正常关闭,释放资源。
3.流式传输:
TCP 提供的是面向流的传输,而不是面向报文的传输。它将数据视为字节流,将数据分割成多个数据段(Segment)进行传输。这使得 TCP 可以根据网络状况和接收端的接收能力进行动态的拆分和组装。
4.拥塞控制:
TCP 通过拥塞控制机制来避免网络拥塞。它会根据网络的拥塞程度和传输速率进行自适应的调整,保证网络的稳定性和公平性。
5.双向通信:
TCP 提供全双工的双向通信能力,允许通信双方同时发送和接收数据。它使用序列号和确认机制来保证双方数据的可靠传输和正确接收。
TCP 在通信过程中使用三次握手建立连接,四次挥手关闭连接。具体流程如下:
1.建立连接(三次握手):
-
第一步:客户端向服务器发送连接请求(SYN)报文。
-
第二步:服务器收到请求后,回复客户端一个确认(ACK)报文和自己的连接请求(SYN)报文。
-
第三步:客户端收到服务器的回复后,再发送一个确认(ACK)报文,完成连接建立。
2.数据传输:
连接建立后,双方可以进行数据的传输,数据会被分割成多个数据段进行传输。
3.关闭连接(四次挥手):
-
第一步:一方发送关闭连接请求(FIN)报文。
-
第二步:另一方收到请求后,发送确认(ACK)报文,进入半关闭状态。
-
第三步:数据传输完成后,另一方发送关闭连接请求(FIN)报文。
-
第四步:收到请求的一方发送确认(ACK)报文,双方关闭连接。
TCP 提供了可靠的数据传输和流量控制的机制,适用于对数据传输可靠性要求较高的应用场景。然而,由于 TCP 建立连接和维护状态的开销较大,它的传输效率相对较低,对实时性要求较高的应用场景可能不适用。在网络通信中,TCP 与 UDP(User Datagram Protocol)是两种最常用的传输协议,各自具有不同的特点和应用场景。
7.tcp报文有哪些内容(说了一些字段,和为了实现可靠传输的一些字段)
1.TCP报文介绍
TCP 报文包含以下几个重要的字段:
1.源端口号(Source Port)和目标端口号(Destination Port):
用于标识通信的源端口和目标端口,以确定发送和接收的应用程序。
2.序列号(Sequence Number):
用于标识 TCP 报文中数据部分的字节流的顺序。通过序列号,接收端可以按正确的顺序重新组装数据。
3.确认号(Acknowledgment Number):
用于确认收到的数据的序列号。接收端在收到数据后,会向发送端发送一个带有确认号的确认报文,表示已经成功接收到该序列号之前的数据。
4.数据偏移(Data Offset):
指示 TCP 报文头部的长度,以字节为单位。
5.控制位(Control Flags):
TCP 报文的控制字段,包含以下几个标志位:
-
SYN(Synchronize):用于建立连接。
-
ACK(Acknowledgment):用于确认收到的数据。
-
FIN(Finish):用于关闭连接。
-
RST(Reset):用于重置连接。
-
PSH(Push):用于提前传送数据,不进行缓存。
-
URG(Urgent):用于指示报文中存在紧急数据。
-
窗口大小(Window Size):用于流量控制,指示发送端可以发送的未确认数据的最大量。
-
校验和(Checksum):用于检测报文是否损坏或被篡改。
-
紧急指针(Urgent Pointer):当 URG 标志位被设置时,指示紧急数据的边界。
-
选项(Options):可选字段,用于扩展 TCP 的功能和控制。
2.可靠传输保证
这些TCP 报文中的序列号和确认号字段用于保证数据的可靠传输。发送方通过序列号对发送的数据进行编号,接收方通过确认号告知发送方已经成功接收到哪些数据。如果发送方没有收到确认报文,会认为数据丢失,会根据超时和重传策略重新发送数据。这种确认和重传机制可以保证数据的正确性和完整性。
除此之外,还通过一下方式,保证可靠传输
1.确认和重传机制:
接收端收到数据后,会发送带有确认号的确认报文,告知发送端数据已经接收到。如果发送端没有收到确认报文,会认为数据丢失,会根据超时和重传策略重新发送数据。
2.滑动窗口机制:
接收端会告知发送端当前可接收的窗口大小,发送端根据窗口大小进行数据发送。这样可以控制发送端的发送速率,避免接收端无法及时处理过多的数据。
3.丢失重传机制:
如果发送端没有收到确认报文,会认为数据丢失,会根据超时和重传策略重新发送数据。
4.拥塞控制:
拥塞控制是 TCP 中用于保证可靠传输的重要机制之一。它主要用于解决网络拥塞的问题,确保在网络负载较重的情况下,TCP 数据的传输仍然能够进行并且不会造成严重的网络性能问题。拥塞控制通过动态调整发送端的发送速率,以适应当前网络的拥塞程度。它包括慢启动、拥塞避免、快速重传和快速恢复等机制,通过调整发送窗口大小和发送速率来控制数据的发送量,避免网络拥塞和数据丢失。
3.拥塞控制补充介绍
拥塞控制是 TCP 中用于保证可靠传输的重要机制之一。它主要用于解决网络拥塞的问题,确保在网络拥塞的情况下,TCP 数据的传输仍然能够进行并且不会造成严重的网络性能问题。
拥塞控制的主要目标是通过动态调整发送端的发送速率,以适应当前网络的拥塞程度,避免过多的数据注入到网络中,造成网络拥塞和数据丢失。
拥塞控制的机制主要包括以下几个方面:
1.慢启动:
发送端在连接刚建立时,以较慢的速度逐渐增加发送的数据量,以便先探测网络的拥塞情况。通过逐渐增加发送速率,避免在网络负载较重的情况下一下子发送大量数据导致拥塞。
2.拥塞避免:
一旦连接建立后,发送端会根据网络的拥塞程度调整发送速率。它通过维护一个拥塞窗口大小,动态调整发送端的窗口大小来控制发送速率。当网络负载较重时,会减小拥塞窗口大小,降低发送速率;当网络负载较轻时,会逐渐增大拥塞窗口大小,提高发送速率。
3.快速重传和快速恢复:
当发送端收到连续的冗余的确认报文时,它会认为网络中出现了拥塞,并采取快速重传和快速恢复的机制。快速重传即在接收到冗余的确认报文后,立即重传对应的丢失的数据段,而不等待超时。快速恢复即在进行重传后,不将拥塞窗口大小减半,而是将其设置为当前窗口大小的一半。
通过以上机制,TCP 可以动态地根据网络的拥塞程度来控制发送速率,避免网络拥塞和数据丢失,从而保证数据的可靠传输。拥塞控制是 TCP 可靠性的重要保障之一,它与序列号和确认号机制相互配合,共同保证了 TCP 数据的可靠传输。
8.怎么实现可靠传输的?
TCP 通过多个方面来保证可靠传输,包括序列号与确认号机制、滑动窗口机制、超时重传机制、拥塞控制机制等。下面将详细介绍每个方面的作用。
1.序列号与确认号机制:
-
序列号:TCP 在发送数据时对每个数据段进行序列号编号,确保接收方按照正确的顺序重组数据。
-
确认号:接收方通过确认号告知发送方已成功接收到哪些数据段,发送方可以根据确认号判断哪些数据需要重传。
2.滑动窗口机制:
-
发送窗口:发送方维护一个发送窗口,指示可以连续发送的数据段数量。发送方只发送在发送窗口范围内的数据段。
-
接收窗口:接收方维护一个接收窗口,指示可以接收的数据段数量。接收方只接收在接收窗口范围内的数据段。
3.超时重传机制:
-
发送方设置一个定时器,如果在设定的超时时间内未收到对应的确认报文,则认为数据段丢失,需要重新发送。
-
接收方在接收到乱序的数据段时,通过发送重复的确认报文告知发送方需要重新发送丢失的数据段。
4.拥塞控制机制:
-
慢启动:在连接刚建立时,发送方以较慢的速度逐渐增加发送的数据量,以便先探测网络的拥塞情况。
-
拥塞避免:一旦连接建立后,发送方会根据网络的拥塞程度调整发送速率。它通过维护一个拥塞窗口大小,动态调整发送端的窗口大小来控制发送速率。
-
快速重传和快速恢复:当发送方收到连续的冗余确认报文时,它会认为网络中出现了拥塞,并采取快速重传和快速恢复的机制。
通过以上方面的机制,TCP 实现了可靠传输:
- 序列号与确认号机制确保数据的正确性和完整性。
- 滑动窗口机制控制发送方和接收方之间的数据流,提高传输效率。
- 超时重传机制处理数据丢失的情况,确保数据的可靠传输。
- 拥塞控制机制通过动态调整发送速率,避免网络拥塞和数据丢失,保持传输的稳定性和网络性能的平衡。
这些机制相互配合,共同保障了 TCP 数据的可靠传输,并适应不同网络环境下的变化和拥塞情况。
9.http版本更新变化
HTTP(Hypertext Transfer Protocol)是一种用于传输超文本的协议,它定义了客户端和服务器之间的通信规则。HTTP 的版本随着时间的推移发生了多次更新和演进,下面将介绍主要的 HTTP 版本及其更新变化、新特性、功能和优势。
1.HTTP/1.0:
- 发布于1996年,是最早的 HTTP 版本之一。
- 特点:
- 基于传统的请求-响应模型,每个请求都需要单独建立和关闭连接。
- 没有持久连接,每个请求都需要重新建立 TCP 连接,导致性能低下。
- 优势:
- 简单易懂,易于实现。
2.HTTP/1.1:
- 发布于1999年,是当前最广泛使用的 HTTP 版本之一。
- 特点:
- 引入了持久连接(Persistent Connections)和管道化(Pipelining),允许多个请求和响应复用同一个连接,提高性能。
- 引入了分块传输编码(Chunked Transfer Encoding),允许将消息分成多个块进行传输,提高传输效率。
- 引入了缓存控制、压缩传输等新特性,提升了性能和效率。
- 优势:
- 支持持久连接,减少了连接建立和关闭的开销,提高了性能。
- 引入了更多的优化特性,提高了传输效率和用户体验。
3.HTTP/2:
- 发布于2015年,是对 HTTP/1.1 的重大升级。
- 特点:
- 引入了二进制协议,将消息分帧传输,提高传输效率和可靠性。
- 支持多路复用(Multiplexing),允许在同一个连接上同时传输多个请求和响应。
- 引入了服务器推送(Server Push),服务器可以主动向客户端推送资源,减少请求延迟。
- 支持头部压缩(Header Compression),减少了头部信息的大小,节省带宽。
- 优势:
- 提高了性能和效率,减少了延迟,提高了页面加载速度。
- 支持多路复用和服务器推送,减少了网络请求的次数,提高了并发性能。
- 头部压缩减少了数据传输量,节省了带宽消耗。
4.HTTP/3:
- 基于 UDP 协议的 QUIC(Quick UDP Internet Connections)协议。
- 特点:
- 使用了 UDP 作为传输层协议,提供更低的延迟和更好的容错性。
- 支持多路复用、流量控制、拥塞控制等特性。
- 引入了 TLS 1.3 加密,提供更高的安全性。
- 优势:
- 提供更低的延迟和更好的性能,特别是在高丢包率和高延迟网络环境下。
- 改善了传输的可靠性和安全性。
总体而言,随着 HTTP 版本的更新,每个版本都带来了新的特性、功能和优势,以提升网络传输的效率、性能和安全性。从 HTTP/1.0 到 HTTP/3,协议的发展主要关注持久连接、传输效率、并发性能、延迟优化以及安全性等方面的改进,以满足日益增长的网络需求和用户期望。选择适合的 HTTP 版本取决于应用场景和需求,以获得最佳的性能和用户体验。
10.https介绍,与http区别
1.数据传输的加密:
-
HTTP传输的数据是明文的,容易被拦截和窃听。HTTPS使用SSL/TLS协议对数据进行加密,确保数据在传输过程中的机密性和完整性。
-
HTTPS使用公钥加密和私钥解密的方式,使得数据只能被预期的接收方解密。
2.安全性和身份验证:
-
HTTPS通过数字证书来验证服务器的身份,确保用户与正确的服务器进行通信,防止中间人攻击。
-
数字证书由受信任的第三方机构(CA)签发,证明服务器是可信的,并提供加密密钥。
3.默认端口:
- HTTP默认使用端口80进行通信,而HTTPS默认使用端口443。
这使得服务器能够识别和处理不同协议的请求。
4.SEO和搜索引擎排名:
-
采用HTTPS可以提升网站在搜索引擎中的排名和可信度。
-
搜索引擎通常更倾向于显示HTTPS网站的结果,认为其更安全可靠。
5.浏览器行为:
-
当访问使用HTTPS的网站时,浏览器会显示一个锁形状的图标,指示连接是安全的。
-
当访问使用HTTP的网站时,浏览器通常不显示任何特殊标志,表示连接是不安全的。
6.性能影响:
-
HTTPS会对服务器和客户端的性能产生一定的影响,因为加密和解密数据需要额外的计算资源。
-
但随着硬件和网络的改进,这种性能影响已经大大降低。
总的来说,HTTPS相较于HTTP提供了更高的安全性,确保数据在传输过程中的机密性和完整性。在需要保护用户隐私和敏感信息的场景下,特别是涉及用户登录、支付等操作时,使用HTTPS是必要的。同时,采用HTTPS也有助于提升网站的可信度和搜索引擎排名。
11.https的tls层握手的过程
HTTPS的TLS握手过程如下:
1.(ClientHello)客户端发送协议版本和加密算法列表:
客户端向服务器发送一个ClientHello消息,其中包含支持的TLS协议版本和加密算法列表。
2.(ServerHello)服务器回应:
服务器接收到ClientHello消息后,选择与客户端最匹配的TLS版本和加密算法。服务器发送一个ServerHello消息作为回应,包含选择的协议版本和加密算法。
3.服务器发送证书:
服务器将自己的数字证书发送给客户端,证书中包含了服务器的公钥。该证书由受信任的第三方机构(CA)签发,用于验证服务器的身份。
4.客户端验证证书:
客户端收到服务器的证书后,首先验证证书的有效性和合法性。客户端检查证书的签名、有效期等信息,以确保服务器的身份和证书的真实性。
5.客户端生成随机数:
客户端生成一个随机数,作为后续加密通信的密钥(pre-master secret)。
6.客户端加密密钥的协商:
客户端使用服务器的公钥对随机数进行加密,并将加密后的密文发送给服务器,用于协商加密通信所需的对称密钥。
7.服务器解密密钥:
服务器收到客户端发送的密文后,使用自己的私钥进行解密,获取客户端生成的对称密钥。
8.服务器和客户端生成会话密钥:
服务器和客户端使用双方生成的随机数和对称密钥,分别计算出用于加密和解密的会话密钥。
9.安全通信建立:
握手过程完成后,服务器和客户端使用会话密钥对后续的通信进行加密和解密,确保数据在传输过程中的机密性和完整性。
整个握手过程中,TLS使用公钥加密和非对称加密算法来确保通信的安全性。服务器的数字证书用于验证服务器的身份,而对称密钥的协商使得加密通信的效率更高。
值得注意的是,TLS握手过程仅在连接建立时执行一次,后续的通信会使用建立好的会话密钥进行加密。这样可以提供更高的效率,并减少加密和解密的开销。
12.http缓存,怎么实现控制的各自的字段
浏览器缓存策略可以分为强缓存和协商缓存。
-
强缓存是指客户端向服务器发送的请求中包含了强缓存控制头,如“Cache-Control”和“Expires”等,服务器根据这些头中的信息来决定是否使用缓存数据。如果请求中包含了强缓存控制头,服务器将直接使用缓存数据,而不需要进行协商。
-
协商缓存是指客户端向服务器发送的请求中不包含强缓存控制头,而是通过缓存协商的方式,由客户端和服务器之间进行协商,来确定是否使用缓存数据和缓存的有效期等信息。在协商缓存中,客户端会向服务器发送一个缓存请求,服务器根据请求中的信息来决定是否使用缓存数据,如果使用缓存数据,服务器也会返回一个缓存版本号给客户端,以便客户端判断是否需要更新缓存数据。
相比强缓存,协商缓存需要更多的网络交互和服务器资源,但是可以更好地利用缓存,避免缓存数据被重复使用,从而提高网站的性能和响应速度。
在实际应用中,强缓存和协商缓存可以结合使用,以实现更好的缓存控制效果。同时,需要注意缓存数据的安全和完整性,以避免缓存数据被篡改或失效。
强缓存:
服务器通知浏览器一个缓存时间,在缓存时间内,下次请求,直接用缓存,不在时间内,执行比较缓存策略。
-
强缓存命中直接读取浏览器本地资源,在network中显示的是from memory 或者 from disk,如在微信小程序开发中,命中图片缓存,显示from disk。
-
控制强缓存的字段有:Cache-Control(http1.1)和Expires(http1.0)
-
协商缓存,让客户端与服务器之间能实现缓存文件是否更新的验证、提升缓存的复用率,将缓存信息中的Etag和Last-Modified
通过请求发送给服务器,由服务器校验,返回304状态码时,浏览器直接使用缓存。 -
Cache-control是一个相对时间,用以表达自上次请求正确的资源之后的多少秒的时间段内缓存有效。
-
Expires是一个绝对时间。用以表达在这个时间点之前发起请求可以直接从浏览器中读取数据,而无需发起请求
-
Cache-Control的优先级比Expires的优先级高。前者的出现是为了解决Expires在浏览器时间被手动更改导致缓存判断错误的问题。
如果同时存在则使用Cache-control。
强缓存-expires:
- 优势特点
1、HTTP 1.0 产物,可以在HTTP 1.0和1.1中使用,简单易用。
2、以时刻标识失效时间。 - 劣势问题
1、时间是由服务器发送的(UTC),如果服务器时间和客户端时间存在不一致,可能会出现问题。
2、存在版本问题,到期之前的修改客户端是不可知的。
HTTP缓存都是从第二次请求开始的:
强缓存-cache-conche:
- 优势特点
1、HTTP 1.1 产物,以时间间隔标识失效时间,解决了Expires服务器和客户端相对时间的问题。
2、比Expires多了很多选项设置。 - 劣势问题
1、存在版本问题,到期之前的修改客户端是不可知的。
协商缓存:
- 协商缓存的状态码由服务器决策返回200或者304
- 当浏览器的强缓存失效的时候或者请求头中设置了不走强缓存,并且在请求头中设置了If-Modified-Since 或者 If-None-Match 的时候,会将这两个属性值到服务端去验证是否命中协商缓存,如果命中了协商缓存,会返回 304 状态,加载浏览器缓存,并且响应头会设置 Last-Modified 或者 ETag 属性。
- 对比缓存在请求数上和没有缓存是一致的,但如果是 304 的话,返回的仅仅是一个状态码而已,并没有实际的文件内容,因此 在响应体体积上的节省是它的优化点。
- 协商缓存有 2 组字段(不是两个),控制协商缓存的字段有:Last-Modified/If-Modified-since(http1.0)和Etag/If-None-match(http1.1)
- Last-Modified/If-Modified-since表示的是服务器的资源最后一次修改的时间;Etag/If-None-match表示的是服务器资源的唯一标
识,只要资源变化,Etag就会重新生成。 - Etag/If-None-match的优先级比Last-Modified/If-Modified-since高。
协商缓存-协商缓存-Last-Modified/If-Modified-since
- 1.服务器通过 Last-Modified 字段告知客户端,资源最后一次被修改的时间,例如 Last-Modified: Mon, 10 Nov 2018 09:10:11 GMT
- 2.浏览器将这个值和内容一起记录在缓存数据库中。
- 3.下一次请求相同资源时时,浏览器从自己的缓存中找出“不确定是否过期的”缓存。因此在请求头中将上次的 Last-Modified 的值写入到请求头的 If-Modified-Since 字段
- 4.服务器会将 If-Modified-Since 的值与 Last-Modified 字段进行对比。如果相等,则表示未修改,响应 304;反之,则表示修改了,响应 200 状态码,并返回数据。
优势特点
- 1、不存在版本问题,每次请求都会去服务器进行校验。服务器对比最后修改时间如果相同则返回304,不同返回200以及资源内容。
劣势问题
1、只要资源修改,无论内容是否发生实质性的变化,都会将该资源返回客户端。例如周期性重写,这种情况下该资源包含的数据实际上一样的。 - 2、以时刻作为标识,无法识别一秒内进行多次修改的情况。 如果资源更新的速度是秒以下单位,那么该缓存是不能被使用的,因为它的时间单位最低是秒。
- 3、某些服务器不能精确的得到文件的最后修改时间。
- 4.、如果文件是通过服务器动态生成的,那么该方法的更新时间永远是生成的时间,尽管文件可能没有变化,所以起不到缓存的作用。
协商缓存-Etag/If-None-match
为了解决上述问题,出现了一组新的字段 Etag 和 If-None-Match
Etag 存储的是文件的特殊标识(一般都是 hash 生成的),服务器存储着文件的 Etag 字段。之后的流程和 Last-Modified 一致,只是 Last-Modified 字段和它所表示的更新时间改变成了 Etag 字段和它所表示的文件 hash,把 If-Modified-Since 变成了 If-None-Match。服务器同样进行比较,命中返回 304, 不命中返回新资源和 200。
浏览器在发起请求时,服务器返回在Response header中返回请求资源的唯一标识。在下一次请求时,会将上一次返回的Etag值赋值给If-No-Matched并添加在Request Header中。服务器将浏览器传来的if-no-matched跟自己的本地的资源的ETag做对比,如果匹配,则返回304通知浏览器读取本地缓存,否则返回200和更新后的资源。
Etag 的优先级高于 Last-Modified。
优势特点
- 1、可以更加精确的判断资源是否被修改,可以识别一秒内多次修改的情况。
- 2、不存在版本问题,每次请求都回去服务器进行校验。
劣势问题 - 1、计算ETag值需要性能损耗。
- 2、分布式服务器存储的情况下,计算ETag的算法如果不一样,会导致浏览器从一台服务器上获得页面内容后到另外一台服务器上进行验证时现ETag不匹配的情况。
第一次请求资源时,服务器返回资源,并在response header中回传资源的缓存策略;
第二次请求时,浏览器判断这些请求参数,击中强缓存就直接200,否则就把请求参数加到request header头中传给服务器,看是否击中协商缓存,击中则返回304,否则服务器会返回新的资源。
13.事件处理机制,事件捕获,目标,冒泡,可以改变事件在哪个阶段执行吗?怎么改?
https://www.freecodecamp.org/chinese/news/event-propagation-event-bubbling-event-catching-beginners-guide-2/
这篇文章介绍的巨特么好,很清晰。
1.事件捕获与事件冒泡介绍
JavaScript中的事件捕获和事件冒泡是两种不同的事件传播方式,它们之间有联系但又有区别。下面我将详细介绍它们的概念、联系、区别和典型场景:
1.事件捕获(Event Capturing):
-
事件捕获是指事件从最外层的祖先元素开始,逐级向下传播到目标元素的过程。
-
在事件捕获阶段,事件首先被祖先元素捕获,然后依次传递给子孙元素,直到达到目标元素。
-
在事件捕获阶段,事件处理程序首先被位于目标元素之上的祖先元素处理。
-
事件捕获阶段通过使用addEventListener方法的第三个参数设置为true来启用。
2.事件冒泡(Event Bubbling):
-
事件冒泡是指事件从目标元素开始,逐级向上传播到最外层的祖先元素的过程。
-
在事件冒泡阶段,事件首先由目标元素处理,然后逐级向上传递给祖先元素。
-
在事件冒泡阶段,事件处理程序会依次由目标元素的父元素、父元素的父元素等处理,直到达到最外层的祖先元素。
-
事件冒泡阶段是默认的事件处理方式,不需要额外设置。
3.联系:
-
事件捕获和事件冒泡都是事件传播的方式,都是从某个元素开始,经过一系列的传递,最终到达目标元素或最外层的祖先元素。
-
事件捕获和事件冒泡都是DOM事件模型的组成部分,这个模型描述了事件在DOM结构中传播和处理的过程。
4.区别:
-
事件捕获是自顶向下的传播方式,而事件冒泡是自底向上的传播方式。
-
事件捕获阶段的事件处理程序首先被位于目标元素之上的祖先元素处
理,-而事件冒泡阶段的事件处理程序首先由目标元素处理。 -
默认情况下,事件处理采用事件冒泡机制,如果需要使用事件捕获,需要显式地指定。
-
在事件处理程序中,可以通过event.stopPropagation()方法阻止事件继续传播,即停止事件捕获或事件冒泡。
5.典型场景:
-
事件捕获和事件冒泡可以用于实现事件委托,即将事件处理程序绑定到父元素上,利用事件的传播机制来处理子元素触发的事件。
-
事件捕获通常用于特定需求的事件处理,例如在特定层级的父元素上捕获事件,进行某些前置操作或拦截处理。
-
事件冒泡通常用于一般的事件处理需求,例如点击按钮、链接或其他交互元素时触发的事件处理。
综上所述,事件捕获和事件冒泡是JavaScript中事件传播的两种方式,通过它们可以灵活地处理不同的事件需求,并实现事件的委托、前置处理等功能。
2.结合代码介绍
事件捕获的示例:
<div id="outer">
<div id="inner">
Click me!
</div>
</div>
javascript
Copy code
function handleEvent(event) {
console.log('Event Capturing:', event.target.id);
}
const outer = document.getElementById('outer');
const inner = document.getElementById('inner');
outer.addEventListener('click', handleEvent, true);
inner.addEventListener('click', handleEvent, true);
在上面的示例中,我们通过将addEventListener方法的第三个参数设置为true来启用事件捕获。当我们在内部元素inner上点击时,事件会首先在外部元素outer上进行捕获,然后传播到内部元素inner。输出结果将是:
Event Capturing: outer
Event Capturing: inner
事件冒泡的示例:
<div id="outer">
<div id="inner">
Click me!
</div>
</div>
javascript
Copy code
function handleEvent(event) {
console.log('Event Bubbling:', event.target.id);
}
const outer = document.getElementById('outer');
const inner = document.getElementById('inner');
outer.addEventListener('click', handleEvent);
inner.addEventListener('click', handleEvent);
在上面的示例中,我们使用默认的事件冒泡机制,即不显式指定事件处理阶段。当我们在内部元素inner上点击时,事件会在内部元素上处理,然后逐级向上传播到外部元素outer。输出结果将是:
Event Bubbling: inner
Event Bubbling: outer
通过这些示例,您可以清晰地看到事件捕获和事件冒泡在代码中的应用,以及它们之间的区别。
3.改变事件执行阶段
1.javascript中改变
可以改变事件在哪个阶段执行的方式,可以通过addEventListener方法的第三个参数进行设置。该参数是一个布尔值,用于指定事件的处理方式:
如果将第三个参数设置为true,则事件在捕获阶段执行。
如果将第三个参数设置为false或省略不写,默认为false,则事件在冒泡阶段执行。
例如,要在事件捕获阶段执行事件处理程序,可以这样设置:
element.addEventListener('click', handleEvent, true);
要在事件冒泡阶段执行事件处理程序,可以这样设置:
element.addEventListener('click', handleEvent, false); // 或省略第三个参数,默认为false
2.react中改变
在React中,事件处理函数 只能在事件冒泡阶段执行,无法直接在事件捕获阶段执行。React将事件处理函数统一绑定在 组件的根元素 ,(react16及以下版本,会一直冒泡到html根元素上),并在事件冒泡阶段处理所有的事件。
这是由React的事件系统决定的。在React中,事件处理函数会被封装为合成事件(SyntheticEvent),并统一委托给根元素进行处理。React的事件系统是基于浏览器原生事件机制实现的,但它提供了更高级的抽象和一致性,以便跨浏览器和跨平台运行。
react中,当按钮被点击,按钮上的事件处理器首先被调用,然后父元素的事件处理器才被调用。
这个事件流顺序从未逆转过(即捕获阶段从未被触发)。
这是因为 React 的合成事件仅利用了冒泡阶段(目标阶段也包含在内)。如果只是为了触发event.target所在的地方(如此处按钮的例子),这样的设置是合理的。
- 记住 React 在合成事件中只是
仿造
JavaScript 原生的冒泡和捕获阶段。
1.react中改变默认默认冒泡事件为事件捕获:
合成事件 并不默认专注在捕获阶段,除非特意设置。若需要触发捕获阶段,可将父元素div的事件监听器由onClick修改成onClickCapture:
import React, { Component } from "react";
class Molly extends Component {
...
render() {
return (
<div className="im-a-parent" onClickCapture={this.handleCallFamilyToEat}>
<button className="im-a-child" onClick={this.handleCookEggs}>Cook Eggs</button>
<button className="im-a-child" onClick={this.handleMakeRice}>Make Rice</button>
<button className="im-a-child" onClick={this.handleMixChicken}>Mix Chicken</button>
</div>
);
}
}
export default Molly;
注意在这个例子中捕获阶段代替了冒泡阶段被触发。
2.react中阻止冒泡:
假设你在同一个元素上绑定了多个事件。此时用event.stopPropagation()
肯定可以阻止父元素事件的触发,但是该元素上的其他事件还是会触发。
为了防止其他事件触发,可以使用 event.stopImmediatePropagation()
。这个方法可以阻止父元素和该元素上其他事件的触发。
3.总结
需要注意的是,在大多数情况下,您无需显式地设置事件处理函数在事件捕获阶段执行,因为默认情况下它们会在事件冒泡阶段执行,并且满足大多数开发需求。
14.事件委托,怎么拿到具体的子元素
在JavaScript中,事件委托是一种将事件处理程序附加到其父元素上,以代理处理其子元素事件的技术。当子元素触发事件时,事件会冒泡到父元素,并通过事件对象的target属性指向具体的子元素。
1.可以通过检查event.target属性来获取触发事件的具体子元素。
下面是一个示例:
// 获取父元素
var parentElement = document.getElementById('parentElement');
// 添加事件监听器到父元素
parentElement.addEventListener('click', function(event) {
// 检查事件目标的子元素
var targetElement = event.target;
// 执行具体子元素的处理逻辑
if (targetElement.classList.contains('childElement')) {
// 具体子元素被点击
console.log('具体子元素被点击:', targetElement);
}
});
在上面的示例中,父元素 parentElement
上附加了一个点击事件的事件监听器。当点击父元素的子元素时,事件会冒泡到父元素,并通过event.target
属性获取到具体的子元素。然后,你可以根据需要检查子元素的属性、类名等来执行相应的逻辑。
通过这种方式,你可以使用一个事件处理程序来处理多个子元素的事件,而不需要为每个子元素都添加单独的事件监听器。这种技术可以提高性能和代码的可维护性。
2.targetElement.classList.contains()介绍:
classList 是元素对象的属性,它返回一个 DOMTokenList 对象,表示元素的类列表。classList 对象提供了一系列方法来操作元素的类名。
contains 是 classList 对象的方法之一,用于检查指定的类名是否存在于元素的类列表中。它返回一个布尔值,如果指定的类名存在于元素的类列表中,则返回 true,否则返回 false。
在上述示例中,targetElement.classList.contains(‘childElement’) 用于检查 targetElement 是否具有名为 ‘childElement’ 的类。如果具有该类名,则表示该子元素被点击,并执行相应的逻辑。
15.BFC
BFC(块级格式化上下文)是一种在 HTML 页面中控制块级元素布局和定位的机制。BFC 可以看作是一个独立的布局环境,其中的元素按照一定的规则进行排列和渲染。
BFC 的主要作用是解决普通流中的一些布局问题,例如清除浮动、防止边距重叠等。
1.BFC 的一些使用场景:
1.清除浮动:
当父元素包含浮动元素时,会导致父元素的高度塌陷,这时可以使用 BFC 来清除浮动。通过在父元素上创建一个 BFC,可以使父元素包含浮动元素的高度正常计算,而不塌陷。
2.防止边距重叠:
在普通流中,相邻的两个块级元素的上下边距可能会发生重叠。但是在不同的 BFC 中,块级元素的边距是相互独立的,不会重叠。
3.创建独立的上下文:
BFC 可以创建一个独立的布局上下文,使得内部的元素不受外部影响,外部也无法影响内部。这在一些特殊的布局需求中非常有用,例如使用 overflow: auto 或 overflow: hidden 创建的 BFC。
## 2.实现 BFC 方法:
1.使用浮动(float):
设置元素的浮动属性为 left 或 right 可以创建一个 BFC。
2.使用绝对定位(position: absolute 或 position: fixed):
设置元素的定位属性为绝对定位或固定定位可以创建一个 BFC。
3.使用块级格式化上下文的 CSS 属性(display: inline-block、display: table-cell、display: flex 等):
设置元素的 display 属性为某些值可以创建一个 BFC。
4.使用溢出(overflow: hidden、overflow: auto):
设置元素的溢出属性为 hidden 或 auto 可以创建一个 BFC。
这些方法中的任何一个都可以将元素变成一个 BFC,从而影响其内部布局和外部元素的交互。
16.tihs指向
- 普通函数中this指向的是调用它的对象,如果作为构造函数,它指向创建的对象实例;
- 箭头函数不会创建自己的this, 所以它没有自己的this,它只会在自己作用域的上一层继承this。所以箭头函数中this的指向在它在定义时已经确定了,之后不会改变。
1.全局上下文:
全局上下文:当在全局上下文中使用 this 时,它指向全局对象(在浏览器中是 window 对象,在 Node.js 中是 global 对象)。
console.log(this); // 在全局作用域中输出全局对象(window 或 global)
2.函数调用:
当函数作为独立函数调用时,this 指向全局对象。这是因为函数没有被绑定到任何对象上。
function myFunction() {
console.log(this);
}
myFunction(); // 在浏览器中输出全局对象(window),在 Node.js 中输出全局对象(global)
3.方法调用:
当函数作为对象的方法调用时,this 指向调用该方法的对象。
const obj = {
name: 'John',
sayHello: function() {
console.log(`Hello, ${this.name}!`);
}
};
obj.sayHello(); // 输出 "Hello, John!"
4.构造函数调用:
当函数作为构造函数使用 new 关键字调用时,this 指向新创建的对象实例。
function Person(name) {
this.name = name;
}
const john = new Person('John');
console.log(john.name); // 输出 "John"
5.箭头函数:
箭头函数的 this 是在定义时继承自外部作用域,并且在整个箭头函数生命周期内保持不变。
const obj = {
name: 'John',
sayHello: () => {
console.log(`Hello, ${this.name}!`);
}
};
obj.sayHello(); // 输出 "Hello, undefined!"
在箭头函数中,this 的值是继承自外部作用域,因此在上述例子中,箭头函数没有自己的 this,导致 this.name 为 undefined。
首先,这里有个问题是js分严格模式和非严格模式,严格模式和非严格模式下this指向稍微有点区别,主要就是全局作用域中普通函数中的this指向问题,严格模式下是指向undefined的,非严格模式下是指向window。
二面
1.自我介绍
2.大二就去实习了,你感觉实习都有哪些收获?谈到最大的收获就是建立起了前端的知识框架,增长了视野
3.说说你理解的前端
4.前端工程化,webpack的作用,如何处理兼容性
前端工程化是一种通过使用工具和流程优化前端开发的方法。它包括许多方面,如模块化开发、自动化构建、代码规范、性能优化、自动化测试等。
Webpack 是一个常用的前端工程化工具,主要用于模块打包。它可以将项目中的各种资源(JavaScript、CSS、图片等)视为模块,并通过配置将它们打包成适合部署到生产环境的静态文件。
1.Webpack 的主要作用包括:
-
模块打包:Webpack 可以识别各种类型的模块依赖关系,并将它们打包成一个或多个输出文件。这样可以减少网络请求次数,提高页面加载性能。
-
代码转换和优化:Webpack 支持使用各种插件和加载器来处理和转换不同类型的文件,如将 ES6/ESNext 转换为 ES5 以提供浏览器兼容性,对 CSS 进行预处理、压缩和自动添加浏览器前缀等。
-
资源管理:Webpack 可以管理和优化各种资源,如图片、字体等。它可以根据需要对资源进行压缩、合并、懒加载等处理,以提高应用的性能。
-
代码分割:Webpack 支持代码分割,将代码分成多个块,按需加载。这样可以实现按需加载和异步加载,提高页面的首屏加载速度。
处理兼容性是前端开发中重要的一环。
2.一些处理兼容性的常用方法:
-
使用 CSS Reset 或 Normalize.css:它们可以重置或规范化不同浏览器之间的默认样式差异,以确保一致的展现效果。
-
使用 Autoprefixer:Autoprefixer 是一个 PostCSS 插件,可以根据配置的目标浏览器版本自动添加适当的浏览器前缀。
-
使用 Babel:Babel 是一个 JavaScript 编译工具,可以将新版本的 JavaScript 代码转换为旧版本的代码,以提供浏览器的兼容性支持。
-
使用 Polyfill 或垫片库:Polyfill 是一个用于实现浏览器缺失功能的脚本,可以填充浏览器不支持的新 API,以实现跨浏览器的兼容性。
-
使用特性检测:通过检测浏览器的特性支持情况,可以有针对性地使用不同的代码或技术实现兼容性。
综合使用上述方法,可以有效地处理前端应用的兼容性问题,并确保应用在不同浏览器和设备上的一致性体验。
5.cdn服务器,我们通信的过程中,中间代理转发的服务器叫啥
1.CDN介绍
CDN(内容分发网络)是一种分布式的网络架构,用于加速用户对静态内容(如图片、脚本、样式表等)的访问。它通过在全球各地建立多个节点服务器,并缓存内容副本在离用户最近的节点上,来提供更快的内容传输和更低的延迟。
2.CDN工作流程
CDN(内容分发网络)的工作流程可以概括为以下几个步骤:
1.用户请求:
当用户在浏览器中输入网址或点击页面上的链接时,会发起对某个特定资源(如图片、脚本等)的请求。
2.DNS 解析:
用户的请求首先会发送给本地的 DNS(域名系统)服务器,DNS 服务器负责将域名解析为对应的 IP 地址。
3.路由定位:
基于解析得到的 IP 地址,请求会被路由到离用户最近的 CDN 节点。
4.缓存检查:
CDN 节点首先会检查请求的资源是否存在于其本地缓存中。如果存在,则直接返回缓存的副本给用户,从而提供快速的响应。
5.缓存未命中:
如果请求的资源不在本地缓存中(缓存未命中),CDN 节点会根据一定的策略从源服务器获取资源。
6.资源获取:
CDN 节点向源服务器发起请求,获取请求的资源。在此过程中,CDN 节点可能会使用一些优化技术,如持久连接、多路复用等,以提高性能和效率。
7.响应返回:
源服务器将请求的资源发送给 CDN 节点。CDN 节点会将资源返回给用户,并将资源保存在本地缓存中,以便后续请求的快速响应。
8.边缘节点之间的通信:
在 CDN 中,不同的边缘节点之间可能会进行通信,以便在节点之间共享缓存内容,提高整体的缓存命中率。
通过这样的工作流程,CDN 可以将静态资源缓存在离用户更近的节点上,使用户能够从最快的服务器获取内容,减少网络延迟和带宽消耗。CDN 还可以通过负载均衡、智能路由等技术,提高整体的可用性和可靠性,提供稳定高效的内容分发服务。
3.中间代理服务器
在通信过程中,中间代理转发的服务器通常称为代理服务器或中间代理服务器。
代理服务器位于客户端和目标服务器之间,接收客户端请求并转发给目标服务器,同时也可以对请求和响应进行一些处理。代理服务器在网络中起到了中继和转发的作用,可以提供一些额外的功能,如缓存、负载均衡、安全过滤等。
代理服务器在 CDN 中也发挥了重要的作用。
CDN 通过代理服务器将用户的请求转发到最佳的缓存节点,从而实现就近访问、减少网络拥塞和提高响应速度。代理服务器可以根据用户的位置和网络情况,选择最优的缓存节点进行转发,以提供更好的用户体验。
总结:在通信过程中,中间代理转发的服务器通常称为代理服务器或中间代理服务器。在 CDN 中,代理服务器起到了选择最佳缓存节点并转发用户请求的作用,以提供更快的内容传输和更好的用户体验。
6.http缓存有了解过吗max-age和s-maxage的作用和区别?谁的优先级更高?no-cache和no-store区别
在 HTTP 缓存中,max-age 和 s-maxage 是用于控制缓存过期时间的指令。它们具有以下作用和区别:
1.max-age:
max-age 是指定资源在客户端缓存中的最大存储时间,以秒为单位。当客户端请求一个被缓存的资源时,如果该资源的 max-age 值仍在有效期内,客户端可以直接从缓存中获取该资源,并避免向服务器发送请求。
2.s-maxage:
s-maxage 与 max-age 类似,但它只适用于共享缓存,如代理服务器缓存。当代理服务器缓存响应时,它会使用 s-maxage 指令来设置响应在代理服务器缓存中的最大存储时间。如果请求的资源在代理服务器的缓存中,并且 s-maxage 仍在有效期内,代理服务器可以直接返回缓存的响应,而无需向源服务器发送请求。
区别:
-
max-age 适用于客户端缓存和私有缓存(private cache),而 s-maxage 仅适用于共享缓存和代理服务器缓存。
-
s-maxage 的优先级高于 max-age。如果响应中同时设置了 s-maxage 和 max-age,代理服务器应该优先考虑 s-maxage 的值作为缓存的最大存储时间。
关于 no-cache 和 no-store 的区别如下: -
no-cache:no-cache 指令表示客户端或代理服务器在使用缓存前必须先与服务器确认资源是否有更新。它要求在每次请求时发送一个条件请求到服务器,服务器会根据资源的状态来决定是否返回缓存的响应。虽然称为 “no-cache”,但实际上仍然可以使用缓存,但需要经过服务器的验证。
-
no-store:no-store 指令表示客户端或代理服务器不应该将任何响应存储在缓存中。每次请求都必须直接从服务器获取资源,并且响应也不应该被缓存。这个指令提供了最高级别的缓存控制,确保敏感信息不会被存储在缓存中。
总结:
max-age 和 s-maxage 用于控制缓存过期时间,s-maxage 优先级高于 max-age。
no-cache 要求在每次请求前与服务器确认资源是否有更新,而 no-store 则禁止在缓存中存储任何响应。
7.Vue用的那个版本?2/3都用过,版本区别?(先说哪些方面不同,再谈具体的)
Vue 3 是 Vue.js 的下一个主要版本,相对于 Vue 2,它带来了一些重要的变化和改进。以下是 Vue 2 和 Vue 3 版本之间的一些主要区别:
1.性能优化:
Vue 3 在性能方面进行了一些优化。通过使用 Proxy 替代 Object.defineProperty,提供更高效的响应式系统。Vue 3 还引入了静态树提升(Static Tree Hoisting)和更好的代码压缩,以提高渲染性能。
2.Composition API:
Vue 3 引入了 Composition API,它是一种基于函数的 API 风格,使组件的逻辑可以更好地组织和重用。与 Vue 2 中的 Options API 相比,Composition API 更加灵活和可组合,可以更好地处理复杂的逻辑和状态管理。
3.更好的 TypeScript 支持:
Vue 3 在 TypeScript 方面进行了一些改进,提供了更好的类型推断和类型检查支持,使开发者可以更轻松地使用 TypeScript 开发 Vue 应用程序。
4.更小的包体积:
Vue 3 中的包体积相对较小,通过移除一些不常用的特性和优化打包方式,可以减少应用程序的大小。
5.Teleport:
Vue 3 引入了 Teleport 组件,它可以在组件树中的任何位置渲染内容,而不会受到父组件的样式和限制。
6.Fragments:
Vue 3 支持使用 Fragments 来包裹多个组件根元素,而不需要额外的包裹元素。
7.更好的 TypeScript 支持:
Vue 3 在 TypeScript 方面进行了一些改进,提供了更好的类型推断和类型检查支持,使开发者可以更轻松地使用 TypeScript 开发 Vue 应用程序。
需要注意的是,由于 Vue 3 引入了一些重大的改变,从 Vue 2 迁移到 Vue 3 可能需要进行一些代码的调整和迁移工作。Vue 官方提供了一些工具和指南,帮助开发者顺利迁移他们的应用程序到 Vue 3。
8.TypeScript也用过?学到什么程度?说下TS的一些关键词(提到类型,泛型,类型推断,类型断言,接口)infer有用过吗?(比较深入的点,没用过)
1.介绍
在 TypeScript 中,有许多重要的概念和功能,包括类型、泛型、类型推断、类型断言、接口和 infer。下面对它们进行简要介绍:
1.类型:
TypeScript 是一种静态类型的编程语言,它允许开发者在代码中指定变量、函数参数、函数返回值等的类型。类型可以帮助在编译时发现潜在的错误,并提供更好的代码提示和文档。
2.泛型:
泛型允许在定义函数、类或接口时使用类型参数,以增加代码的灵活性和复用性。通过泛型,可以编写更通用的代码,使函数和类能够处理不同类型的数据,而不需要重复编写相似的代码。
3.类型推断:
TypeScript 具有类型推断的能力,即在某些情况下,它可以自动推断出变量的类型,而无需显式指定类型。类型推断根据变量的初始化值和上下文来确定类型,并在后续使用中进行类型检查。
4.类型断言:
类型断言允许开发者显式地指定一个值的类型,即告诉编译器某个值的类型更准确或更具体。类型断言使用类似于类型转换的语法,但在运行时并不会实际改变值的类型,只是在编译阶段起作用。
5.接口:
接口是一种用于描述对象的结构的方式。它定义了对象应该具有的属性和方法,以及它们的类型。通过接口,可以提供对对象的类型检查和文档,同时也支持类的实现。
6.infer:
infer 关键字是在条件类型(Conditional Types)中使用的,用于在泛型条件类型中推断类型。它允许从给定的类型中推断出另一个类型,并将其绑定到一个新的类型参数。
这些概念和功能在 TypeScript 中起到关键作用,使得开发者能够更好地定义和使用类型,增加代码的可读性、可维护性和安全性。深入理解这些概念将有助于充分利用 TypeScript 的优势,并编写更健壮的代码。
2.代码示例
1.类型示例:
let name: string = 'John'; // 声明一个字符串类型的变量
let age: number = 30; // 声明一个数字类型的变量
let isStudent: boolean = true; // 声明一个布尔类型的变量
2.泛型示例:
function identity<T>(arg: T): T {
return arg;
}
let result = identity<string>('Hello'); // 显式指定类型参数
let result2 = identity('World'); // 类型推断,无需显式指定类型参数
3.类型推断示例:
let message = 'Hello World'; // 推断为字符串类型
let count = 10; // 推断为数字类型
let isValid = true; // 推断为布尔类型
function add(a: number, b: number) {
return a + b; // 返回值推断为数字类型
}
4.类型断言示例:
let value: any = 'Hello World';
let length: number = (value as string).length; // 类型断言为字符串类型
let num: any = 10;
let square = (num as number) * (num as number); // 类型断言为数字类型
5.接口示例:
interface Person {
name: string;
age: number;
}
function greet(person: Person) {
console.log(`Hello, ${person.name}! You are ${person.age} years old.`);
}
let john: Person = { name: 'John', age: 30 };
greet(john);
6.infer 示例:
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
function sum(a: number, b: number): number {
return a + b;
}
type Result = ReturnType<typeof sum>; // 推断为数字类型(函数的返回类型)
以上是一些简单的代码示例,用于介绍 TypeScript 中的类型、泛型、类型推断、类型断言、接口和 infer。这些示例可以帮助你更好地理解这些概念和它们的用法。在实际开发中,你可以根据具体需求和场景使用它们,并结合更复杂的代码和业务逻辑。
9.flex布局了解过吗flex-basis什么意思?默认值是?
display: flex;
是 CSS 中的一个布局属性,用于创建一个弹性布局容器。当应用于一个元素上时,该元素成为一个弹性容器,其子元素成为弹性项目,可以通过设置各种属性来灵活地调整布局。
在弹性布局中,flex-basis
是一个用于设置弹性项目的初始主轴尺寸
的属性。它定义了项目在主轴上的初始宽度、高度或尺寸
。
使用方法如下:
.flex-container {
display: flex;
}
.flex-item {
flex-basis: 200px;
}
在上面的示例中,.flex-container
是一个具有 display: flex; 的容器,其子元素 .flex-item 是弹性项目。通过设置 flex-basis: 200px;
,每个弹性项目的主轴尺寸(宽度或高度,取决于主轴方向)被初始化为 200 像素。
值得注意的是,flex-basis
的默认值是 auto
,这意味着弹性项目的尺寸将根据其内容自动调整。你可以使用不同的单位(像素、百分比等)指定具体的尺寸值。
除了 flex-basis,还有其他一些与弹性布局相关的属性,如flex-grow、flex-shrink 和 flex
等,它们可以一起使用来更精确地控制弹性项目的布局和大小。弹性布局是一种强大而灵活的布局方式,能够轻松实现自适应和响应式设计。
10.层叠上下文有了解过吗?说下作用
1.层列上下文介绍
层叠上下文(Stacking Context) CSS 中的一个概念和机制。CSS(层叠样式表)是一种用于描述网页样式和布局的标记语言,它定义了网页元素的外观和行为。层叠上下文是 CSS 中用于处理元素层叠关系的一种机制,它决定了元素如何在页面上相互叠放。
通过层叠上下文,你可以控制元素的层叠顺序、遮盖关系、透明度和混合模式效果,以及剪裁和溢出等方面的行为。它是一种在 CSS 渲染过程中处理元素层叠关系的机制,确保元素按照指定的层叠规则正确显示。
层叠上下文的形成是由一系列因素决定的,包括元素自身的属性(如 position、z-index 等)、其后代元素的层叠顺序、兄弟元素之间的层叠关系等。一些特定的 CSS 属性和值也会触发层叠上下文的形成。
层叠上下文的作用包括:
1.层叠顺序:
层叠上下文定义了元素的层叠顺序,即确定哪个元素在另一个元素的上方或下方。这是通过层叠上下文的层叠顺序值(Stacking Context Order)来决定的。
2.遮盖关系:
层叠上下文可以控制元素的遮盖关系,使某个元素可以遮盖或被其他元素遮盖。这是通过层叠上下文的层叠顺序和元素的堆叠水平(Stacking Level)来确定的。
3.透明度和混合模式:
层叠上下文可以影响元素的透明度和混合模式效果。在层叠上下文内部,元素和其子元素之间可以形成一种独立的渲染环境,使透明度和混合模式只在该环境内生效。
4.剪裁和溢出:
层叠上下文可以定义元素的剪裁区域,控制元素的溢出部分是否可见。这是通过层叠上下文的剪裁区域(Clipping Region)来实现的。
5.继承性:
层叠上下文可以继承父元素的层叠上下文属性,使得某些层叠属性可以沿着 DOM 树传递,形成一种层叠影响范围。
层叠上下文的形成是由一系列因素决定的,包括元素自身的属性(如 position、z-index 等)、其后代元素的层叠顺序、兄弟元素之间的层叠关系等。一些特定的 CSS 属性和值也会触发层叠上下文的形成,例如 position: relative;、opacity、transform、filter 等。
通过合理使用层叠上下文,可以更好地控制页面元素的层叠效果,避免元素重叠和遮挡的问题,提升用户界面的可视性和交互体验。
2.代码示例介绍
1.层叠顺序示例:
.container {
position: relative;
z-index: 1;
}
.child {
position: absolute;
top: 10px;
left: 10px;
z-index: 2;
}
在上面的示例中,.container 定义了一个相对定位的容器,并设置了 z-index 属性为 1。.child 是容器的子元素,使用绝对定位,并设置了 z-index 属性为 2。由于 .child 的层叠顺序高于 .container,所以它会在页面上显示在 .container 之上。
2.遮盖关系示例:
.container {
position: relative;
}
.child1 {
position: absolute;
top: 10px;
left: 10px;
z-index: 1;
}
.child2 {
position: absolute;
top: 20px;
left: 20px;
z-index: 2;
}
在这个示例中,.container 是一个相对定位的容器,其中包含两个绝对定位的子元素 .child1 和 .child2。由于 .child2 的层叠顺序高于 .child1,所以它会遮盖在 .child1 上方,从而形成遮盖关系。
3.透明度和混合模式示例:
.container {
position: relative;
opacity: 0.8;
mix-blend-mode: multiply;
}
.child {
position: absolute;
top: 10px;
left: 10px;
background-color: red;
opacity: 0.5;
}
在这个示例中,.container 定义了一个相对定位的容器,并设置了透明度为 0.8 和混合模式为 multiply。.child 是容器的子元素,使用绝对定位,并设置了背景颜色为红色和透明度为 0.5。由于 .child 在 .container 的层叠上下文中,它的透明度和混合模式会在自身范围内生效,而不受外部容器的影响。
4.剪裁示例:
.container {
width: 200px;
height: 200px;
overflow: hidden;
}
.child {
width: 300px;
height: 300px;
}
在上述示例中,.container 设置了固定的宽度和高度,并通过 overflow: hidden; 属性进行剪裁。.child 元素的尺寸大于 .container,但由于剪裁效果,超出容器范围的部分将被隐藏。
5.溢出示例:
.container {
width: 200px;
height: 200px;
overflow: scroll;
}
.child {
width: 300px;
height: 300px;
}
在这个示例中,.container 同样设置了固定的宽度和高度,但使用 overflow: scroll; 属性,使其具有滚动溢出的效果。.child 元素的尺寸仍然大于 .container,但现在 .container 将显示滚动条,允许用户滚动查看超出容器范围的内容。
最后两个示例演示了如何使用 overflow 属性来控制元素的剪裁和溢出行为。通过设置合适的值(如 hidden、scroll、auto 等),你可以根据需要对元素的内容进行剪裁或提供溢出滚动功能。这在处理容器中的大量内容或限制内容显示范围时非常有用。
11.事件循环了解过吗?讲下事件循环
事件循环(Event Loop)是 JavaScript 中用于处理异步操作的一种机制。它负责管理 JavaScript 运行时环境中的任务队列,并确保任务按照特定的顺序执行。
JavaScript 是一门单线程的语言,意味着它在任意给定时间只能执行一个任务。然而,JavaScript 也支持异步操作,例如网络请求、定时器和事件处理等,这些操作可能需要等待一段时间才能完成。
事件循环的目的是处理异步操作并保持主线程的响应能力。
1.它由以下几个组成部分:
-
调用栈(Call Stack):用于存储执行上下文(函数调用)的栈结构。当函数调用时,会将该函数的执行上下文推入调用栈,当函数执行完成后,会将其从调用栈中弹出。
-
任务队列(Task Queue):用于存储待执行的任务,包括异步操作的回调函数、事件处理函数等。
-
事件循环(Event Loop):负责持续监测调用栈和任务队列的状态,并根据特定规则将任务从任务队列中取出并推入调用栈,以执行相应的任务。
2.事件循环的基本流程如下:
-
(1)首先,JavaScript 解释器会执行同步代码,并将函数调用推入调用栈执行。
-
(2)当遇到异步操作时,例如定时器或网络请求,它们会被移出主线程并在指定的时间后返回执行结果。在此期间,JavaScript 运行时环境会继续执行后续的同步代码。
-
(3)异步操作完成后,其回调函数会被推入任务队列中。
-
(4)事件循环会不断检查调用栈是否为空。当调用栈为空时,它会从任务队列中取出一个任务,并将其推入调用栈,以执行相应的回调函数。
-
(5)执行回调函数期间,可能会产生新的异步操作,将它们推入任务队列。
-
(6)事件循环会重复上述过程,不断从任务队列中取出任务并执行,直到任务队列和调用栈都为空。
通过事件循环的机制,JavaScript 可以实现非阻塞的异步操作,保持主线程的响应性能,并且能够按照特定顺序执行异步任务。这样可以更好地处理网络请求、定时器、事件处理和其他异步操作,提供更好的用户体验。
12.从事件循环的角度谈下异步和同步
从事件循环的角度来看,同步和异步是关于任务执行的两种不同方式。
1.同步任务(Synchronous Tasks):
- 同步任务是按照代码的顺序依次执行的任务。
- 当遇到同步任务时,JavaScript 解释器会将任务推入调用栈中,并立即执行它。
- 在同步任务执行期间,JavaScript 运行时环境会阻塞主线程,直到该任务执行完成。
- 同步任务执行完成后,JavaScript 解释器会继续检查任务队列和调用栈,执行下一个任务。
2.异步任务(Asynchronous Tasks):
- 异步任务是不会立即执行的任务,需要等待特定条件满足或指定时间后才执行。
- 当遇到异步任务时,JavaScript 解释器会将任务移出主线程,并在异步操作完成后将其回调函数推入任务队列中。
- 异步任务执行期间,JavaScript 运行时环境不会阻塞主线程,可以继续执行后续的任务。
- 当调用栈为空时,事件循环会从任务队列中取出一个任务,并将其推入调用栈,以执行相应的回调函数。
- 通过事件循环的机制,异步任务可以在后台执行,不会阻塞主线程,从而保持了 JavaScript 运行时的响应性能。常见的异步任务包括定时器(setTimeout、setInterval)、网络请求(Ajax、fetch)、事件处理函数等。
需要注意的是,异步任务的执行顺序不一定是按照它们被调用的顺序执行的,而是取决于它们在任务队列中的排队顺序和当前调用栈的状态。因此,异步任务的执行顺序可能与代码编写的顺序不完全一致。
总结来说,同步任务是按照顺序执行的任务,会阻塞主线程;而异步任务是不会立即执行的任务,通过事件循环的机制在特定条件满足时执行,不会阻塞主线程。这种异步执行的方式使得 JavaScript 可以处理复杂的任务和保持响应性能。
13.UMD了解过吗?这里没深入了解过,说自己了解过ESModule和CommonJs,讲下这两个的区别(说了两点:1.同步加载和异步加载的区别;2.导入值是否可修改的差别)
1.UMD介绍
UMD(Universal Module Definition,通用模块定义)是一种用于创建可在不同环境(浏览器、Node.js 等)中使用的 JavaScript 模块的规范。
在早期的 JavaScript 开发中,存在多种模块化方案,如 CommonJS 和 AMD。但它们在不同的环境下使用的语法和加载机制有所不同,导致模块在不同环境中的兼容性问题。
UMD 的目标是提供一种通用的模块定义规范,使得一个模块可以在多种环境中使用,无需修改模块的代码。它通过在模块中进行环境判断和兼容性处理来实现。
UMD 模块通常包含以下几个部分:
1.使用条件判断检测当前的执行环境,
通常使用 typeof 和 require 等关键词进行判断。
2.根据当前环境的判断结果
选择合适的模块加载方式和导出方式。
-
如果是在浏览器环境中,通常会将模块挂载到全局对象(如 window)上,供其他脚本访问。
-
如果是在 Node.js 环境中,通常会使用 module.exports 导出模块,供其他模块引用。
-
如果是在支持 AMD(异步模块定义)的环境中,可以使用 AMD 的方式加载和导出模块。
3.编写模块的具体代码实现
该部分代码可以是符合 CommonJS 或 AMD 规范的代码,或是纯粹的原始 JavaScript 代码。
UMD 模块的优势在于它能够在不同的环境中无缝运行,无需对模块代码进行修改。这种灵活性使得 UMD 成为早期 JavaScript 模块化开发中的一种常用规范。
然而,随着 ES6 模块的出现和现代浏览器的普及,使用原生的 ES6 模块语法和模块加载机制已成为主流。因此,对于新的项目,建议优先考虑使用 ES6 模块,而将 UMD 作为对于旧代码的兼容方案。
2.UMD代码实现
下面是一个使用 UMD 规范编写的示例代码:
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD 环境下加载模块
define(['dependency'], factory);
} else if (typeof exports === 'object' && typeof module === 'object') {
// CommonJS 环境下加载模块
module.exports = factory(require('dependency'));
} else {
// 全局环境下挂载模块到 root 对象上
root.MyModule = factory(root.Dependency);
}
})(typeof self !== 'undefined' ? self : this, function (Dependency) {
// 模块的具体实现
var MyModule = {};
// 模块的功能代码
MyModule.doSomething = function () {
// ...
};
// 导出模块
return MyModule;
});
在上述示例代码中:
1.使用自执行函数包裹模块的代码,将其作为一个独立的闭包。
2.函数的第一个参数 root 表示全局对象,在浏览器环境下为 window 对象,在 Node.js 环境下为 global 对象。
3.函数的第二个参数 factory 是一个回调函数,用于定义模块的具体实现。
4.在函数内部,通过判断当前的执行环境来选择合适的方式加载和导出模块。
-
(1)如果当前环境支持 AMD(异步模块定义),则使用 define 函数加载模块。
-
(2)如果当前环境是 CommonJS(如 Node.js),则使用 module.exports 导出模块。
-
(3)否则,将模块挂载到全局对象 root 上。
5.在模块的具体实现中,可以根据需要添加自定义的功能代码。
6.最后,通过 return 关键字返回模块对象,使其可以被其他模块或全局对象访问到。
这样,使用 UMD 规范编写的模块可以在不同的环境中使用,无需修改模块的代码。在浏览器环境中,可以使用 AMD 方式加载模块;在 Node.js 环境中,可以使用 CommonJS 方式加载模块;在全局环境中,可以通过全局对象访问模块。
CommonJS 与 ES Modules异同
CommonJS 和 ES Modules(ESM)是两种不同的模块化规范,用于在 JavaScript 中组织和管理代码模块。它们有一些区别和不同的语法和特性。
CommonJS 是最早提出的模块化规范,主要用于服务器端开发和 Node.js 环境。它使用 require 来引入模块,使用 module.exports 或 exports 来导出模块。CommonJS 模块是同步加载的, 适用于文件系统的读取操作。在 CommonJS 中,模块是一个对象,它的属性和方法可以通过导出和引入进行访问。
示例使用 CommonJS 的导入和导出模块的方式:
// 导出模块
module.exports = {
foo: 'Hello',
bar: 'World'
};
// 导入模块
const myModule = require('./myModule');
console.log(myModule.foo); // 输出: Hello
console.log(myModule.bar); // 输出: World
ES Modules 是 ECMAScript(JavaScript)的官方模块化规范,从 ECMAScript 6(ES6)开始引入。
它可以在浏览器环境和现代的 JavaScript 运行时(如 Node.js)中使用。ES Modules 使用 import 来引入模块,使用 export 来导出模块。ES Modules 支持异步加载,适用于网络请求等异步场景。
示例使用 ES Modules 的导入和导出模块的方式:
// 导出模块
export const foo = 'Hello';
export const bar = 'World';
// 导入模块
import { foo, bar } from './myModule';
console.log(foo); // 输出: Hello
console.log(bar); // 输出: World
ES Modules 的语法更加简洁和直观,可以进行静态分析和优化。它支持按需导入和导出模块,动态导入模块以及命名导入和导出。
ES Modules 也在逐渐取代 CommonJS 成为 JavaScript 模块化的主流标准。
需要注意的是,Node.js 在较新的版本中也支持 ES Modules,可以使用 .mjs 文件扩展名或在 package.json 中设置 “type”: “module” 来启用 ES Modules。但是,由于现有的生态系统和大量的第三方模块仍使用 CommonJS 规范,因此在 Node.js 中仍然可以继续使用 CommonJS 模块。
14.算法:分苹果:1000个苹果,10个盒子
15.算法:“10+2+12-3-10”计算最终结果
三面
1.自我介绍
2.项目中的难点(简历强相关)
3.大二就去实习了,为什么后来没有实习,是感觉实习学不到东西吗?
4.你平时是怎么学习前端的?(这个问题抖音三面都问了)
5.承接上问,平时写过哪些内容的博客?有没有自己去实践做一些东西?(讲了下自己写过的博客内容,讲了下正在做的一个远程控制软件)
6.你觉得自己哪里学的比较好(说到vue框架和js的一些核心原理理解得比较好)
7.vue的响应式原理,vue3与vue2的区别?
Vue.js 的响应式原理是通过使用 Object.defineProperty(或 ES6 的 Proxy)来劫持 JavaScript 对象的访问和修改,从而实现数据的变化监听和自动更新视图的能力。
具体来说,以下是 Vue.js 的响应式原理的关键步骤:
1.初始化阶段:
-
Vue 实例创建时,会对数据对象进行遍历,使用 Object.defineProperty(或 Proxy)为每个属性设置 getter 和 setter。
-
这样,当访问或修改这些属性时,Vue 就能捕获到对应的操作,并执行相应的响应式处理。
2.数据劫持阶段:
-
当访问被劫持的属性时,getter 函数会被触发,用于收集依赖(即将当前的观察者对象加入依赖列表中)。
-
当修改被劫持的属性时,setter 函数会被触发,用于通知依赖进行更新。
-
在 getter 和 setter 中,Vue 会执行相应的依赖收集和派发更新的操作。
3.依赖收集阶段:
-
每个被劫持的属性都有一个依赖列表,用于存储当前属性的观察者对象(Watcher)。
-
当访问被劫持的属性时,Vue 会检查当前是否存在正在进行依赖收集的观察者对象,如果有,则将该观察者对象添加到依赖列表中。
4.视图更新阶段:
-
当修改被劫持的属性时,Vue 会通知该属性的依赖列表进行更新。
-
对于需要更新的依赖,Vue 会重新求值(执行 getter)获取最新的值,并与之前的值进行比较。
-
如果值发生变化,Vue 会通知对应的 Watcher 对象,触发视图的更新操作。
通过以上步骤,Vue 实现了数据和视图之间的自动关联。当数据发生变化时,相关的视图会自动更新,从而实现了响应式的效果。
需要注意的是,Vue.js 的响应式原理是基于对象的属性劫持,因此只有对象的属性是响应式的,而对于数组的变化,Vue 采用了一些特殊的处理方式来实现数组的响应式。此外,Vue 还提供了一些 API(如 s e t 、 set、 set、delete、Vue.set、Vue.delete)用于操作响应式的数组。
8.浏览器为什么要设置两个队列而不是一个队列
浏览器中设置两个队列(宏任务队列和微任务队列)的原因是为了处理不同类型的任务,并确保任务的执行顺序和优先级。
1.宏任务队列:
-
宏任务队列用于处理一些耗时较长的任务,如事件回调、定时器回调、网络请求等。
-
宏任务队列中的任务按照先进先出(FIFO)的顺序执行,即每次从队列的头部取出一个任务执行。
-
浏览器会在每个宏任务执行完毕后,检查微任务队列是否为空,如果不为空,则执行微任务队列中的任务。
2.微任务队列:
-
微任务队列用于处理一些需要尽快执行的任务,如 Promise 的回调、MutationObserver 的回调等。
-
微任务队列中的任务在宏任务执行完毕后立即执行,而不需要等待下一个宏任务。
-
微任务队列中的任务也按照先进先出的顺序执行。
3.为什么要设置两个队列而不是一个队列?
-
执行机制:宏任务队列中的任务是在一个事件循环周期内执行的,而微任务队列中的任务则在当前任务执行完毕后立即执行。
-
优先级:微任务队列的优先级要高于宏任务队列。这意味着微任务中的任务会在下一个宏任务执行之前执行,从而保证微任务中的操作能够尽早生效,并且可以在下一个宏任务中更新视图。
将任务分为宏任务和微任务队列有助于管理任务的执行顺序和优先级。宏任务适用于处理一些相对较慢的任务,而微任务适用于处理一些需要快速执行的任务,并在当前任务结束前立即执行。这样的任务分配机制有助于提高页面的响应性和性能。
9.为什么要有事件循环这个机制,其他语言有的也没有这个机制不是也活得好好吗
JavaScript 之所以引入事件循环机制,是因为 JavaScript 在浏览器环境中主要用于处理用户交互和异步操作,而事件循环能够有效地管理和调度这些异步任务的执行顺序。
以下是一些原因解释为什么 JavaScript 需要事件循环机制:
1.单线程执行:
JavaScript 在浏览器中是单线程执行的,即一次只能执行一个任务。如果没有事件循环机制,长时间的同步操作或阻塞操作会导致页面失去响应,用户体验变差。
2.异步操作:
JavaScript 经常需要处理异步操作,如定时器、网络请求、事件回调等。这些异步操作需要在适当的时机执行,并且在执行完成后触发相应的回调或处理。
3.非阻塞执行:
JavaScript 的事件循环机制可以实现非阻塞执行。当遇到耗时的操作时,不会阻塞主线程,而是将任务交给事件循环,继续执行后续任务,当耗时操作完成后,通过回调函数或者 Promise 的方式将结果返回并执行相应的处理。
4.响应性:
事件循环机制使得 JavaScript 能够快速响应用户的交互操作。例如,点击按钮、滚动页面等交互事件可以立即得到响应,而不会被长时间的同步操作阻塞。
5.优化资源利用:
事件循环机制可以合理地管理任务的执行顺序,避免资源的浪费。它能够根据任务的类型和优先级来安排执行顺序,提高任务的效率和性能。
虽然其他语言也有异步编程的需求,但它们的异步机制和任务调度方式可能不同。JavaScript 的事件循环机制是为了适应浏览器环境下的异步任务处理而设计的,并且在 Web 开发中发挥了重要的作用。它使得 JavaScript 能够以非阻塞的方式处理异步操作,保证页面的响应性和性能。
10.你觉得自己的优缺点有哪些?
11.有没有系统的看过哪本书(这里给了一些建议)
12.除了写博客,有没有自己去做一些小东西(说到自己在做一个远程控制软件)
13.远程控制怎么实现的呢,这里讲到webRTC
14.算法题:一道手写题实现接口Array.prototype.getReader = function () {…}
15.你对未来的一个职业规划是怎样的,最近的2/3年?(先成长为一个能够独当一面的工程师吧hhh)
16.你有什么想实现的但还未实现的想法吗
我这边问的差不多了,你还有什么要补充的吗