iframe优缺点
优点:
- 网页嵌套:通过 iframe,可以将另一个网页嵌入到当前页面中。这对于集成第三方内容(如地图、视频、社交媒体插件等)非常方便,可以在不离开当前页面的情况下展示外部内容。
- 安全隔离:每个 iframe 都有独立的文档对象模型(DOM),这意味着它们之间的操作相互隔离。这可以防止外部网页影响当前页面的样式和脚本。
- 并行、异步加载:iframe 可以并行且异步加载其嵌套的网页,不会阻塞当前页面的渲染和加载。这可以提高页面的整体性能和加载速度。
缺点:
- 性能问题:每个iframe都需要加载渲染,会增加页面加载时间和资源消耗
- SEO 问题:搜索引擎可能无法正确地解析 iframe 内的内容,这可能会影响页面的搜索引擎优化(SEO)。
- 跨域限制:由于浏览器的同源策略限制,嵌套在 iframe 中的网页只能与同域的父页面进行受限的通信。跨域通信需要通过特殊的技术手段(如跨文档消息传递)来实现。
- 响应式问题:在移动设备等小屏幕上,iframe 可能需要滚动或者缩放才能适应屏幕,这可能会导致用户体验下降。
?H5新特性
- 新增选择器 document.querySelector、document.querySelectorAll
- 拖拽释放(Drag and drop) API
- 媒体播放的 video 和 audio
- 本地存储 localStorage 和 sessionStorage
- 离线应用 manifest
- 桌面通知 Notififications
- 语意化标签 article、footer、header、nav、section
- 增强表单控件 calendar、date、time、email、url、search
- 地理位置 Geolocation
- 多任务 webworker
- 全双工通信协议 websocket
- 历史管理 history
- 跨域资源共享(CORS) Access-Control-Allow-Origin
- 页面可见性改变事件 visibilitychange
- 跨窗口通信 PostMessage
- Form Data 对象
- 绘画 canvas
?自定义元素
HTML 提供了一系列内置的元素(如 <div>、<p> 等),而自定义元素则是由开发者自定义的,可以用来扩展 HTML 语义和功能。
在传统的 HTML 中,我们只能使用已定义的标签来创建元素,如 <div>、<p>、<span> 等。但随着 Web 技术的不断发展,HTML5 引入了自定义元素的功能,允许开发者创建自己的标签,并赋予其特定的行为和样式。
CSS
?BFC
带你用最简单的方式理解最全面的BFC哔哩哔哩bilibili
BFC,即块级格式化上下文(Block Formatting Context),是CSS中的一个概念。它是指一个独立的渲染区域,其中的元素按照一定的规则进行布局和渲染。
BFC的主要作用是控制元素在布局上的行为,包括外边距的塌陷、浮动元素对周围元素的影响以及清除浮动等。以下是BFC的一些特性和应用:
- 外边距折叠:在BFC中,垂直方向上的相邻元素的外边距会发生折叠(合并)现象,从而避免了不必要的间距。但在同一个BFC的元素中,父子元素之间的外边距不会发生折叠。
- 清除浮动:BFC可以包裹浮动元素,使其不对其他元素造成影响。通过创建一个新的BFC,可以清除浮动带来的高度塌陷问题。
- 阻止浮动元素重叠:在BFC中,浮动元素会受到BFC边界的限制,从而阻止其与其他元素的重叠。这有助于解决一些浮动布局导致的错位或溢出问题。
- 自适应两栏布局:BFC可以使用浮动或者overflow: hidden;等方式创建,结合盒模型和清除浮动,可以实现一些常见的布局需求,如自适应两栏布局。
创建BFC的方式有多种,包括:
- 给元素添加 float 属性。
- 给元素添加 display: inline-block;、display: table-cell;、display: table-caption; 等属性。
- 给元素添加 overflow 属性为除 visible、clip 之外的值。
总而言之,BFC是一种控制元素布局行为的机制,它可以解决一些常见的布局问题,如外边距塌陷、浮动元素重叠等。了解BFC的使用和原理对于前端开发中复杂布局的实现非常有帮助。
?实现水平居中
- 行内元素:text-align:center
- 块级元素:
- margin: 0 auto(确定宽度的情况下)
- transform + 绝对定位
- display:flex + justify-content:center
- 子display:inline-block + 父text-align:center
- display:table + margin:auto
?实现垂直居中
- 对于文字:设置line-height
- flex + align-items
- transform + 定位
- display:table + verticle-align:middle
?水平垂直居中
① 使用Flexbox:通过将父容器设置为display: flex;,然后使用justify-content: center;和align-items: center;属性来水平和垂直居中子元素。
.parent {
display: flex;
justify-content: center;
align-items: center;
}
② 使用Grid布局:通过将父容器设置为display: grid;,然后使用place-items: center;属性来水平和垂直居中子元素。
.parent {
display: grid;
place-items: center;
}
③ 使用绝对定位和transform属性:将子元素的位置设置为绝对定位,并且使用top: 50%;和left: 50%;将元素的左上角移动到父容器的中心,然后使用transform: translate(-50%, -50%);将元素的中心与父容器的中心对齐。
.parent {
position: relative;
}
.child {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
④ 使用表格布局:将父容器设置为display: table;,然后将子元素设置为display: table-cell;和vertical-align: middle;以实现水平和垂直居中。(文本水平垂直居中)
.parent {
display: table;
}
.child {
display: table-cell;
vertical-align: middle;
text-align: center;
}
⑤父flex + 子margin:auto
⑥绝对定位均设0 + margin:auto
?圣杯布局&双飞翼布局
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
/*
思路:
1、容器宽度设为100%来填充整个页面宽度
2、为左右中三者设置float:left,以此可以显示在一行
3、为left和right部分设置负的margin值,让它们移到center行中,但注意,现在的left和right将center左右部分覆盖了
4、解决覆盖问题,有两种方式:
(1) 为center左右设置内边距,但这时会将页面撑大,所以我们要将内边距合并到100%的宽度里,这里我们想到了用怪异盒模型
(2) 用定位的方式解决,为容器wrapper设置内边距,然后为left和right设置相对定位,并为其移动到左右两边
?
扩展:
与双飞翼布局的区别?
圣杯布局通过设置负边距与容器内边距来使三栏显示于一列,而双飞翼布局则为中间列额外包裹添加外边距来防止左右两侧遮挡,两者实现效果相似
*/
.item {
height: 100px;
/* 这里设置浮动是为了让三者可以并排显示 */
float: left;
}
?
.center {
/* 为了防止左右两块与center重叠,我们要为其设置内边距 */
/* padding: 0 300px; */
/* 方式一:将盒子模型设为怪异盒模型,防止padding将左右顶开 */
/* box-sizing: border-box; */
}
?
.center {
width: 100%;
background-color: green;
}
?
.wrap {
padding:0 300px;
}
?
.left {
width: 300px;
background-color: yellow;
/* 为了让left块和center在同一行,需要用负的margin值让它向回移动 */
margin-left: -100%;
/* 方式二:设relative */
position: relative;
left: -300px;
}
?
.right {
width: 300px;
background-color: red;
/* 为了让right块和center在同一行,需要用负的margin值让它向回移动 */
margin-left: -300px;
/* 方式二:设relative */
position: relative;
right: -300px;
}
</style>
</head>
<body>
<!-- 圣杯布局分左右中三部分 -->
<div class="wrap">
<!-- 为了让中间内容先渲染,写在上面 -->
<div class="item center"></div>
<div class="item left"></div>
<div class="item right"></div>
</div>
</body>
</html>
clearfix
.clearfix::after {
content: "";
clear: both;
display: block;
}
clearfix通过为父元素添加伪元素,并为其添加清除浮动效果来解决浮动元素引起的高度塌陷问题,这里设置display:table主要有两方面作用:
- 生成独立的BFC,从而避免浮动元素对其周围其他元素的影响
- 表格布局有自动调整单元格高度的特性,即表格单元格会根据内容自动撑开高度。通过将伪元素设置为 display: table,可以使其撑开父元素的高度,从而让父元素正确地包含浮动元素。
?flex
对于轴线:
- 设定主轴方向
- flex-direction:
- row(default)
- row-reverse
- column
- column-reverse
- 是否换行
- flex-wrap:
- nowrap(default)
- wrap
- wrap-reverse
- 前两者可以合并
- flex-flow:
- row nowrap(default)
- 主轴对齐方式
- justify-content:
- flex-start(default) 左对齐
- flex-end 右对齐
- center
- space-between 两端对齐,中间间隔相同
- space-around 每个成员两侧的间隔相等
- space-evenly 均匀分布,两侧靠边间隔与元素之间间隔也相同
- 成员在交叉轴(主轴外的另一个轴)的对齐方式,默认可以理解为y轴
- align-items:
- flex-start 交叉轴起点对齐
- flex-end 交叉轴终点对齐
- center
- baseline 文字基线对齐
- stretch(default) 若未设置高度,则默认占满整个交叉轴
- 多根轴线(多行)的对齐方式(多根主轴在交叉轴上的对齐方式)
- align-content:
- flex-start 与交叉轴起点对齐
- flex-end 与交叉轴终点对齐
- center
- space-between 交叉轴两端对齐,中间平均分布
- space-around 每根轴线两侧间隔都相同
- space-evenly 均匀分布,两侧靠边间隔与元素之间间隔也相同
- stretch(default) 占满整个交叉轴
对于其中的每一项:
- order 值为数字,为排序的权重
- flex-grow 值为数字,意为成员的放大比例,默认为0,若都设为1,则会放大等分剩余空间(忽略其原本宽度,全部等分)
- flex-shrink 值为数字,意为成员的缩小比例,默认为1,若空间不足,则会缩小相同比例,若设为0,则不缩小,若都不缩小,则会超出
- flex-basis 值为长度,意为在未分配剩余空间时,成员占据的主轴空间大小,默认为auto,即为成员的本来大小
- flex 为flex-grow flex-shrink flex-basis的简写,默认为0 1 auto ,后两者可选,我们常用这个属性,其实本质大多数用的都是flex-grow
- align-self 单个成员的交叉轴对齐方式
- auto(default) 继承自父元素 如果没有父元素,则默认为stretch
- flex-start
- flex-end
- center
- baseline
- stretch
?清除浮动
浮动的原理是让图片脱离文档流,直接浮在桌面上。我们一般布局的时候都是只设置宽度不设置高度,让内容来自动填充高度。但使用浮动后会让原本填充的高度消失,父元素高度为0,后续添加内容布局会产生混乱,造成高度塌陷,这时候就可以利用清除浮动来解决父元素高度塌陷的问题。
浮动导致的问题及其解决方案:
- 父元素高度塌陷
- 父元素固定宽高(适用于宽高确定的板块)
- 在浮动元素后面新加一个空标签,并设置clear: both;
- 优点:简单
- 缺点:添加了许多空的div,语义化差
- overflow: hidden
- 优点:简单易用,浏览器支持好
- 缺点:要设置width和zoom,且有时会裁剪掉部分元素
- clearfix::after(主流做法)
.clearfix:after{/*伪元素是行内元素 正常浏览器清除浮动方法*/
content: "";
display: block;
height: 0;
clear:both;
visibility: hidden;
}
.clearfix{
*zoom: 1;/*ie6清除浮动的方式 *号只有IE6-IE7执行,其他浏览器不执行*/
}
- 同级非浮动元素被覆盖
- 给被遮盖的元素也设置浮动
- 为被遮盖的元素触发BFC(宽度足够会同行显示)
- 为被遮盖的元素设置clear: both;(会换行显示)
?Grid布局
①定义grid布局:
- display: grid/inline-grid
②设置行与列的布局
- grid-template-rows / grid-template-columns
- 基本使用:grid-template-rows(grid-template-columns): 100px 100px 200px,设置三行(列),高度(宽度)分别为100 100 200px
- repeat():grid-template-rows(grid-template-columns): repeat(3, 100px),设置三行(列),高度(宽度)均为100px
- auto-fill:grid-template-rows(grid-template-columns): repeat(auto-fill, 100px),不定有几行/几列,能装多少装多少,当可用空间不足以容纳更多的列时,会创建空的隐式网格项来填充空白区域
- auto-fit:尽量将每一列放入网格容器中,使得不留下空白的空间,当可用空间不足以容纳更多的列时,会自动调整已有的网格项的大小,以填充剩余空白区域
- fr:占比,与flex-grow类似,grid-template-rows(grid-template-columns): 100px 1fr 2fr,除去100px,剩下按1:2划分
- minmax():grid-template-rows(grid-template-columns): 1fr 1fr minmax(100px, 2fr),表示第三行(列)最少为100px,最大不能超过前两行(列)的两倍
- auto:自动调整,grid-template-rows(grid-template-columns): 100px auto 100px,实现自由适配的三栏布局
- row-gap / columns-gap / gap:行间距列间距以及二合一写法
③划分区域自定义放置元素
- grid-template-areas:划分区域
grid-template-columns: 120px 120px 120px;
grid-template-rows: repeat(3, 300px);
grid-template-areas:
". header header"
"sidebar content content"
"sidebar . .";
- 定义三行三列,通过grid-template-areas来划分区块,并为其命名, . 表示空
.sidebar {
grid-area: sidebar;
}
?
.content {
grid-area: content;
}
?
.header {
grid-area: header;
}
- 规定哪个类的内容放在哪个区块中
④划分区域自动填充元素
- grid-auto-flow
- row 默认值,按行进行依次填充
- column,按列进行依次填充
- 附加值dense,是否紧凑填充,可以自动在后方找到合适大小的块填充到前面的单元格中,防止按序填充时出现元素过大填充不满的现象
⑤自由安排元素位置
- grid-column-start、grid-column-end、grid-row-start、grid-row-end:分别对应网格的上边框位置、下边框位置、左边框位置、右边框位置,通过这四个元素将内容定位到网格中(左上方顶点为1,向右向下递增)
- span:声明项目跨度(占几个格子),在start中设置,end就可以不用设置
- 简写形式:grid-area: 2 / 1 / 4 / 3(其中,2表示起始行,1表示起始列,4表示结束行,3表示结束列)
⑥所有元素在单元格内的对齐方式
- justify-items / align-items:水平/垂直对齐方式
- start
- end
- center
- stretch(默认)
- place-items:水平和垂直的合并形式,写两个就是左水平右垂直,写一个就是两者共用
⑦单个元素在单元格内的对齐方式
- justify-self、align-self、place-self:与⑥中用法完全一致,只是针对单个元素使用
⑧单元格在容器内的对齐方式
- justify-content / align-content:水平/垂直对齐方式
- start(默认)
- end
- center
- stretch
- space-between
- space-around
- space-evenly
- place-content为上两者的合并形式
⑨多出来的东西怎么放?隐式网格
- grid-auto-columns / grid-auto-rows:定义隐式网格的单元格长宽
一些面试题
- 请解释什么是CSS Grid布局?
- Grid布局中有哪些主要的概念和术语?
- 请解释Grid容器和Grid项之间的区别是什么?
- 如何在父元素上应用Grid布局?
- 请解释Grid布局中的行和列是如何定义的?
- 如何指定Grid项在网格中的位置?
- Grid布局中的重叠和层叠是如何处理的?
- 如何控制Grid项的大小和对齐方式?
- 请解释Grid布局的自动布局机制。
- 如何媒体查询和响应式设计与Grid布局结合使用?
?选择器
基本选择器:id class 标签 层次选择器:后代、子代、兄弟、通用 结构选择器:第一个、最后一个、奇数偶数个、伪类、伪元素 属性选择器:属性
?animation有哪些属性
CSS 的 animation 属性用于为元素添加动画效果。以下是 animation 属性的一些常用子属性:
1、animation-name: 定义动画的名称。
.animation {
animation-name: myAnimation;
}
?
@keyframes myAnimation {
0% { opacity: 0; }
100% { opacity: 1; }
}
2、animation-duration: 定义动画的持续时间。
.animation {
animation-name: myAnimation;
animation-duration: 2s;
}
3、animation-timing-function: 定义动画的时间函数(即动画进度的加速度)。
.animation {
animation-name: myAnimation;
animation-timing-function: ease-in-out;
}
4、animation-delay: 定义动画开始之前的延迟时间。
.animation {
animation-name: myAnimation;
animation-delay: 1s;
}
5、animation-iteration-count: 定义动画的播放次数,infinite为永不停止。
.animation {
animation-name: myAnimation;
animation-iteration-count: 3;
}
6、animation-direction`: 定义动画的播放方向。
.animation {
animation-name: myAnimation;
animation-direction: reverse;
}
7、animation-fill-mode: 定义动画在播放之前和之后如何应用样式。
- none:默认值,动画不会对元素应用任何样式。在非活动时间段,元素将恢复到初始样式。
- forwards:在动画结束后,元素将保持动画结束状态。也就是说,元素将保持在最后一帧的状态。
- backwards:在动画开始前,元素将立即应用动画的第一帧样式。这意味着动画开始前的状态会提前被应用。
- both:结合了 forwards 和 backwards,在动画开始前和结束后都会应用样式。
.animation {
animation-name: myAnimation;
animation-fill-mode: forwards;
}
8、animation-play-state: 定义动画的播放状态。
.animation {
animation-name: myAnimation;
animation-play-state: paused;
}
这些是 animation 属性的一些常用子属性,它们可以用来控制动画的各个方面,从而创建出丰富多样的动画效果。使用这些属性可以自定义动画的外观、持续时间、延迟等。要实现动画效果,还需要定义动画的关键帧(@keyframes)规则,包含动画在不同百分比时的样式。
在 CSS 中,可以使用 @keyframes 规则来定义动画的关键帧(即动画的不同阶段)。@keyframes 规则定义了动画的每个关键帧以及相应的样式。
下面是一个使用 @keyframes 定义动画关键帧的示例:
@keyframes slide {
0% {
transform: translateX(0);
}
50% {
transform: translateX(100px);
}
100% {
transform: translateX(200px);
}
}
在这个例子中,我们创建了一个名为 slide 的动画。通过 @keyframes 规则,我们定义了三个关键帧:0%、50% 和 100%。
- 在 0% 关键帧,元素的 transform 属性设置为 translateX(0),即初始状态。
- 在 50% 关键帧,元素的 transform 属性设置为 translateX(100px),表示动画进行到一半时的状态。
- 在 100% 关键帧,元素的 transform 属性设置为 translateX(200px),表示动画结束时的状态。
你可以在关键帧中定义任意数量的状态,并根据需要设置不同的样式属性。使用百分比来表示关键帧的时间点,动画会根据关键帧之间的插值进行平滑过渡。
定义完关键帧后,你可以使用 animation-name 属性将动画应用到具体的元素上,如下所示:
.element {
animation-name: slide;
animation-duration: 2s;
}
通过将 animation-name 设置为关键帧名称,你可以将定义好的动画应用于元素,并设置动画的持续时间(在此示例中为 2 秒)。
请注意,在实际使用中,除了设置关键帧的样式,你还可以使用其他属性调整动画的速度、延迟、重复次数等。根据具体需求,你可以进一步完善动画效果。
?说说transition
CSS 的 transition 属性用于在元素发生状态变化时,平滑地过渡(或动画)到新的样式。它允许你控制一个或多个 CSS 属性的变化过程,使得元素的变化更加柔和和可控。
transition 属性由以下几个子属性组成:
1、transition-property:指定要过渡的 CSS 属性名称,可以是单个属性,也可以是多个属性。 当 transition 属性只设置了 transition-duration 值时,没有指定 transition-property 值,默认情况下所有可过渡的 CSS 属性都会应用过渡效果。
.element {
transition-property: width;
}
2、transition-duration:指定过渡的持续时间,以秒(s)或毫秒(ms)为单位。
.element {
transition-duration: 0.5s;
}
3、transition-timing-function:指定过渡的时间函数,用于控制过渡的速度曲线。常见的值包括 ease(默认值)、linear、ease-in、ease-out 和 ease-in-out,也可以使用贝塞尔曲线来定义自定义的速度曲线。
.element {
transition-timing-function: ease-in-out;
}
4、transition-delay:指定过渡开始之前的延迟时间,以秒(s)或毫秒(ms)为单位。
.element {
transition-delay: 0.2s;
}
通过使用这些子属性,你可以控制元素的过渡效果,从而实现例如悬停时颜色渐变、尺寸变化、透明度渐变等效果。
例如,下面的代码演示了一个当鼠标悬停在元素上时,背景颜色发生渐变过渡的效果:
.element {
background-color: blue;
transition-property: background-color;
transition-duration: 0.5s;
transition-timing-function: ease-in-out;
}
?
.element:hover for(let [key, value] of map) {
if(largeStr.indexOf(key)!==-1) {
}
}
background-color: red;
}
以上代码将使元素的背景颜色在悬停时从蓝色平滑地过渡到红色,过渡时间为 0.5 秒,速度曲线为先加速后减速。这只是 transition 属性的一个简单示例,你可以根据需要调整和组合这些子属性来实现更复杂的过渡效果。
?外边距塌陷解决方案
外边距塌陷是指在垂直方向上,两个相邻元素的外边距(margin)合并成一个外边距的现象。以下是几种常见的外边距塌陷情况及其解决方案:
- 父子元素外边距塌陷:
- 情况:父元素的上边距与第一个子元素的上边距合并,导致外边距塌陷。
- 解决方案:给父元素添加 overflow: hidden; 或 float: left/right; 属性,为父元素创建新的块级格式化上下文(BFC),从而阻止外边距的合并。
- 相邻兄弟元素外边距塌陷:
- 情况:两个相邻的兄弟元素,上一个元素的下边距与下一个元素的上边距合并,导致外边距塌陷。
- 解决方案:给其中一个元素添加 padding-top 或 border-top 属性,或者在两个元素之间插入空元素(例如 <div></div>)。
- 空元素与外边距塌陷:
- 情况:一个没有内容的空元素或没有上边框/上内边距的元素,上边距与其下面的元素的上边距合并,导致外边距塌陷。
- 解决方案:给空元素添加 padding-top 或 border-top 属性,或者为其添加 display: inline-block; 属性。
- 内部包含块的外边距塌陷:
- 情况:一个元素的内部包含块(例如 <div>)中的第一个子元素的上边距与外部元素的上边距合并,导致外边距塌陷。
- 解决方案:为外部元素添加 overflow: hidden; 或 float: left/right; 属性,将其变为 BFC,阻止外边距合并。
这些解决方案的原理都是通过改变元素的布局特性来创建新的块级格式化上下文(BFC),从而阻止外边距的合并。BFC 可以独立地控制元素的布局行为,使得外边距不再发生塌陷。
?说一说overflow的各个值
- visible:正常显示,多余部分超出盒子显示
- scroll:默认显示滚动条
- hidden:不提供滚动条,也不支持用户滚动,多于内容会被裁剪,但可以通过脚本控制滚动
- clip:和hidden类似,但不允许通过脚本滚动,也不会生成BFC
- auto:溢出显示滚动条,不溢出则不显示,生成BFC
- overlay:行为与auto相同,但滚动条绘制在内容之上,不占据空间
?说说伪类和伪元素
伪类: 用于已有元素处于某种状态时为其添加对应的样式,这个状态是根据用户行为而动态变化的
例如:当用户悬停在指定元素时,可以通过:hover来描述这个元素的状态,虽然它和一般css相似,可以为 已有元素添加样式,但是它只有处于DOM树无法描述的状态下才能为元素添加样式,所以称为伪类
伪元素: 用于创建一些不在DOM树中的元素,并为其添加样式
例如,我们可以通过:before来在一个元素之前添加一些文本,并为这些文本添加样式,虽然用户可以看见 这些文本,但是它实际上并不在DOM文档中
?引入样式时,link和@import的区别?
- 加载顺序: <link> 在页面加载时同时被加载,而 @import 在页面加载后才被加载。因此,<link> 可以更快地加载外部资源,并且不会阻塞页面的加载,而 @import 会延迟页面的显示,直到所有的 CSS 文件加载完毕。
- 兼容性: <link> 是 HTML 标签,可以被所有浏览器和搜索引擎正确解析。而 @import 是 CSS 的语法规则,只能被符合 CSS3 规范的浏览器正确解析,低版本的浏览器可能会忽略 @import 声明。
- 使用方式: <link> 可以用于加载任何类型的文件,如 CSS、JavaScript、图像、音频等。而 @import 只能用于加载 CSS 文件。
- 作用域: <link> 中的样式表对整个页面都起作用,而 @import 只对当前样式表中的规则起作用。
综上所述,<link> 更适合引入外部资源,而 @import 更适合在 CSS 文件中嵌入其他 CSS 文件。如果需要同时加载多个 CSS 文件,最好使用 <link>,以提高页面的性能和可访问性。
?display、visibility、opacity的区别
- display:none; DOM结构:浏览器不会渲染display属性为none的元素,不占据空间,意思就是页面上没有它的一席之地,你在开发者模式中选不中那个元素。 事件监听:无法进行DOM事件监听。 性能:动态改变此属性时会引起回流,性能较差。 继承:不会被子元素继承,因为子元素也不被渲染。 transtion过渡不支持display。
- visibility:hidden; DOM结构:元素被隐藏了,浏览器会渲染visibility属性为hidden的元素,占据空间,意思就是页面上有它的空间,在开发者模式中能选中那个元素。 事件监听:无法进行DOM事件监听。 性能:动态改变此属性时会引起重绘,性能较高。 继承:会被子元素继承,子元素通过设置visibility:visible;来显示自身,使子元素取消自身隐藏。 transtion:visibility会立即显示,隐藏时会延时。
- opacity:0; DOM结构:opacity属性值为0时透明度为100%,元素隐藏,占据空间,opacity值为0到1,为1时显示元素。 事件监听:可以进行DOM事件监听。 性能:提升为合成层,不会引发其他元素的重绘,性能较高。 继承:会被子元素继承,子元素不能通过设置opacity:1;来取消隐藏。 transtion:opacity可以延时显示与隐藏。
JS
?简述JavaScript对象、函数、数组的定义。
对象(Object):JavaScript 的对象是一种复合数据类型,用于存储键值对。对象由一组属性(properties)组成,每个属性都有一个键(key)和对应的值(value)。对象可以通过字面量表示法或构造函数来创建。
函数(Function):JavaScript 是一门函数式编程语言,函数在 JavaScript 中是一等公民。函数是可执行的代码块,可以接收参数并返回一个值。可以通过函数声明或函数表达式的方式定义函数。
数组(Array):JavaScript 数组是一种有序、可变的数据集合,可以存储多个值。数组可以包含不同类型的数据,并且长度是动态的。可以使用字面量表示法或构造函数来创建数组。
?说说JS中的隐式转换
隐式类型转换是指 JavaScript 在比较或计算操作中自动将一个数据类型转换为另一个数据类型。这种类型转换是根据 JavaScript 的类型转换规则进行的,以下是一些常见的隐式类型转换规则:
==
- 字符串和数字之间的比较:如果一个操作数是字符串,另一个操作数是数字,JavaScript 会将字符串转换为数字,然后进行比较。
- 布尔值和其他类型之间的比较:布尔值在进行比较时,会被转换为数字,true 被转换为 1,false 被转换为 0。
- 对象和原始值之间的比较:对象在进行比较时,JavaScript会先使用 valueOf() 方法获取对象的原始值,然后再根据前面提到的方法将两个值进行比较。如果 valueOf() 方法无法返回原始值,则会尝试调用 toString() 方法来获取值。
- null 和 undefined 的比较:它们在进行相等比较时被认为是相等的。
运算
- string+其他基本数据类型,转为string
- 其他基本数据类型与的number类型操作,均转为number
补充问题:
当a为多少时,if判断成立
let a
if(a == 1&&a == 2&&a == 3) {
console.log('success!')
}
答案:
a = {
a:1,
valueOf() {
return a++
}
}
前端性能优化
首屏优化
- 压缩、分包(利用浏览器并行的能力加载)、删除无用代码
- 静态资源分离,如放在CDN上面
- JS脚本非阻塞加载(或将其放在最底部,最后加载,以防阻塞页面渲染)
- 合理利用缓存
- 服务器端渲染(SSR)
- 预置loading、骨架屏
渲染优化
- GPU加速,利用GPU来处理复合图层(像素密集型)进行“加速”
- 减少回流、重绘,例如:
- 避免使用CSS表达式,因为当页面变化时,都会进行重新计算
- 不适用table布局,因为该布局中的一个小改动都会引起整个table重新布局,所以现在table布局已经几乎淘汰
- 能使用css就不使用js
- 离屏渲染,正常情况下数据经过计算和渲染后,就会直接显示在屏幕上,而使用离屏渲染,就会在内存将画面全部渲染好,再放在页面上,以防画面过于复杂而使用户感到掉帧
- 懒加载,将需要的资源提前加载到本地,需要时直接从缓存中取出,节约时间。
JS优化
- 防止内存泄露,比如:
- 使用全局变量不当产生的内存泄露
- DOM引用没有及时清除,导致DOM删除后但JS引用依旧存在,占用资源
- 定时器没有及时关闭(所以建议自己封装可以自动关闭的定时器组件)
- 循环语句尽早break
- 合理使用闭包
- 减少DOM访问,因为JS引擎和渲染引擎交互较为费时,如果需要常用,要使用一个变量将其缓存起来,不要每次都去查询获取
- 防抖——多次点击,只执行最后一次
- 节流——一定时间内,多次调用只执行一次
- Web Worker(工作线程),可以将一些耗时的数据处理操作从主线程中剥离出来,使主线程更加专注于页面渲染和交互。
?跨浏览器兼容问题
- CSS盒模型差异,可以使用CSS重置或使用标准化库来进行统一各浏览器的默认样式
- JavaScript API兼容性问题,可以通过特性检测来判断当前浏览器是否支持某个API,或使用polyfill库来提供缺失的功能
- flex布局兼容性问题,可以使用grid等布局来作为替代方案
?如何保证代码的质量和可维护性
- 模块化,特定的模块负责特定的功能,提高代码的可复用性,降低耦合
- 通过一些设计模式来管理实例,例如单例模式、观察者模式等
- 进行一些单元测试
- 代码复用,提出可复用的函数及组件
- 错误处理,合理处理异常和错误情况
- 使用代码质量检查工具来检查修复代码规范问题,例如ESLint
?关于变量提升和函数提升的问题
var c = 1;
function c(c) {
console.log(c)
}
c(2)
//函数作?域:局部作?域
var a = 1;
function b() {
a = 10;
return;
//a函数声明,提前变量a,将a认为是函数b作?域的变量,具有局部效果
function a(){}
}b();
console.log(a); // 1
var m= 1, j = k = 0;
function add(n) {
return n = n+1;
}
y = add(m);
function add(n) {
return n = n + 3;
}
z = add(m);
说一下Vue2响应式的实现
- 创建 Vue 实例时,会调用 new Vue(),其中会通过 observe 函数对 data 数据进行观测。
- 在 observe 函数中,会使用 Object.defineProperty 递归地遍历所有属性,并为每个属性创建对应的 getter 和 setter。
- 在属性的 getter 中,会进行依赖收集。首先,通过闭包引用一个全局变量 Dep.target,该变量保存当前正在计算的 Watcher 实例。然后,在 getter 中调用 dep.depend() 方法,将当前的 Watcher 添加到依赖集合中。
- 在属性的 setter 中,当属性的值发生改变时,会触发 setter。在 setter 中,会对新值进行响应式处理(递归调用 observe),并通知相关的依赖进行更新。这一过程通过调用 dep.notify() 实现,dep.notify() 会遍历依赖集合,调用每个依赖的 update 方法,触发相应的更新操作。
- 在模板编译过程中,解析模板中的指令表达式,对每个指令表达式生成对应的 Watcher 实例。Watcher 实例内部会调用属性的 getter 方法,在 getter 中触发依赖收集的过程,将当前 Watcher 添加到依赖集合中。
- 当属性的值发生变化时,会触发相应的更新操作。依赖收集过程中收集到的 Watcher 实例会在更新阶段被遍历,并调用其 update 方法来更新视图。
?关于跨域
跨域请求是指在浏览器中,当请求的源地址与请求地址的协议、域名、端口号不一致时,会被浏览器的同源策略所限制,这是为了保护用户的信息安全。
- JSONP(JSON with Padding):JSONP通过动态创建
- CORS(Cross-Origin Resource Sharing):CORS是一种现代的跨域解决方案,通过在服务器端设置响应头部,允许客户端跨域访问资源。在前端开发中,可以通过设置服务器的响应头部字段Access-Control-Allow-Origin来允许特定的源进行跨域请求。
- 代理服务器(Proxy Server):通过在自己的服务器上设置一个代理服务器,将跨域请求转发到目标服务器,并将目标服务器的响应返回给前端。这种方法需要对服务器进行配置,适用于无法直接修改目标服务器响应头部的情况。
- WebSocket:WebSocket是一种全双工通信协议,可以在同一域名下使用不同的端口进行跨域通信。通过建立WebSocket连接,可以实现实时通信和跨域数据传输。
?请解释一下什么是响应式设计(Responsive Design),以及在开发响应式网站时需要考虑哪些方面,并列举常用方法
响应式设计(Responsive Design)是一种网页设计和开发的方法,旨在使网站能够适应不同设备、屏幕尺寸和浏览器窗口大小,以提供更好的用户体验。响应式设计通过使用灵活的布局、弹性的图像和媒体查询等技术,让网页能够根据用户设备的特性进行自适应和优化。
在开发响应式网站时,需要考虑以下几个方面:
- 弹性布局:使用相对单位(如百分比、em、rem)而不是固定像素来定义元素的尺寸和布局。这样可以让网页的元素根据屏幕尺寸的变化而自动调整大小和位置。
- 媒体查询:使用CSS的媒体查询功能来检测设备的特性(如屏幕宽度、触摸支持等),并根据不同的条件应用不同的样式规则。这样可以根据不同设备的屏幕大小和特性来调整网页的布局和样式。
- 图片优化:针对不同设备加载适当尺寸和分辨率的图片,以减少加载时间和带宽占用。可以使用响应式图片技术,如<img srcset>和<picture>元素来提供多个不同尺寸的图片,并根据设备的屏幕密度和大小选择合适的图片加载。
- 触摸友好性:优化网页在触摸设备上的交互和操作体验,比如增大可点击区域、使用合适的手势和触摸事件等。
- 导航菜单:设计合适的导航菜单,以便在小屏幕设备上能够轻松导航和访问各个页面。
- 测试和调试:在不同设备、不同浏览器和不同分辨率下进行测试和调试,以确保网页在各种情况下都能正常显示和运行。
- 性能优化:考虑网页加载速度和性能,减少不必要的资源请求和加载,使用压缩和缓存等技术来提高网页的响应速度。
以下是一些常用的技术和方法来实现响应式设计:
- 媒体查询(Media Queries):使用CSS的媒体查询功能,根据设备的特性(如屏幕宽度、设备类型等)应用不同的样式。通过设置不同的CSS规则,可以根据设备的宽度、高度、方向等特性来改变布局和样式。
- 相对单位(例如em和rem):相对单位可以根据父元素或根元素的大小进行调整。em单位相对于父元素的字体大小,rem单位相对于根元素(通常是html元素)的字体大小。使用这些相对单位可以根据设备的屏幕大小自适应调整元素的大小。
- 百分比布局:使用百分比作为元素的宽度、高度等属性值,以便根据容器的大小来调整元素的尺寸。
- 视口单位(例如vw、vh):视口单位是相对于视口宽度和高度的单位。vw表示视口宽度的百分比,vh表示视口高度的百分比。使用视口单位可以根据屏幕的实际尺寸来调整元素的大小。
- 弹性盒布局(Flexbox):弹性盒布局是一种灵活的布局方式,可以使容器中的元素在不同尺寸的屏幕上自动调整位置和大小。
- 栅格布局(Grid):网格布局是一种二维布局系统,可以将页面划分为行和列,并指定元素在网格中的位置。通过设置不同的网格模板和位置属性,可以实现灵活的响应式布局。
- 响应式图片:使用srcset和sizes属性来提供不同尺寸的图片,以适应不同设备的显示需求。
- 断点(Breakpoints):根据不同设备的屏幕宽度和布局需求,设置断点来改变页面的布局和样式。
?什么是单页面应用(SPA)?它与传统多页面应用相比有什么优势和劣势?
单页面应用(Single Page Application,SPA)是一种Web应用程序的架构模式,它在加载初始HTML页面后,通过JavaScript动态地更新页面的内容,而不需要每次页面切换时重新加载整个页面。
与传统的多页面应用相比,SPA有以下优势:
- 更好的用户体验:由于SPA使用了异步加载和局部更新的方式,用户在浏览网站时可以享受到更流畅、快速的交互体验,减少了页面刷新的延迟。
- 较少的网络请求:SPA通常在初始加载时会下载所有所需的JavaScript、CSS和其他静态资源,之后只需要通过API请求数据。相比多页面应用,SPA减少了对服务器的频繁请求,提高了性能。
- 良好的代码组织:SPA使用组件化的方式组织代码,将页面拆分为多个可重用的组件。这样可以提高代码的复用性、可维护性和可测试性,使开发工作更加高效。
然而,SPA也存在一些劣势:
- 初始加载较慢:由于SPA需要在初始加载时下载所有所需资源,首次加载时间可能会较长。这对于一些网络条件较差的用户来说可能是一个问题。
- 对SEO不友好:传统的搜索引擎爬虫通常会根据HTML页面的内容进行索引,而SPA的内容是通过JavaScript动态生成的,对搜索引擎的爬取和索引不太友好。虽然有一些技术可以解决这个问题(如服务器端渲染),但相对复杂。
- 依赖于JavaScript:由于SPA大量依赖于JavaScript来处理页面逻辑和数据交互,因此如果用户禁用了JavaScript,SPA可能无法正常运行。
?JS垃圾回收机制
- 项目中,如果存在大量不被释放的内存(堆/栈/上下文),页面性能会变得很慢。当某些代码操作不能被合理释放,就会造成内存泄漏。我们尽可能减少使用闭包,因为它会消耗内存。
- 浏览器垃圾回收机制/内存回收机制:
- 浏览器的Javascript具有自动垃圾回收机制(GC:Garbage Collecation),垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。
- 标记清除:在js中,最常用的垃圾回收机制是标记清除:浏览器会为内存中所有的变量添加标记,然后再遍历上下文,为其中所有变量及被变量引用的变量清除标记,这样剩下被标记的就是待回收对象。 谷歌浏览器:“查找引用”,浏览器不定时去查找当前内存的引用,如果没有被占用了,浏览器会回收它;如果被占用,就不能回收。 IE浏览器:“引用计数法”,当前内存被占用一次,计数累加1次,移除占用就减1,减到0时,浏览器就回收它。
- 优化手段:内存优化 ; 手动释放:取消内存的占用即可。
- (1)堆内存:fn = null 【null:空指针对象】
- (2)栈内存:把上下文中,被外部占用的堆的占用取消即可。
- (3)防止出现隐藏类,在一开始调用构造函数生成实例时就传入需要的变量,防止动态添加和删除属性
- (4)静态分配和对象池
- 内存泄漏
- 在 JS 中,常见的内存泄露主要有 4 种,全局变量、闭包、DOM 元素的引用、定时器
?JS 中 this 的五种情况
- 作为普通函数执行时,this指向window。
- 当函数作为对象的方法被调用时,this就会指向该对象。
- 构造器调用,this指向返回的这个对象。
- 箭头函数捕获的是最近一层非箭头函数作用域的this值,不会绑定到字面量复制的对象上。
- 基于Function.prototype上的 apply 、 call 和 bind调用模式,这三个方法都可以显示的指定调用函数的 this 指向。apply接收参数的是数组,call接受参数列表,bind方法通过传入一个对象,返回一个this绑定了传入对象的新函数。这个函数的 this指向除了使用new时会被改变,其他情况下都不会改变。若为空默认是指向全局对象window。
?原型 && 原型链
原型关系:
- 每个 class都有显示原型 prototype
- 每个实例都有隐式原型 __ proto__
- 实例的_ proto_指向对应 class 的 prototype
? 原型: 在 JS 中,每当定义一个对象(函数也是对象)时,对象中都会包含一些预定义的属性。其中每个函数对象都有一个prototype 属性,这个属性指向函数的原型对象。
原型链:函数的原型对象上的constructor默认指向函数本身,原型对象除了有原型属性外,为了实现继承,还有一个原型链指针_proto_ ,该指针是指向上一层的原型对象,而上一层的原型对象的结构依然类似。因此可以利用_proto_ 一直指向Object的原型对象上,而Object原型对象用Object.prototype.__ proto__ = null表示原型链顶端。如此形成了js的原型链继承。同时所有的js对象都有Object的基本方法
特点: JavaScript对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时,与之相关的对象也会继承这一改变。
?new运算符的实现机制
- 首先创建了一个新的空对象
- 设置原型,将对象的原型设置为函数的prototype对象。
- 让函数的this指向这个对象,执行构造函数的代码(为这个新对象添加属性)
- 判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象。
?请解释一下什么是事件委托(或称事件代理),以及它在前端开发中的作用是什么?
事件委托(或称事件代理)是一种在前端开发中常用的技术,它利用了事件冒泡机制,将事件处理程序绑定到一个父元素上,而不是直接绑定到每个子元素上。当事件触发时,事件会经过冒泡阶段依次向上传播至父元素,父元素可以根据事件的目标元素来判断应该执行哪个事件处理函数。
事件委托的作用主要有以下几点:
- 减少事件处理程序的数量:通过将事件处理程序绑定到父元素上,可以避免为每个子元素都绑定事件处理程序,减少内存消耗。
- 动态绑定事件:对于通过 JavaScript 动态添加的子元素,使用事件委托可以自动为它们绑定事件处理程序,无需额外的操作。
- 简化代码结构:使用事件委托可以将事件处理程序集中管理,使代码更加简洁、可读性更高。
举个例子,假设有一个列表的父元素,里面包含了多个子元素(项),我们需要为每个子元素绑定点击事件,实现点击子元素时改变其样式。使用事件委托可以这样处理:
// 获取父元素
const parentElement = document.getElementById('parent');
?
// 绑定点击事件到父元素
parentElement.addEventListener('click', function(event) {
// 判断事件目标是否是子元素(项)
if (event.target && event.target.nodeName === 'LI') {
// 执行事件处理逻辑,例如改变样式
event.target.classList.toggle('active');
}
});
通过将点击事件绑定到父元素上,无论新增或删除了子元素,甚至是动态生成的子元素,都会自动触发相应的事件处理逻辑,大大简化了代码的管理和维护。
?说说你常用的ES6新特性
- 块级作用域(Block Scope): 使用 let 和 const 关键字声明的变量具有块级作用域,即只在当前代码块中有效。相比于 var 声明的变量具有函数作用域而言,这种方式更加灵活和可控。
- 箭头函数(Arrow Function): 箭头函数通过 => 符号定义,可以简化函数的书写。它具有以下特点:
- 自动绑定上下文:箭头函数不会创建自己的 this,而是继承外层作用域的 this。
- 省略 return 关键字:当函数体只有一条返回语句时,可以省略 return 关键字。
- 更简洁的语法:当只有一个参数时,可以省略括号;当函数体为空时,可以使用 () => {} 表示。
- 解构赋值(Destructuring Assignment): 解构赋值允许从数组或对象中提取值并赋给变量。例如,可以通过以下方式提取数组元素:
Codeconst [a, b] = [1, 2];
也可以通过以下方式提取对象属性:
Codeconst { name, age } = { name: 'Alice', age: 18 };
- 模板字符串(Template Strings): 模板字符串使用反引号 `` 包裹,并支持在字符串中插入变量或表达式。例如:
Codeconst name = 'Alice';
console.log(`Hello, ${name}!`);
- 扩展运算符(Spread Operator): 扩展运算符用于展开可迭代对象(如数组和字符串),将其元素逐个展开,例如:
Codeconst arr = [1, 2, 3];
console.log(...arr); // 输出:1 2 3
- 类(Class): ES6 引入了类语法,可以使用 class 关键字定义一个类。类可以包含构造函数、实例方法、静态方法等。例如:
Codeclass Person {
constructor(name) {
this.name = name;
}
?
sayHello() {
console.log(`Hello, my name is ${this.name}!`);
}
?
static getInfo() {
console.log('This is a person class.');
}
}
?
const person = new Person('Alice');
person.sayHello(); // 输出:Hello, my name is Alice!
Person.getInfo(); // 输出:This is a person class.
- 简化对象字面量(Object Literal Shorthand): 当定义对象字面量时,如果属性名和变量名相同,可以直接省略写属性值。例如:
Codeconst name = 'Alice';
const age = 18;
?
const person = { name, age };
console.log(person); // 输出:{ name: 'Alice', age: 18 }
- 模块化(Module): ES6 引入了模块化概念,可以将代码拆分为多个模块,每个模块有自己的作用域,并通过 import 和 export 关键字进行模块之间的导入和导出。例如:
// moduleA.js
export function greet(name) {
console.log(`Hello, ${name}!`);
}
?
// moduleB.js
import { greet } from './moduleA.js';
greet('Alice'); // 输出:Hello, Alice!
- Promise: Promise 是用于处理异步操作的对象,避免了传统的回调地狱。它表示一个异步操作的最终完成或失败,并可以链式调用 then、catch、finally 方法处理结果。例如:
function fetchData() {
return new Promise((resolve, reject) => {
// 异步操作...
if (/* 异步操作成功 */) {
resolve('Data fetched successfully!');
} else {
reject('Failed to fetch data!');
}
});
}
?
fetchData()
.then(data => console.log(data)) // 成功时执行
.catch(error => console.error(error)) // 失败时执行
.finally(() => console.log('Done')); // 不论成功与否都执行
- 数组方法(Array Methods): ES6 提供了许多方便的数组方法来处理和遍历数组,如 forEach、map、filter、reduce 等。这些方法提供了更简洁、可读性更高的方式来操作数组。例如:
javascript Codeconst numbers = [1, 2, 3, 4];
?
numbers.forEach(num => console.log(num)); // 遍历输出每个元素
const doubledNumbers = numbers.map(num => num * 2); // 对每个元素进行操作并返回新数组
const evenNumbers = numbers.filter(num => num % 2 === 0); // 过滤出符合条件的元素
const sum = numbers.reduce((acc, num) => acc + num, 0); // 对数组进行累加
说一说apply、call、bind
apply、call 和 bind 都是 JavaScript 中用于改变函数执行上下文(this)的方法,它们的区别如下:
- apply:
- 语法:function.apply(thisArg, [argsArray])
- 参数:需要指定函数执行时的上下文对象 thisArg 和可选的参数数组 argsArray。
- 功能:立即调用函数,并改变函数内部的 this 指向 thisArg,同时可以传递一个数组或类数组对象作为参数(通过 arguments 对象传递参数也可以)。
- 示例:fn.apply(obj, [arg1, arg2])
- call:
- 语法:function.call(thisArg, arg1, arg2, ...)
- 参数:需要指定函数执行时的上下文对象 thisArg 和可选的多个参数 arg1, arg2, ...。
- 功能:立即调用函数,并改变函数内部的 this 指向 thisArg,同时可以传递多个参数(通过逗号分隔)。
- 示例:fn.call(obj, arg1, arg2)
- bind:
- 语法:function.bind(thisArg, arg1, arg2, ...)
- 参数:需要指定函数执行时的上下文对象 thisArg 和可选的多个参数 arg1, arg2, ...。
- 功能:创建一个新的函数,函数内部的 this 指向 thisArg,并且绑定了指定的参数。不会立即执行函数,而是返回一个新函数,之后可以再次调用。
- 示例:var boundFn = fn.bind(obj, arg1, arg2)
总结:
- apply 和 call 可以立即调用函数并改变函数内部的 this 指向,区别在于参数传递方式不同,apply 接受参数数组,而 call 接受多个参数。
- bind 不会立即执行函数,而是返回一个新函数,函数内部的 this 指向绑定的对象,并且可以预先绑定一些参数。
- 这三种方法都可以实现改变函数执行上下文的目的,根据具体需求选择合适的方法。
说一下装箱和拆箱
在 JavaScript 中,基本数据类型(Primitive Types)和引用数据类型(Reference Types)在处理和操作时有所不同。当我们对基本数据类型值进行属性访问或方法调用时,JavaScript 会将基本数据类型自动转换为对应的包装对象,这个过程称为装箱(Boxing)。
下面以数字类型(Number)为例,简要说明基本数据类型装箱的过程:
- 创建包装对象:当我们使用属性或方法来访问数字类型的值时,JavaScript 在后台会创建一个临时的包装对象(Wrapper Object)。对于数字类型,创建的包装对象是 Number 对象。
- 存储值:创建的包装对象将会存储对应的基本数据类型值。例如,对于数字类型的装箱过程,包装对象中会存储相应的数字值。
- 访问属性或方法:一旦装箱完成,我们可以通过包装对象来访问属性和方法。这些属性和方法是与包装对象相关联的。例如,通过.toFixed()方法调用来格式化一个数字。
- 自动拆箱:当我们使用包装对象进行操作后,如果需要将结果赋值给一个变量,JavaScript 会自动进行拆箱(Unboxing)操作,将包装对象转换回基本数据类型的值。
装箱的过程是自动发生的,JavaScript 引擎会在需要时自动执行装箱和拆箱操作,使得开发者能够像操作引用类型一样操作基本类型。而这种装箱和拆箱的过程在后台进行,对于开发者来说是透明的。
需要注意的是,由于装箱过程涉及到对象的创建和数据拷贝,相比于直接操作基本类型,使用包装对象会带来额外的开销。因此,在不必要的情况下,最好直接操作基本类型,而不是通过装箱和拆箱操作。
?最大安全整数
JavaScript中的最大安全整数是 9007199254740991。它可以使用 Number.MAX_SAFE_INTEGER 常量来表示。这个值是由 JavaScript 中的双精度浮点数表示法决定的,在进行数值运算时不会丢失精度。
超过最大安全整数的数值会导致精度丢失,可能会引发意外的结果。如果需要处理超过最大安全整数范围的大整数,可以使用第三方的大数库或者 BigInt 类型(ES2020 新增)来处理。BigInt 类型可以表示任意精度的整数,但是在进行数值计算时需要特别注意性能和兼容性的问题。
?commonjs和es模块有什么区别?
CommonJS(简称CJS)和ES Modules(简称ESM)是两种不同的模块化规范,有以下不同:
- 语法差异:
- CommonJS:使用 require() 导入模块,使用 module.exports={xxx} 或 exports.xxx=xxx 导出模块。
- ESM:使用 import 导入模块,使用 export 导出模块。
- 对于基本数据类型,ESM导入的是值的引用,CJS导入的是值的拷贝,故ESM中引入的值是常量,不允许直接修改(对象修改或新增属性除外)
- 补充:如果导入的是对象,那么变量中传递来的就是对象的地址值,地址值进行拷贝后通过它访问的还是同一个对象,所以这里对象导入的可以理解为都是引用
- 作为整体导入时(即import * as mod from xxx),这时导入的对象第一级是只读的,如果尝试对第一层属性进行修改,就会报错
- CJS是运行时同步加载(动态),而ESM是编译时异步加载(静态)
- CJS适用于服务端开发,ESM适用于浏览器端
补充:相同点——CJS和ESM导入时,都会将整个模块的代码执行一遍,然后缓存执行的结果
?commonJS中module.exports和exports有什么区别
每个node.js文件在执行时都会隐式的、自动的创造一个module对象,同时,module对象会创建一个名叫exports,初始值为{}的属性:
var module = {
exports:{}
}
为了可以更好的导出对应功能模块里的内容,它会又隐式的创建一个变量exports:
//隐式的创建了一个变量exports
var exports = module.exports
?
//此时exports和module.exports 引用的是同一个对象
console.log(exports === modules.exports) //true
所以我们通过exports.xxx = xxx的方式进行导出,它最终也是被加到了modules.exports对象中
最终导出的是modules.exports对象,这就意味着如果我们将exports的指向改变,那么通过exports.xxx = xxx添加的导出就不会再添加到modules.exports对象中,xxx也就不会被导出
?如何理解JS中的异步
JS是一门单线程语言,因为它运行在浏览器的渲染主线程中(当遇到JS代码时,渲染主线程会将控制权交给js引擎来处理),而渲染主线程只有一个。
而渲染主线程承担着诸多工作,渲染页面,执行js都在其中执行。
如果用同步的方式,极有可能导致主线程产生阻塞,从而导致消息队列中很多任务无法及时执行。
所以浏览器就采取了异步措施,当某些异步任务触发时,如定时器、网络请求、事件监听器等,主线程就会将任务交给其他线程去处理,自身立即结束任务执行,转而执行后续任务,等到异步任务执行完毕,将事先传递的回调函数包装成任务,加入到对应队列(延时队列、微任务队列、交互队列)的末尾排队,等待主线程调度执行。
在这种异步模式下,浏览器就不会发生阻塞,保证了单线程的流畅运行。
?说一说事件循环
事件循环又叫消息循环,是浏览器渲染主线程的工作方式。
在Chrome的底层实现中,它开启了一个for的死循环,每次循环从消息队列中取出第一个任务执行,而其他线程只需要在合适的时机将任务放到消息队列中。
以前我们会将消息队列简单分为宏任务队列和微任务队列,现在随着浏览器环境愈发复杂,浏览器采用了更加灵活多变的处理方式。
根据W3C官方解释,每个任务都有不同的类型,同类型的任务须在同一个队列。不同的队列有不同的优先级(例如交互队列优先级大于延时队列),在一次事件循环中,由浏览器自行决定取哪一个队列的任务,但浏览器必须有一个微任务队列,且其优先级是最高的,必须优先调度执行。
?JS中的计时器能做到精确计时吗?
不可以
- 计算机中采用晶体振荡器来计时,虽然比较准确,但对比原子钟,精确度还是不足,且会受温度电压等因素影响,在计算机中通常会使用网络时间协议来进行校准。而js计时是调用操作系统中的计时函数,也就携带了些许偏差。
- 按照W3C标准,当浏览器中使用计时器,其嵌套层级超过五层时,之后的计时器就默认会有一个4ms的延迟,这4ms也会引起时间的偏差
- 而最主要的因素还是事件循环中,如果渲染主线程上有任务正在执行,就会推迟计时器的执行
?如何判断一个对象是否为空
如果使用常规的Object.getOwnPropertyNames()来判断,虽然可以将一般的不可枚举类型属性也判断出来,但无法判断继承的属性以及Symbol类型值定义的属性。所以我们需要使用Reflect.ownKeys()来进行转换,这样通过它返回的数组长度就能精准判断出对象是否为空。
?重写一下String.prototype.trim
1、正则
String.prototype.trim = function() {
return this.replace(/(^\s*)|(\s*$)/g, "")
}
2、双指针法
String.prototype.trim = function() {
while(this[0]===' ') {
this = this.subString(1)
}
while(this[this.length-1]===' ') {
this = this.subString(0, this.length-1)
}
}
本文暂时没有评论,来添加一个吧(●'◡'●)