问题1:请描述一下TCP连接过程
TCP连接的过程通常包括三个主要步骤:建立连接、数据传输和断开连接。总结即:3次握手4次挥手。
在TCP连接的建立过程中,进行了三次握手。以下是每次握手的描述:
-
第一次握手(SYN):
- 客户端向服务器发送一个带有SYN标志的TCP报文段,表示客户端请求建立连接。
- 客户端选择一个初始序列号(ISN)并将其包含在报文段中。
-
第二次握手(SYN + ACK):
- 服务器收到客户端的请求后,会回复一个带有SYN和ACK标志的TCP报文段作为确认。
- 服务器也会选择一个自己的初始序列号,并将其包含在报文段中。
- 确认号(ACK)字段表明服务器接收到了客户端的初始序列号。
-
第三次握手(ACK):
- 客户端收到服务器的确认后,会发送一个带有ACK标志的TCP报文段作为最后的确认。
- 客户端的确认号表示客户端已经接收到了服务器的初始序列号。
- 服务器收到客户端的确认后,就可以开始正式传输数据
在TCP连接的断开过程中,进行了四次挥手。以下是每次挥手的描述:
-
第一次挥手(FIN):
- 一方(通常是客户端)发送一个带有FIN标志的TCP报文段,表示希望关闭连接。
- 发送方不再发送数据,但仍然可以接收对方发送的数据。
-
第二次挥手(ACK):
- 接收方(通常是服务器)收到关闭请求后,发送一个带有ACK标志的TCP报文段作为确认。
- 这个报文段确认了收到了关闭请求,并向对方表示自己还有未完成的数据要发送。
-
第三次挥手(FIN):
- 接收方(通常是服务器)也准备好关闭连接时,会发送一个带有FIN标志的TCP报文段。
- 这是它发起的关闭请求,表示它已经完成了所有数据的发送。
-
第四次挥手(ACK):
- 发送方(通常是客户端)收到关闭请求后,发送一个带有ACK标志的TCP报文段作为最后的确认。
- 这个报文段表示发送方已确认接收方的关闭请求,并且双方都知道连接已经关闭。
通过这四次挥手,双方能够确保彼此都知道关闭请求和确认,并完成了所有数据的传输。这样,TCP连接就顺利地断开了。
问题2:TCP与UDP的区别
主要区别如下:
- 连接性:TCP是面向连接的协议,通过建立可靠的连接来传输数据;UDP是无连接的协议,数据包之间独立发送,不保证可靠传输。
- 可靠性:TCP提供可靠的数据传输,确保数据按顺序到达且无丢失;UDP不保证可靠性,可能存在数据丢失或乱序的情况。
- 速度:UDP比TCP快,因为TCP需要进行连接的建立和维护等额外操作,而UDP直接发送数据。
- 数据量和分包:TCP没有数据报大小限制,可以处理大量数据,并自动进行分包和重组;UDP有最大长度限制(约65,535字节),需要手动处理分包和重组。
- 适用场景:TCP适合要求数据完整性和顺序性的应用,如文件传输、电子邮件等;UDP适合实时性要求高但可容忍少量数据丢失的应用,如音视频流媒体、在线游戏等。
总之,TCP提供可靠的、有序的数据传输,适用于对可靠性要求较高的应用;UDP提供快速的、简单的数据传输,适用于实时性要求高的应用。选择哪种协议取决于应用需求和优先级。
问题3:请简单概述TCP的拥塞控制和流量控制
TCP的拥塞控制和流量控制是两个关键机制,用于确保网络传输的稳定性和公平性。
-
拥塞控制:
- 拥塞控制是TCP在网络中防止拥塞(即过多数据导致网络性能下降)的机制。
- TCP使用一种叫做拥塞窗口(Congestion Window) 的变量来控制发送数据的速率。
- 拥塞控制算法通过动态调整拥塞窗口的大小,以便根据网络状况适应传输速率。
- 常见的拥塞控制算法包括慢开始、拥塞避免和快重传/快恢复等。
-
流量控制:
- 流量控制是为了确保接收方能够处理来自发送方的数据而采取的措施。
- TCP使用一种叫做接收窗口(Receive Window) 的机制进行流量控制。
- 接收窗口告知发送方可以发送多少数据,在接收方处理数据之前不要发送更多的数据。
- 通过动态调整接收窗口的大小,TCP可以确保发送方的数据与接收方的处理能力相匹配,避免数据丢失或缓冲区溢出。
这两个机制共同作用于TCP协议,通过动态地调整窗口大小和传输速率,以适应网络的变化和接收方的处理能力。拥塞控制确保网络不被过载,而流量控制则确保发送方不会压倒接收方。这样,TCP可以在网络中实现高效、可靠的数据传输。
问题4:请回答https与http的区别
-
安全性:
- HTTPS(安全HTTP)通过使用SSL(或TLS)加密来保护数据的传输安全性。它使用公钥加密和私钥解密的方式,确保通信过程中的隐私和数据完整性。
- HTTP(超文本传输协议)在数据传输过程中不提供任何加密措施,因此数据可能以明文形式在网络上传输,容易被窃听和篡改。
-
端口号:
- HTTPS默认使用443端口进行通信。
- HTTP默认使用80端口进行通信。
-
授权与身份验证:
- HTTPS常用于需要保护用户敏感信息的网站,如银行、电子商务等。它提供了对服务器身份的验证机制,可以防止恶意伪造网站。
- HTTP没有内置的身份验证机制,无法验证服务器的真实性,容易受到中间人攻击。
- 响应速度
- HTTP页面响应速度比HTTPS快,因为HTTP使用TCP三次握手建立连接,客户端和服务器需要交换 3 个包,而HTTPS除了TCP的三个包,还要加上ssl握手需要的 9 个包,所以一共是 12 个包。
总结起来,HTTPS相比于HTTP,在传输过程中提供了更高的安全性,通过加密保护数据隐私和完整性。它常用于处理敏感信息的网站和保护用户的隐私。而HTTP是一种简单的传输协议,适用于一般的非敏感数据传输。
问题5:什么是跨域?
跨域就是前端访问后台时,域名,协议,端口有一个不相同时,就会出现跨域,请求不到数据。
跨域问题常见于以下情况:
- 在JavaScript中使用XMLHttpRequest或Fetch API进行AJAX请求时,目标URL与当前页面的域名不一致。
- 在页面中引用外部的CSS、JavaScript文件或字体等资源时,资源的URL与当前页面的域名不一致。
- 使用iframe或者通过JavaScript操作其他窗口(如window.open)时,目标窗口的域名与当前页面的域名不一致。
为了解决跨域问题,通常需要通过服务器端设置CORS(Cross-Origin Resource Sharing)响应头来允许跨域请求,或者使用JSONP、代理服务器等技术手段来绕过同源策略。
问题6:cookie,localStorage, sessionStorage三者区别
一、存储的时间有效期不同
1、cookie的有效期是可以设置的,默认的情况下是关闭浏览器后失效
2、sessionStorage的有效期是仅保持在当前页面,关闭当前会话页或者浏览器后就会失效
3、localStorage的有效期是在不进行手动删除的情况下是一直有效的
二、存储的大小不同
1、cookie的存储是4kb左右,存储量较小,一般页面最多存储20条左右信息
2、localStorage和sessionStorage的存储容量是5Mb(官方介绍,可能和浏览器有部分差异性)
三、与服务端的通信
1、cookie会参与到与服务端的通信中,一般会携带在http请求的头部中,例如一些关键密匙验证等。
2、localStorage和sessionStorage是单纯的前端存储,不参与与服务端的通信,所以安全性都要比cookie要好。
四、读写操作的便捷程度
1、cookie的相关操作,cookie操作起来较为繁琐,并且部分数据不可以读取操作
五、对于浏览器的支持
1、cookie出现的时间较早,目前见到的浏览器都支持
2、localStorage和sessionStorage出现的时间较晚,对于版本较低的浏览器不支持(比如IE8版本以下的都不支持)
六、应用场景
1、cookie:判断用户是否登录过网站,以便下次登录时能够实现自动登录(或者记住密码)。如果我们删除Cookie,则每次登录必须从新填写登录的相关信息;保存上次登录的时间等信息;保存上次查看的页面;浏览计数;
2、localStoragese:常用于长期登录(+判断用户是否已登录),适合长期保存在本地的数据;
3、sessionStorage:敏感账号一次性登录;
问题7:什么是闭包
所谓闭包,指的就是一个函数。当两个函数彼此嵌套时,内部的函数就是闭包。
因为在 JavaScript 中,函数属于对象,对象又是属性的集合,而属性的值又可以是对象,所以我们可以在函数内部再定义函数。例如在函数 A 中定义了函数 B,然后在函数外部调用函数 B,这个过程就是闭包。
闭包的形成条件是内部函数需要通过外部函数 return 给返回出来,如下例所示:
function funOne(){ // 外部函数
var num = 0; // 局部变量
function funTwo(){ // 内部函数
num++;
return num;
}
return funTwo;
}
var fun = funOne(); // 返回函数 funTwo
问题8:闭包的应用
通过闭包,我们可以创建私有变量、保存状态、实现封装性等。常见的应用包括:
- 记忆化:闭包可以用来缓存函数的计算结果,以提高性能。
- 封装:通过闭包,可以创建私有变量和方法,隐藏实现细节,提供公共接口。
- 延迟执行:通过闭包可以创建延迟执行函数,例如使用setTimeout和setInterval函数。
- 回调函数:闭包常常用于实现回调函数,在异步操作完成后执行相应的处理逻辑。
需要注意的是,由于闭包会保留对外部函数作用域的引用,使用不当可能导致内存泄漏。在处理闭包时,需要注意及时释放不再使用的资源,避免造成内存浪费。
当我们需要在函数中定义一些变量,并且希望这些变量能够一直保存在内存中,同时不影响函数外的全局变量时,就可以使用闭包。
问题9:ajax中的dataType有哪几种
在 AJAX 中,dataType 是指发送到服务器的数据类型以及期望从服务器返回的数据类型。它用于告诉服务器如何解析响应数据并将其转换为 JavaScript 对象。
以下是常见的 dataType 值:
&[#34;xml&#]34;:预期服务器返回 XML 数据。
&[#34;html&#]34;:预期服务器返回 HTML 数据。
&[#34;text&#]34;:预期服务器返回纯文本数据。
&[#34;json&#]34;:预期服务器返回 JSON 格式的数据。
&[#34;script&#]34;:预期服务器返回 JavaScript 代码。
&[#34;jsonp&#]34;:用于进行跨域请求,并期望服务器返回 JSONP 格式的数据。
&[#34;binary&#]34;:预期服务器返回二进制数据。
这些值可以作为 dataType 的参数传递给 AJAX 请求的配置对象中。根据所选择的 dataType,AJAX 将自动对响应数据进行相应的解析和处理。
问题10:get和post的区别
GET 和 POST 是 HTTP 协议中两种常见的请求方法,它们在以下方面有所区别:
- 请求位置:GET 请求将参数附加在 URL 的查询字符串中,而 POST 请求将参数包含在请求体中。
- 数据传输方式:GET 请求使用 URL 进行数据传输,数据可被缓存、保存在浏览器历史记录中,以及作为书签添加和共享。POST 请求将数据封装在请求体中进行传输,不会被缓存或保存在浏览器历史记录中。
- 安全性:GET 请求的参数暴露在 URL 中,可能会被保存在服务器日志、浏览器历史记录以及代理服务器日志中,因此不适合传输敏感信息。POST 请求的参数在请求体中,相对于 GET 请求更安全。
- 数据长度限制:GET 请求对 URL 长度有限制,不同浏览器有不同的限制,通常是几千字符。POST 请求没有特定的限制,但服务器端可能设置了最大请求体大小。
- 幂等性:GET 请求是幂等的,即多次重复的 GET 请求不会产生副作用。POST 请求不是幂等的,多次重复的 POST 请求可能会产生不同的结果。
- 缓存:GET 请求可以被缓存,可以利用浏览器缓存机制提高性能。POST 请求默认不会被缓存。
根据需求选择使用 GET 还是 POST,一般来说,GET 适用于获取资源、查询数据等无副作用的请求,而 POST 适用于提交表单、发送数据并产生副作用的请求。
问题11:介绍 js 的基本数据类型
js 一共有六种基本数据类型,分别是 Undefined、Null、Boolean、Number、String,还有在 ES6 中新增的 Symbol 类型,代表创建后独一无二且不可变的数据类型,它的出现我认为主要是为了解决可能出现的全局变量冲突的问题。
问题12:null 和 undefined 的区别?
首先 Undefined 和 Null 都是基本数据类型,这两个基本数据类型分别都只有一个值,就是 undefined 和 null。
undefined 代表的含义是未定义,null 代表的含义是空对象。一般变量声明了但还没有定义的时候会返回 undefined,null主要用于赋值给一些可能会返回对象的变量,作为初始化。
undefined 在 js 中不是一个保留字,这意味着我们可以使用 undefined 来作为一个变量名,这样的做法是非常危险的,它会影响我们对 undefined 值的判断。但是我们可以通过一些方法获得安全的 undefined 值,比如说 void 0。
当我们对两种类型使用 typeof 进行判断的时候,Null 类型化会返回 “object”,这是一个历史遗留的问题。当我们使用双等号对两种类型的值进行比较时会返回 true,使用三个等号时会返回 false。
问题13:如何阻止事件冒泡?
event.stopPropagation() 或者 ie 下的方法 event.cancelBubble = true;
问题14:Javascript中Map与Object的区别
1.Map可以将任何数据类型作为key,而Object,只能使用String或者Symbol。
一个 Map的键可以是任意值,包括函数、对象或任意基本类型。
const map = new Map();
map.set(NaN, 'NaN');
map.get(NaN) // NaN
map.set(null, 'null');
map.get(null) // null
map.set(undefined, '未定义');
map.get() // 未定义
2.Map的键是按插入顺序排序的,而Object并非总是按插入顺序
3.性能差别Map相对于Obj还是快的
Map: 在频繁增删键值对的场景下表现更好。
Object: 在频繁添加和删除键值对的场景下未作出优化。
问题14:JavaScript中Map和forEach的区别
- forEach不会有返回值,无法通过break中断,可通过抛出异常中断循环
- Map会返回一个新数组
问题15:HTML5有哪些新特性?
- 语义化标签
- 增强型表单,新增表单 Input 输入类型。这些新特性提供了更好的输入控制和验证。
- 新增视频和音频标签
- Canvas绘图
- SVG绘图
- 地理定位
- 拖放API
- Web Worker
- Web Storage
- WebSocket
问题16:iframe的优缺点?
优点:
1.iframe能够原封不动的把嵌入的网页展现出来。
2.如果有多个网页引用iframe,那么你只需要修改iframe的内容,就可以实现调用的每一个页面内容的更改,方便快捷。
3.网页如果为了统一风格,头部和版本都是一样的,就可以写成一个页面,用iframe来嵌套,可以增加代码的可重用。
4.如果遇到加载缓慢的第三方内容如图标和广告,这些问题可以由iframe来解决。
缺点:
1.会产生很多页面,不容易管理。
2.iframe框架结构有时会让人感到迷惑,如果框架个数多的话,可能会出现上下、左右滚动条,会分散访问者的注意力,用户体验度差。
3.代码复杂,无法被一些搜索引擎索引到,这一点很关键,现在的搜索引擎爬虫还不能很好的处理iframe中的内容,所以使用iframe会不利于搜索引擎优化。
4.很多的移动设备(PDA 手机)无法完全显示框架,设备兼容性差。
5.iframe框架页面会增加服务器的http请求,对于大型网站是不可取的。
问题17:行内元素有哪些?块级元素有哪些?CSS的盒模型?
常见的行内元素有:等。行内元素不会独占一行,只会根据内容的大小进行排列。
常见的块级元素有:等。块级元素会独占一行,相邻的块级元素会另起一行。
CSS的盒模型是指在网页布局中,每个元素都被看作一个盒子,包含了内容区域、内边距、边框和外边距。CSS盒模型分为两种:
- 标准盒模型(content-box):元素的宽度(width)和高度(height)只包括内容区域的大小,不包括内边距、边框和外边距。
- IE盒模型(border-box):元素的宽度(width)和高度(height)包括了内容区域、内边距和边框的大小,其中外边距不计入。
通过CSS的属性可以控制元素使用哪种盒模型。默认值是,如果设置为,则元素的宽度和高度将包括内边距和边框的大小。
问题18:call()和apply()的区别
和是JavaScript中用于调用函数的方法,它们的主要区别在于参数的传递方式。
方法:通过指定的上下文对象(即函数的执行环境)以及一系列单独的参数来调用函数。参数需要按顺序一个个传递给函数。
function greeting(name, age) {
console.log(`Hello, ${name}. You are ${age} years old.`);
}
greeting.call(null, 'John', 25); // 输出:Hello, John. You are 25 years old.
方法:与类似,也是通过指定上下文对象来调用函数,但参数需要以数组或类数组对象的形式传递给函数。数组中的每一项对应函数的参数列表中的一个参数。
function greeting(name, age) {
console.log(`Hello, ${name}. You are ${age} years old.`);
}
greeting.apply(null, ['John', 25]); // 输出:Hello, John. You are 25 years old.
总结:
- call()和apply()都可以改变函数内部的this指向(即函数的执行上下文)。
- 区别在于参数的传递方式,call()需要按顺序一个个传递参数,而apply()将参数以数组形式传递。
- 在ES6中,我们通常使用扩展运算符(…)代替apply(),因为它更简洁方便。例如:greeting(…['John', 25]);
问题19:var、let和const有什么区别
1.var定义的变量可以跨块作用域访问,不可以跨函数作用域访问。
2.let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问;不允许在相同作用域内,重复声明同一个变量。
3.const定义的常量,初始化时必须赋值,只能在块作用域里使用,不能修改。
问题20:深拷贝和浅拷贝
在 JavaScript 中,有深拷贝(deep copy)和浅拷贝(shallow copy)两种方式来复制对象或数组。它们的主要区别在于拷贝后的结果对原始对象中的嵌套对象的引用关系处理不同
1.浅拷贝(Shallow Copy):
浅拷贝会创建一个新的对象或数组,并将原始对象中的属性值或数组元素的引用复制到新对象中。换句话说,新对象与原始对象共享同一组对象引用。修改其中一个对象的属性或数组元素时,另一个对象也会受到影响。
var originalArray = [1, 2, 3];
var shallowCopyArray = originalArray.slice();
shallowCopyArray.push(4);
console.log(originalArray); // 输出: [1, 2, 3]
console.log(shallowCopyArray); // 输出: [1, 2, 3, 4]
2.深拷贝(Deep Copy):
深拷贝会递归地复制对象或数组及其所有嵌套对象,创建一个完全独立的副本。修改其中一个对象的属性或数组元素不会对其他对象造成任何影响。
var originalObject = { name: 'John', age: 25 };
var deepCopyObject = JSON.parse(JSON.stringify(originalObject));
deepCopyObject.age = 30;
console.log(originalObject); // 输出: { name: 'John', age: 25 }
console.log(deepCopyObject); // 输出: { name: 'John', age: 30 }
在上面的示例中,通过使用JSON.stringify()和JSON.parse()对原始对象进行深拷贝。当修改深拷贝的对象的属性时,原始对象不会受到影响。
需要注意的是,深拷贝方法(如上述示例中的基于JSON的方式)可能无法处理包含特殊类型或循环引用的复杂对象结构,因此在实际应用中需要综合考虑深拷贝的适用性和性能。
问题21:CSS选择符都有哪些?哪些属性可以继承?优先级算法如何计算?
- 元素选择符:通过元素名称来选择元素,例如 p 选择所有 <p> 元素。
- 类选择符:通过类名来选择元素,以点号开头,例如 .example 选择所有具有 class 属性为 "example" 的元素。
- ID选择符:通过ID来选择元素,以井号开头,例如 #header 选择 id 为 "header" 的元素。
- 属性选择符:通过元素的属性来选择元素,例如 [type="text"] 选择所有 type 属性值为 "text" 的元素。
- 后代选择符:用空格分隔的选择符,选择指定元素的后代元素,例如 div p 选择 <div> 内部的所有 <p> 元素。
- 相邻兄弟选择符:使用加号 (+) 选择紧接在指定元素之后的同级元素,例如 h2 + p 选择紧接在 <h2> 元素后的 <p> 元素。
- 伪类选择符:表示特定状态或行为的元素,例如 :hover 选择鼠标悬停在元素上的状态。
- 伪元素选择符:用于创建元素的虚拟副本或添加特定内容,例如 ::before 在元素前插入内容。
一些常见的可继承属性包括:
- font
- text-align
- color
- line-height
- visibility
总结:字体、文本相关的属性通常可继承。布局、盒模型相关的属性通常不可继承,继承的属性使子元素可以从父元素继承样式,不继承的属性需要给每个元素单独设置样式。
在CSS中,元素的样式优先级是根据以下规则计算的(从高到低):
- !important:具有 !important 标记的样式具有最高优先级。
- 内联样式:直接应用于元素的内联样式。
- ID选择符:具有ID选择符的样式。
- 类选择符、属性选择符和伪类选择符:具有类选择符、属性选择符或伪类选择符的样式。
- 元素选择符和伪元素选择符:具有元素选择符或伪元素选择符的样式。
- 继承:继承自父元素的样式。
如果多个规则具有相同的优先级,则后面出现的规则将覆盖先前的规则。如果两个规则具有相同的选择器和优先级,则样式表中靠后的规则将覆盖先前的规则。
问题22:判断对象是否为数组有哪几种方法?
1.Object.prototype.toString.call() (所有类型都可以用来判断)
Object.prototype.toString.call({})
2.instanceof方法 (判断Array.prototype是否在该对象的原型链__proto__中)
console.log([] instanceof Array); //true
3.constructor方法 (判断该对象的构造函数是否是Array)
console.log([].constructor === Array); // true
4.Array.isArray() (数组自带的判断方法)
console.log(Array.isArray([])); // true
问题23:typeof与instanceof的区别
typeof与instanceof都是判断数据类型的方法,区别如下:
- 返回值:typeof会返回一个变量的基本类型,instanceof返回的是一个布尔值
- instanceof:instanceof 可以准确地判断复杂引用数据类型,但是不能正确判断基础数据类型
- typeof:typeof 也存在弊端,它虽然可以判断基础数据类型(null 除外),但是引用数据类型中,除了function 类型以外,其他的也无法判断
问题24:为什么要初始化CSS?
初始化CSS的目的是为了确保在不同浏览器和环境下,网页的初始样式表现一致,并消除浏览器默认样式带来的不确定性。
浏览器对HTML元素有其自己的默认样式,这些默认样式可能会导致不同浏览器之间呈现的差异。通过初始化CSS,可以将所有元素的样式重置为统一的基准状态,然后再根据设计需要逐个定义样式。
以下是一些常见的原因和好处:
- 重置样式:初始化CSS可以重置所有元素的默认样式,确保它们在各种浏览器中具有一致的外观。
- 避免浏览器差异:不同浏览器对某些元素的默认样式存在差异,例如边距、字体大小等。通过初始化CSS,可以消除这些差异,使页面在各种浏览器中更加一致。
- 提供可预测性:通过显式地定义元素的初始样式,可以增加代码的可预测性和可维护性。开发者可以从一个已知的起点开始,而不必依赖浏览器的默认样式。
- 自定义设计:初始化CSS为开发者提供了一个干净的起点,可以更容易地根据设计需求自定义样式。而不必考虑浏览器默认样式对设计的影响。
总之,初始化CSS有助于消除浏览器默认样式的差异,并提供一个可预测和一致的样式基准,使开发者能够更好地控制网页的外观和行为。
问题25:什么是BFC?如何触发?有何特点?如何解决margin塌陷?
1.BFC本质上是开启一个独立的布局空间。让内部元素不影响外部元素的布局,同时外部元素也无法影响内部元素
2.BFC触发条件
- IE下为 Layout,可通过 zoom:1 触发
- 根元素
- position !=(static|relative),比如可以为absolute/fixed
- display: inline-block / table/table-cell/table-caption
- float != none
- ovevflow != visible
3.有何特点
BFC规则
- 属于同一个 BFC的两个相邻 Box 垂直排列,一个接一个地放置
- 属于同一个 BFC 的两个相邻 Box 的 margin 会发生重叠。Box垂直方向的距离由margin决定,你要避免相邻块元素外边距重叠的话,那你就要避免产生BFC
- BFC 中子元素的 margin box 的左边,与包含块(BFC)border box的左边相接(子元素 absolute 除外)
- BFC的区域不会与 float 的元素区域重叠
- BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此计算 BFC的高度时,浮动子元素也参与计算
- 文字层不会被浮动层覆盖,环绕于周围
4.通过BFC可以解决以下问题
- .阻止margin重叠
- 可以保护浮动元素,清楚内部浮动(清除浮动的原理是两个div都位于同一个 BFC 区域之中)
- 自适应两栏布局
- 可以阻止元素被浮动元素覆盖
问题26:什么是rem布局?
1.在HTML根元素中设置font-size大小,rem是相对单位,根据设置的fon-size的大小而定例如在font-size 设置为14px,则1rem就是14px
2.rem 布局就是让页面中的元素可以做到自适应,当设备大小发生改变时,等比例适配当前的设备。页面元素使用rem 做尺寸单位
问题27:如何实现响应式布局?
1、媒体查询,根据视口尺寸定义不同样式
2、流式布局,利用百分比做单位
3,弹性盒,网格系统布局
4、图片响应式:使用 CSS和JavaScript技术使图像根据屏幕尺寸自动调整大小,并提供高分辨率版本以适应屏幕。
5、em、rem 适配,其中rem需要配合is 当视口查化时动态改变 html元毒的字体大小
问题28:link 标签和 import 标签的区别?
1、加载顺序和性能:当浏览器解析 HTML 文档时,link 标签会立即加载并同时请求外部资源,而@import 会在 HTML文档加载完毕后加载外部样式表。这意味着使用 link 可以更快地加载并目不会阻赛页面显示,而@import可能导致页面加载速度变慢。
2、兼容性: link 标签是 HTML标签,可以在 HTML和XHTML中使用,并且受到所有浏览器的支持。而@impot是 CSS 规则,只能在 CSS 文件中使用,目在些较旧的浏览器中可能不被完全支持。
3、功能性: link标签可以引入不同类型的瓷源,包括 CSSJavaScript、图像等,而@impot 只能引入 CSS 文件
4、权重: 在 CSS中,通过@import 引入的样式表的权重较低,会被其他样式表中的规则所覆盖。而 link引入的样式表没有此限制,其权重与页面中的其他样式表相同。
问题29:transition 和 animation 的区别?
Transition(过渡)和Animation(动画)是用于为元素添加动态效果的CSS属性。它们之间的主要区别如下:
Transition(过渡):
- Transition用于在元素从一种状态转换到另一种状态时应用平滑的过渡效果。
- 过渡通常通过改变元素的某些属性(如颜色、大小、位置等)来触发,并在一定的时间内逐渐过渡到新的属性值。
- 过渡可以由多个属性同时进行,每个属性可以具有不同的过渡时间和过渡函数。
Animation(动画):
- Animation用于创建更复杂的动画效果,可以定义多个关键帧,控制元素在不同时间点上的不同样式。
- 动画通过在关键帧之间进行插值来实现平滑的过渡效果。
- 动画使用@keyframes规则来定义关键帧,指定元素在不同百分比或关键帧名称处的样式。
- 可以通过指定动画持续时间、重复次数、暂停、播放状态等参数来控制动画的执行方式。
简而言之,Transition用于实现基本的属性过渡效果,而Animation则更适合创建更复杂和精细控制的动画效果。 Transition对单一属性的过渡较为方便,而Animation对于多个属性和复杂动画序列的控制更为灵活。
问题30:变量提升是什么? 与函数提升的区别?
1.变量提升就是当我们在一个作用域定义变量的时候,该变量的声明会被提升到作用域的顶端,比如用var声明变量的时候就可以造成变量提升,以致于我们可以在变量声明之前访问到该变量只不过这时候访问的变量值为undefined.
2.函数提升和变量提升类似,只不过函数提升优先级要高于变量提升,因为在js中函数是一等公民,优先级很高
问题31:for in 与 for of的区别
- for in 与 for of都可遍历数组对象,for in可以遍历对象的所有可枚举的属性,包括其原型链上的属性,for of 只遍历对象自身的可枚举属性,不会遍历原型链上的属性。
- for in 与 for of都可遍历数组:for in循环可以遍历数组的索引,for of 循环只能遍历数组的值
- 性能:在遍历大型数组时,for of比 for in更高效,因为for in 循环遍历过程中需要额外的查找属性在原型链上的位置。
- 使用场景:通常遍历对象的可枚举数学时,用for in 循环 , 而在需要遍历数组的元素时用for of循环
//for in 遍历对象
var obj = {a:1, b:2, c:3}
for (var prop in obj) {
console.log(`${prop}:${obj[prop]}`) // 输出对象自身的属性及其对应的值
}
//输出结果a:1, b:2, c:3
//for of 遍历对象
var obj = {a: 1, b: 2, c: 3};
for (let [prop, value] of Object.entries(obj)) {
console.log(prop + ": " + value); // 输出对象自身的属性及其对应的值
}
//输出结果a:1, b:2, c:3
//for in 遍历数组
var obj = [1,2,3,4]
for (let item in obj) {
console.log(item)
}
//输出结果0,1,2,3
//for of 遍历数组
var obj = [1,2,3,4]
for (let item of obj){
console.log(item)
}
//输出结果1,2,3,4
问题32:隐式转换
- "64" - 4 = 60 解析:当做减法运算时会将字符串转为数字进行计算
- "64" + 4 = "644" 解析:当做加法运算时会将数字转换为字符串进行拼接
- NaN*4 = NaN 解析:NaN与任何数计算都是NaN
问题33:typeof(null)与typeof(undefined)
在JavaScript中, typeof null 的结果是 & [#34;object&#]34; ,而 typeof undefined 的结果是 & [#34;undefined&#]34; 。这种行为被认为是 JavaScript 的设计缺陷之一,因为 null 被看作是一个特殊的对象类型,但实际上它应该被视为一个独立的原始值。而 undefined 则表示一个变量未定义或者一个属性不存在。因此,虽然 null 和 undefined 都代表缺失的值,但它们在类型上是不同的。
问题34:属于flex简写的属性有哪些?
- flex-grow:定义项目相对于其余弹性项目的增长量,默认值为1
- flex-shrink:定义项目相对于其余弹性项目的收缩量,默认值为0
- flex-basis:定义项目在分配多余空间之前的初始大小,默认值为auto
- 所以flex 默认值为:flex: 1 0 auto;
问题35: 下列哪项不属于DOM查找节点的属性
A node.firstChild
B. node.children
C. node.previousSibling
D.node.siblings
选项 D. 不属于 DOM 查找节点的属性。
在 DOM 中,可以使用以下属性来查找节点:
A. :返回指定节点的第一个子节点。
B. :返回指定节点的所有子元素节点(排除文本节点和注释节点)。
C. :返回指定节点的前一个兄弟节点。
D. 并不是 DOM 标准中定义的属性。因此,选项 D 不属于 DOM 查找节点的属性。
问题36: split,splice,slice的区别
方法是用于提取字符串中的一部分,并返回一个新的字符串,原字符串不会被修改。它接受两个参数:起始索引和结束索引(可选)。返回的子字符串包括起始索引处的字符,但不包括结束索引处的字符。
示例:
const str = "Hello, world!";
const slicedStr = str.slice(0, 5);
console.log(slicedStr); // 输出: "Hello"
方法是将一个字符串拆分成一个字符串数组,根据指定的分隔符进行分割。它接受一个参数:分隔符。返回的是一个字符串数组,其中的元素是根据分隔符分割后的子字符串。 示例:
const str = "apple,banana,orange";
const splittedArr = str.split(",");
console.log(splittedArr); // 输出: ["apple", "banana", "orange"]
splice()方法用于数组操作。可以在任何位置添加或删除数组的元素。语法:。其中, 是开始更改数组的位置, 是从 index 位置开始要删除的元素数量, 是要添加到数组的新元素。
array.splice(index, deleteCount, item1, ..., itemX)
参数:
index(必需):开始更改数组的位置(即开始插入、删除或替换的位置)。如果索引值大于数组的长度,则从数组的末尾开始添加元素。如果是负值,则表示从数组末尾倒数的位置开始。
deleteCount(可选):从 index 位置开始要删除的元素数量。如果省略或者值大于 array.length - index,那么从 index 开始所有的元素都会被删除。
item1, ..., itemX(可选):要添加到数组的新元素,从 index 位置开始添加。如果不指定,则 splice() 只删除数组元素。
返回值:
返回一个由被删除的元素组成的新数组。如果没有删除任何元素,则返回一个空数组。
删除元素:
let fruits = ["apple", "banana", "cherry", "durian"];
fruits.splice(2, 1); // 从索引2开始,删除1个元素
console.log(fruits); // 输出: ["apple", "banana", "durian"]
插入元素:
let fruits = ["apple", "banana", "cherry", "durian"];
fruits.splice(2, 0, "mango", "melon"); // 在索引2的位置插入"mango"和"melon"
console.log(fruits); // 输出: ["apple", "banana", "mango", "melon", "cherry", "durian"]
替换元素:
let fruits = ["apple", "banana", "cherry", "durian"];
fruits.splice(2, 1, "mango"); // 从索引2开始,删除1个元素并插入"mango"
console.log(fruits); // 输出: ["apple", "banana", "mango", "durian"]
注意:splice() 方法会直接修改原数组,而不是返回一个新的数组。
总结:
- split 主要用于将字符串拆分成子字符串数组。
- splice 主要用于修改数组内容,可以删除、替换或添加元素。
- slice 主要用于获取数组或字符串的子集,但不修改原数组或字符串。
问题37:箭头函数的特点
- 简洁的语法:箭头函数使用 "=>"(箭头符号) 来定义函数,省略了传统函数声明中的 function 关键字和大括号。例如:(param1, param2) => expression。
- 没有自己的 this 值:箭头函数没有独立的 this 值,它会继承父级作用域中的 this 值。这意味着在箭头函数内部使用的 this 是在定义函数时确定的,而不是在运行时确定的。
- 没有 arguments 对象:箭头函数也没有自己的 arguments 对象,但可以通过剩余参数(rest parameters)来获取函数的所有参数。
- 自动绑定上下文:由于箭头函数继承父级作用域中的 this 值,它通常适用于回调函数、事件处理程序等需要保持上下文一致的场景。
- 不能作为构造函数:箭头函数不支持使用 new 关键字进行实例化,因此不能用作构造函数创建对象。
示例1:
const sum = (...numbers) => {
let total = 0;
for (let number of numbers) {
total += number;
}
return total;
};
console.log(sum(1, 2, 3)); // 输出: 6
console.log(sum(4, 5, 6, 7)); // 输出: 22
console.log(sum(10)); // 输出: 10
示例2:
(function(){
var x = 5
var o = {
x:10,
doIt: function doIt(){
var x = 20;
setTimeout(()=>{console.log(this.x)},0);
}
};
console.log(o.doIt())
})()
以上代码打印结果,依次是:
undefined
10
解释:
在代码中,有一个立即执行的匿名函数。在该函数内部定义了变量 x 和对象 o。o 对象包含属性 x 和方法 doIt。doIt 方法内部再定义了一个变量 x,并使用 setTimeout 来异步执行一个回调函数。
当调用 o.doIt() 时,会触发回调函数的执行。由于 setTimeout 的回调函数使用了箭头函数,箭头函数内部的 this 绑定的是父级作用域的 this,即对象 o。因此,this.x 打印的是 o 对象的属性 x 的值,即为 10。
在 o.doIt() 的最后一行使用了 console.log(o.doIt()),由于 doIt 方法没有返回值,因此打印的结果为 undefined。
问题38:Js中精度计算问题
JS中执行0.1+0.2的结果是: ****A.0.3 ****B . 随机 ****C . 约等于0.3 D . 约等于04
在 JavaScript 中,执行 0.1 + 0.2 的结果是 0.30000000000000004。
这看起来像是一个精度问题,涉及到浮点数的表示和计算。由于 JavaScript 使用 IEEE 754 标准表示浮点数,而浮点数的二进制表示有时无法精确地表示十进制小数。
因此,在某些情况下,进行浮点数计算可能会导致舍入误差或微小的精度损失。这就是为什么在执行 0.1 + 0.2 时,结果不是精确的 0.3。
为了避免这种问题,可以使用适当的方法来处理和比较浮点数,例如四舍五入、设置小数位数或使用专门的库来处理浮点数运算。
故答案:C
问题39:null与undefined的判断
以下哪个表达式的结果为true: ****A. null instanceof Object ****B. null === undefined ****C . null == undefined ****D.NaN == NaN
答案:C. null == undefined
在JavaScript中,null和undefined是特殊的值,它们在比较相等性时是相等的。使用"=="运算符比较null和undefined时,会返回true。其他选项中的表达式结果为false:
A. null instanceof Object:null不是Object的实例,所以返回false。
B. null === undefined:严格相等运算符"==="要求两个操作数的值和类型都相等,null和undefined的类型不同,所以返回false。
D. NaN == NaN:NaN(Not a Number)是一个特殊的数值,它与任何值(包括自身)进行相等性比较都会返回false,所以NaN == NaN也返回false。
问题40: new 关键字考察
var name = 'Jay'
function Person(name){
this.name = name;
}
var a = Person('Tom')
console.log(name)
var b = new Person('Jack')
console.log(b.name)
//上述代码依次打印的结果为 Tom, Jack
// var a = Person('Tom') 的形式非实例构建仅为函数调用,此时this指向为全局的this,此处为浏览器环境window对象,故this.name为全局中定义的name,所以第一个console.log(name) 将输出Tom,此时a变量为undefined,因为person并没返回具体内容,所以a为undefined
// var b = new Person('Jack') 为构建实例对象,此处this.name为内部的this指向故输出b.name 为Jack
问题41:Javascript中的继承方法有哪些
-
原型链继承:
- 通过将子类的原型对象指向父类的实例来实现继承。
- 子类可以继承父类的属性和方法。
- 缺点是所有子类实例共享父类的属性,无法在子类实例中进行独立修改。
-
构造函数继承(借用构造函数):
- 在子类构造函数内部使用 call 或 apply 方法调用父类构造函数,实现属性的继承。
- 子类可以拥有独立的属性副本。
- 缺点是无法继承父类的原型上的方法。
-
组合继承(原型链 + 构造函数继承):
- 结合原型链继承和构造函数继承的方式。
- 通过调用父类构造函数来继承属性,然后将父类的原型对象赋值给子类的原型对象,实现方法的继承。
- 可以继承父类的属性和方法,并且每个子类实例都具有独立的属性。
-
原型式继承:
- 使用一个过渡对象作为中介,创建新的对象并将其原型设置为父对象。
- 通过 Object.create() 方法实现。
- 可以继承父对象的属性,但是共享相同的原型。
-
寄生式继承:
- 在原型式继承的基础上增加了对新对象的扩展操作。
- 创建一个包装函数,在其中封装继承过程和对新对象的扩展操作。
- 返回这个包装函数创建的新对象。
- 可以实现一些特定的继承需求,但容易造成对象识别问题。
-
寄生组合式继承(最常用):
- 结合了寄生式继承和组合继承的方式。
- 使用一个过渡函数来创建子类的原型对象,避免调用父类构造函数时重复执行父类的初始化逻辑。
- 继承父类的属性和方法,并且每个子类实例都具有独立的属性
以上是一些常见的继承方法,每种方法都有其优缺点,选择适合具体场景的继承方法很重要。此外,ES6 引入的 和 关键字也提供了更简洁的语法来实现面向对象的继承。
问题42:可以用什么方法接收不定数量的参数
1.使用 对象:
- 在函数内部可以使用 arguments 对象来获取传递给函数的所有参数。
- arguments 是一个特殊的类数组对象,可以通过索引访问参数。
- 这是个伪数组
2.使用剩余参数语法(Rest Parameters):
- 剩余参数语法允许将不定数量的参数收集到一个数组中。
- 在函数声明时,使用 … 后跟参数名来指示接收的参数为剩余参数,它会自动将传递的参数打包成一个数组。
问题43:reduce的作用
是 JavaScript 数组对象的方法之一,它用于对数组中的所有元素执行一个归约(reduction)操作,将数组元素按照指定规则进行累积计算,并返回最终的结果。
方法接受两个参数:
- 回调函数(callback function):用来定义每一步的操作逻辑。
- 初始值(initial value):可选参数,表示归约操作的初始值。
回调函数可以接受四个参数:
- 累加器(accumulator):累积计算的结果。
- 当前值(current value):正在处理的当前元素。
- 当前索引(current index):可选参数,正在处理的当前元素的索引。
- 原数组(array):可选参数,调用 reduce() 的原数组对象。
回调函数通过逐个处理数组元素,将每次操作的结果累积到累加器中,最后返回最终的累积结果。整个过程可以理解为一个迭代的累积过程。
以下是 方法的一些常见应用场景:
- 求和或累加:将数组中的所有元素相加得到总和。
- 求平均值:计算数组中所有元素的平均值。
- 求最大值或最小值:找出数组中的最大值或最小值。
- 数组转换:根据数组中的元素生成一个新的数组或对象。
示例:
const numbers = [1, 2, 3, 4, 5];
// 求和
const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
console.log(sum); // 输出:15
// 求平均值
const average = numbers.reduce((accumulator, currentValue, index, array) => {
accumulator += currentValue;
if (index === array.length - 1) {
return accumulator / array.length;
} else {
return accumulator;
}
}, 0);
console.log(average); // 输出:3
// 求最大值
const max = numbers.reduce((accumulator, currentValue) => Math.max(accumulator, currentValue), -Infinity);
console.log(max); // 输出:5
// 数组转换
const doubledNumbers = numbers.reduce((accumulator, currentValue) => {
accumulator.push(currentValue * 2);
return accumulator;
}, []);
console.log(doubledNumbers); // 输出:[2, 4, 6, 8, 10]
问题44:JS中的宏任务和微任务有哪些?
- 宏任务(Macro-tasks):
- 整体代码script,包括所有的函数调用。
- setTimeout、setInterval、setImmediate、I/O、UI rendering等。
- 微任务(Micro-tasks):
- Promise的回调。
- process.nextTick。
- MutationObserver。
在事件执行中宏任务的优先级比微任务的优先级要低
问题45:HTTP中的强制缓存
HTTP强制缓存是一种机制,用于在浏览器和服务器之间减少网络请求以提高性能。通过设置响应头中的缓存控制字段,服务器可以指示浏览器在一段时间内直接使用本地缓存而不发送请求到服务器。
当浏览器第一次请求一个资源时,服务器可以通过设置响应头中的或字段来指定该资源的缓存策略。如果设置了合适的缓存策略,浏览器接收到响应后会将资源保存在本地缓存中,并在下一次请求时检查缓存是否有效。
如果缓存有效(根据规则判断),浏览器会直接从本地缓存中加载资源,而不发送请求到服务器,这样可以减少网络延迟和带宽消耗,提高页面加载速度。只有当缓存过期或被明确指示需要重新验证时,浏览器才会发送请求到服务器进行资源的更新。需要注意的是,虽然强制缓存可以提高网页的加载速度和性能,但同时也可能会带来一些问题,如信息的不一致性等。因此,在使用强制缓存时需要注意及时更新缓存数据,以保证信息的准确性。
常见的缓存控制字段包括:
- Cache-Control: max-age:指定资源在本地缓存中的有效时间,以秒为单位。
- Expires:指定资源的过期时间,是一个具体的日期和时间。
- Cache-Control: no-cache:表示资源需要重新验证,即每次都向服务器发送请求进行确认。
- Cache-Control: no-store:表示不缓存该资源,每次都必须向服务器发送请求获取最新的版本。
通过适当配置缓存策略,可以有效地减少对服务器的请求,提升前端性能和用户体验。
问题46:fetch与ajax的区别
1.API设计:ajax基于xmlHttpRequest对象来发送和接收数据,通过函数回调处理异步请;
fetch基于webapi实现,是一种新的webapi,通过promise来处理异步请求
2.兼容性:ajax基于xmlHttpRequest对象进行封装,兼容绝大多数旧版的浏览器;fetch是一种新的webapi,兼容性相比ajax要弱
3.写法:fetch比起ajax的写法要更为简洁,ajax需要配置多项信息来进行请求的设置
4.错误处理:fetch只对网络请求报错,对400、500都当做成功的请求,而ajax不会
5.跨域请求:Fetch对跨域请求有更严格的限制,因为它遵循了同源策略。如果需要进行跨域请求,需要在服务器端设置适当的CORS头以允许跨域请求。然而,Ajax也受到同源策略的限制,但可以通过JSONP、代理服务器等技术来进行跨域请求。另外:Fetch跨域的时候默认不会带cookie 需要手动的指定 credentials:&[#39;include&#]39;
6.流式传输:ajax不支持流式传输,fetch支持流式传输,可通过调用response.body.getReader()方法创建数据读取器,封装一个read方法进行读取,逐步读取出数据内容,进行流式传输
问题47:https的请求过程
1.客户端发起请求,服务端接收后发送一个CA(公钥)的证书给客户端
2.客户端接收证书后进行加密生成随机对称秘钥并使用公钥加密,如有问题会提示风险
3. 客户端 加密后的 对称秘钥 ,发送给【服务器】,作为接下来请求的秘钥
4.服务端用自己的 公钥 解密得到对称秘钥
5.客户端使用 随机对称秘钥 进行解密数据
6.【 客户端 】【 服务端 】使用该秘钥进行通信
总结:在HTTPS的过程中,会通过加密的方式进行传输,主要分为两类:
对称加密【 对称加密就是加密和解密都用同一个密钥. 】;
非对称加密【有两个密钥,pub是公钥用来加密或解密,pri是密钥用来解密或加密】
问题48:响应式布局与自适应布局的区别
- 布局方式:响应式布局使用CSS媒体查询和流式布局来根据设备屏幕尺寸和特性调整元素的大小和位置。自适应布局则是针对特定的设备尺寸创建多个固定布局,在不同设备上加载相应的布局。
- 预设断点:在响应式布局中,通过使用媒体查询,可以设置多个预设断点(breakpoints),在这些断点处进行布局的调整。而自适应布局则会为特定的设备尺寸和屏幕宽度创建独立的布局。
- 动态调整:响应式布局可以动态地根据用户的屏幕尺寸和设备特性进行调整,使得页面在各种设备上都能提供最佳的用户体验。自适应布局则是在设计阶段就确定了特定的布局,不会根据用户设备的实际情况进行动态调整。
- 灵活性:响应式布局更加灵活,可以适应不同尺寸的屏幕,包括桌面、平板和移动设备等。自适应布局则需要为每个目标设备创建特定的布局,因此在新增设备尺寸时可能需要进行额外的适配和开发工作。
总的来说,响应式布局强调根据屏幕尺寸和设备特性动态地调整布局,以提供最佳的用户体验。自适应布局更注重针对特定设备尺寸创建固定布局,适用于已知的设备尺寸范围。选择使用响应式布局还是自适应布局取决于项目需求、设计复杂度、预期的用户体验和可维护性等因素。
问题49:什么是重绘,什么是回流,两者有何区别?如何减少?
1.重绘:重绘是指当元素发送的样式发送改变时,其布局没有发送改变,则浏览器会对元素进行重绘,重绘通常会发生在背景颜色,文字颜色,或字体大小等样式发送改变时,其布局没发生改变,则会触发重绘现场。****
2.回流:回流是指当元素的布局属性发送改变,需要重新计算元素在页面中的布局位置时,浏览器重新进行布局的过程。例如:修改元素的宽度,高度,位置等。
3.重绘与回流的差异:回流的成本比重绘高得多,因为它涉及重新计算元素的几何属性和页面布局。而重绘只需要重新绘制已计算好的元素样式
4.如何减少:
- 使用CSS动画代替javascript 动画 : css动画利用 GPU加速 ,在性能方面通常比javascript动画更高效,使用css的transform和opacity属性来创建动画效果,而不是改变元素的布局属性,如宽度,高度等。
- 使用translate3d开启硬件加 速 : 将元素的位移属性设置为translate3d(0,0,0),可以 强制使用GPU加速 。这有助于避免回流,并提高动画的流畅度。
- 避免频繁操作影响布局的样式属 性 : 当需要对元素进行多次样式修改时,可以考虑将这些修改 合并为一次操作 。通过添加/移除 CSS类 来一次性改变多个样式属性,而不是逐个修改。
- 使用requestAnimationFram e : 通过使用requestAnimationFrame方法调度动画帧,可以确保动画在 浏览器的重绘周期 内执行,从而避免不必要的回流。这种方式可确保动画在最佳时间点进行渲染。
- 使用文档片段(Document Fragment): 当需要向DOM 中插入大量新元素时,可以先将这些元素添加到文档片段中,然后再将整个文档片段一次性插入到 DOM 中。这样做可以减少回流次数。(虚拟 dom vue 的方式)
- 让元素脱离文档流: display:absolute/display:fixed/float:left, (只是减少回流,不是避免回流。)
- 使用visibility:hidden 替代 display:none:visibility: hidden 不会触发回流因为元素仍然占据空间,只是不可见。而使用 display: none 则会将元素从染树中移除,引起回流。
问题50:请说说css层叠规则,优先级计算
1.CSS的层叠规则(Cascading Rules) 是CSS中非常重要的一部分,它定义了当多个样式规则作用于同一个元素时,如何决定最终应用哪一条规则。层叠规则根据特定的优先级和顺序来计算,具体规则如下:
- 选择器的特殊性(specificity):选择器的特殊性用于衡量选择器优先级的一个指标。选择器越具体,其特殊性越高。特殊性由四个部分组成,分别是内联样式、ID选择器、类选择器和标签选择器。特殊性高的选择器优先级更高。
- 选择器的顺序:当多个规则具有相同的特殊性时,层叠规则会根据选择器的顺序来决定应用哪个规则。后面出现的规则会覆盖前面出现的规则。
除了上述的层叠规则外,还有一些其他属性也会影响元素的层叠顺序,例如z-index。z-index用于设置元素在z轴上的位置,其值可正可负。数值越大,元素在z轴上的位置就越靠前,就会遮挡元素的z-index比当前元素的z-index的值小的元素。
2.优先级计算
- 内联样式具有最高的优先级。例如,直接在HTML元素中使用style属性的样式。
- ID选择器,如 #navbar 优先级次之。每当一个ID选择器在样式规则中使用,它的优先级都会增加。
- 类选择器(如 .myClass)、属性选择器(如 [type="text"])和伪类(如 :hover)的优先级再次之。
- 标签选择器(如 div)、伪元素选择器(如 ::before、::after)拥有最低的优先级。
特殊性和优先级
选择器的特殊性是计算优先级的一种方式。特殊性是一个数值,由四个部分组成:a、b、c、d,分别对应上面提到的四种选择器类型。每种类型的选择器都会增加相应部分的数值。比如,ID选择器会增加b的值,类选择器会增加c的值。
当两个选择器的特殊性相同时,位于CSS最后的规则会覆盖前面的规则。同时,使用 可以覆盖其他任何声明,但是过度使用 会导致代码难以维护和理解,因此建议尽量少用。
最佳实践
为了维护代码的清晰和高效,建议:
- 尽量避免使用内联样式,因为它们具有高优先级并可能导致混乱。
- 慎重使用ID选择器,因为它们会增加特殊性并可能导致不必要的复杂性。
- 使用类选择器和标签选择器进行更通用的样式设置,这样可以在多个元素之间共享样式,并且它们的特殊性相对较低,更容易进行覆盖和管理。
问题51:为什么for of 不能遍历对象
因为for…of只能遍历可迭代(iterator)对象,(如数组、字符串、Map、Set等)的值,然而,对象(特指普通的键值对对象,如JavaScript中的对象字面量)并没有内在的迭代顺序。对象的属性并没有按照特定的顺序进行排列,因此直接使用for…of来遍历对象并没有明确的语义。
问题52:promise有几种状态
Promise对象有三种状态:pending(进行中) 、fulfilled(已成功)和rejected(已失败) 。一旦Promise对象从pending状态变成fulfilled或rejected状态,就不会再改变状态。
问题53:bind与箭头函数的区别
方法和箭头函数在JavaScript中都可以用来改变函数的上下文,但它们之间有几个关键的区别:
- 函数创建:bind()方法创建一个新的函数,而箭头函数不会产生新的函数。
- this 值的绑定:箭头函数在定义时就绑定了this值,且不可更改。而使用bind()方法绑定的函数,其this值可以动态改变。
- 参数列表:bind()方法可以设置参数列表,而箭头函数不可以。
- 使用场景:由于箭头函数绑定了定义时的this值,它在某些场景中,如事件处理、定时器回调等,更为方便。然而,如果你需要更灵活或动态的this上下文,bind()可能会更合适。
- 函数名:bind()方法创建的函数有函数名,而箭头函数没有函数名(匿名函数)。
- 返回值:箭头函数没有自己的this,arguments,super或new.target,通常这些值会从包围的函数中“继承”。而使用bind()方法可以创建具有自己独立this和参数列表的函数。
问题54:js中的隐式类型转换条件有哪些
- 逻辑运算符:&&、||、!
- 运算符:+、-、*、/
- 关系操作符:>、<、<=、>=
- 相等运行算符:==
- if / while 条件
问题55:== 的隐式类型转换
- 类型相同,则无需进行类型转换。
- 如果其中一个操作数是 null 或者 undefined ,那么另一个操作数必须是 null 或者 undefined 才会返回 true ,否则返回 false 。
- 如果其中一个操作数是 Symbol ,那么返回 false。
- 如果两个操作数都为 string 和 number 类型,那就就将字符串转换为 number。
- 如果一个操作数是 boolean 类型,那么转换成 number。
- 如果一个操作数为 object ,且另一方为 string、number、或者 Symbol ,就会把object 转换为原始类型再进行判断。
null == undefined; // true
null == 0; // false
'' == null; // false
'' == 0; // true
'123' == 123; // true
0 == false; // true
1 == true; // true
var a = {
value: 0,
valueOf: function(){
this.value++;
return this.value;
}
}
console.log(a==1 && a==2 && a==3); // true
// 对象隐式转换为原始类型,调用对象的valueOf方法返回值,此对象的valueOf()返回一个自定义自增方法,每调用一次增加1,最后结果为3.
// a==1时,a.valueOf()返回1,所以1==1为true,此时a.value==1;
// a==2时,a.valueOf()返回2,所以2==2为true,此时a.value==2;
// a==3时,a.valueOf()返回3,所以3==3为true,此时a.value==3;
// 这时,如果再判断a==1&&a==2&&a==3,则返回false,因为此时的a已经为4了
console.log(a==1 && a==2 && a==3); // false
问题56:+ 的隐式类型转换
- +号操作符,不仅可以用作数字相加,还可以用作字符串拼接。
- 如果其中一个操作数是 string,另外一个操作数是 undefined、null 或者 boolean,则调用 toString() 方法进行字符串拼接。
- 如果是纯对象、数组、正则等,则默认调用对象的转换方法,会存在优先级,然后再进行拼接。
- 如果其中有一个是 number ,另外一个是 undefined、null、boolean、number,则会将其转换为数字进行加法运算,对象的情况参考上一条规则。
- 如果其中一个是 string ,一个是 number,则按照字符串规则进行拼接。
1 + 2; // 3
'1' + '2'; // '12'
'1' + 3; // '13' 字符串拼接
'1' + undefined; // '1undefined'
'1' + null; // '1null'
'1' + true; // '1true'
'1' + 1n; // '11' 字符串和BigInt相加,BigInt转换为字符串
1 + undefined; // NaN undefined转换为NaN
1 + null; // 1 null转换为0
1 + true; // 2 true转换为1
1 + 1n; // TypeError: Cannot mix BigInt and other types, use explicit conversion "无法混合BigInt和其他类型,请使用显式转换"
问题57:object 的隐式类型转换
- 如果部署了 [Symbol.toPrimitive] 方法,优先调用再返回;
- 若不存在 [Symbol.toPrimitive] 方法,则调用 valueOf 方法,如果返回基础数据类型,则执行结束;
- 否则调用 toString 方法,如果转换为基础数据类型,则返回;
- 最后,如果都没有返回基础数据类型,则报错。
var obj = {
value: 1,
valueOf(){
console.log('valueOf', 2);
return 2;
},
toString(){
console.log('toString', 3);
return 3;
},
[Symbol.toPrimitive](){
console.log('[Symbol.toPrimitive]', 4);
return 4;
}
}
console.log(obj + 1); // 5
// 调用[Symbol.toPrimitive]方法,获取返回值4,4+1=5
10 + {}; // '10[object Object]'
// {}调用valueOf方法返回自身,然后继续调用toString方法返回字符串'[object Object]',10与其相加得 '10[object Object]'
[1, 2, undefined, 4, 5] + 10; // '1,2,,4,510'
// 数组调用valueOf方法返回数组本身,然后调用toString方法转换为字符串'1,2,,4,5',与10相加得 '1,2,,4,510'
注意:valueOf方法返回值不为原始值时,则继续寻找toString方法执行,获取返回值,执行结束。
var obj = {
value: 1,
valueOf(){
console.log('valueOf', 2);
return {};
},
toString(){
console.log('toString', 3);
return 3;
},
}
console.log(obj + 1); // 4
问题58:什么是内存泄漏?
定义:程序中存在未释放或无法访问的内存的情况。当你的应用程序不再使用某个对象或数据时,如果没有正确释放相关的内存,这些内存将被占用而无法再被其他对象或数据使用,从而导致内存泄漏。
常见的内存泄漏情况包括以下几种:
- 无用的引用:当你创建了一个对象或数据,并将其赋值给一个变量或存储在某个数据结构中,但后续没有正确地释放或移除这些引用,导致对象或数据无法被垃圾回收器回收。
- 定时器和事件监听器:如果你在应用程序中使用了定时器或事件监听器,但忘记在不再需要它们时取消注册或清除,这些定时器或事件监听器将继续占用内存,即使相关的对象已经不再需要。
- 循环引用:当两个或多个对象之间存在相互引用,并且没有被其他对象引用时,这些对象将无法被垃圾回收器回收。这种情况下,你需要注意避免循环引用,或者手动断开引用关系,以便让垃圾回收器正确地释放内存。
为了避免内存泄漏,可以采取以下措施:
- 确保在不再需要对象或数据时将其正确地释放或移除。
- 注意及时取消注册定时器和事件监听器,以避免它们继续占用内存。
- 避免创建循环引用,或者在不再需要时手动断开引用关系。
- 使用开发者工具和内存分析工具来检测和调试内存泄漏问题。
问题59:谈谈你对V8垃圾回收机制的理解?
- 分代回收
- 标记-清除
- 引用计算(V8不存在引用计算)
问题60:谈谈你对SSR的理解?
- 解析定义:服务端渲染(Server-Side Rendering)url->服务器解析->填好数据的页面->返回页面客户端渲染(Client-Side Rendering)url->返回静态页面->构建vue->请求数据->渲染数据
- 结合具体场景分析SSR的优缺点优点:SSR 的优势在于更好的首次加载性能和搜索引擎优化(SEO)能力。由于服务器端生成了完整的 HTML 页面,搜索引擎可以直接获取到页面的内容,提高了页面在搜索结果中的排名和可索引性。另外,由于客户端浏览器只需要进行简单的页面展示,减少了客户端的计算和渲染时间,提升了用户的体验。总结:对SEO友好,首屏渲染更快(LCP:large-content-paint 快)缺点:由于页面的渲染过程发生在服务器端,服务器的负载和响应时间可能增加。另外,与客户端渲染相比,SSR 需要更多的服务器资源和处理能力。因此,在选择使用 SSR 还是客户端渲染时,需要综合考虑项目需求、性能要求和开发成本等因素。缺点:增加服务器压力,白屏时间延长(FP: first-paint 慢)
- 结合自己实际经验给出案例
问题61:JavaScript中的微任务和宏任务分别有哪些?
- 在javascript中,微任务包含:1、“Promise”;2、“Object.observe”;3、“MutationObserver”;4、Node.js环境下的“process.nextTick”;5、“async/await”。
- 在javascript中,宏任务包含:1、setTimeout;2、setInterval;3、I/O;4、UI交互事件;5、postMessage;6、MessageChannel;7、setImmediate(Node.js环境)。
- 微任务概念:microtask,可以理解是在当前 task 执行结束后立即执行的任务。也就是说,在当前task任务后,下一个task之前,在渲染之前。 所以它的响应速度相比setTimeout(setTimeout是task)会更快,因为无需等渲染。也就是说,在某一个macrotask执行完后,就会将在它执行期间产生的所有microtask都执行完毕(在渲染前)。
- 宏任务概念:(macro)task,可以理解是每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)。 浏览器为了能够使得JS内部(macro)task与DOM任务能够有序的执行,会在一个(macro)task执行结束后,在下一个(macro)task 执行开始前,对页面进行重新渲染。
高频面试题题部分到这告别一段尾声了,平时学习过程我们在不断接触一些学习资源2024年转眼就要过去,下面是我前端学习过程收录的一些资源,不能说能面面俱到,但多数都是我有过接触并且得到业界认可的资源,这里做整理和分享,前往点击【学习】获取希望对我和大家都有所帮助,这也是一点小小的愿望。
最后
在准备这些面试题以及研究所涵盖的主题并查看相关资源的时候,相当于又把一些知识做了复习,对于之前没有记住的内容,通过对这些内容的掌握,可以提升你的面试成功通过机率。
最后,感谢你的阅读,祝编程愉快!