2021前端常见面试题及答案-HTML、CSS、JavaScript汇总,基础很重要!

2021前端面试题汇总,巩固基础

目录

HTML相关面试题

1.网络中使用最多的图片格式有哪些

  • jpg,图片支持颜色比较多,图片可以压缩,但是不支持透明,一般jpg用来保存颜色丰富的图片
  • gif,支持的颜色少,只支持简单的透明,支持动态图
  • png,png支持的颜色多,并且支持复杂的透明,可以用来显示颜色复杂的透明图片
  • webp,谷歌推出的专门用来表示网页中的图片的一种格式,具备以上图片格式的所有优点,文件特别小,但是兼容性不好
  • base64,将图片使用base64编码,这样可以将图片转换成字符,通过字符的形式来引入图片,一般都是一些需要和网页一起加载的图片才会使用base64

2. 音、视频标签的使用

视频标签:<video src=""></video>

标签内部的属性

  • src 需要播放的视频地址
  • autoplay 是否自动播放
  • controls 是否显示控制条
  • poster 没有播放之前要显示的占位图片
  • loop 是否循环播放
  • perload 预加载视频,与autoplay冲突,设置了autoplay属性,该属性就会失效
  • muted 静音模式

音频标签:<audio> <source src="" type=""> </audio>

音频标签的属性和视频标签的属性类似,但是poster属性不可以使用

3. HTML5新增的内容

  • 新增语义化标签
  • 新增表单类型
  • 新增表单元素
  • 新增表单属性
  • 新增表单事件
  • 新增多媒体标签
  • 新增canvas画布

4.HTML5新增语义化标签有哪些,以及语义化标签的优点

优点:

  • 提高可访问性
  • 结构清晰,利于维护

新增标签:

  • header,页面头部
  • main,页面主要内容
  • footer,页面的底部
  • nav,导航栏
  • aside,侧边栏
  • article,加载页面一块独立的内容
  • section,相当于div,块级元素
  • figure,加载独立内容(上图下字)
  • hgroup,标题组合标签
  • mark,高亮显示
  • dialog,加载对话框标签(必须配合open属性)
  • embed,加载插件的标签

5.行内元素和块元素

行内元素:相邻的行内元素会排列在同一行,不会独占一行,设置宽高无效

常见行内元素:a、br、img、input、textarea、span

块元素:独占一行,可设置宽高等属性

常见块元素:div、h1-h6、hr、p、ul、table、

6.iframe是什么?有什么缺点?

iframe元素会创建包含另一个文档的内联框架

缺点:

  • 会阻塞主页面的onload事件
  • 搜索引擎无法解读这种页面,不利于SEO
  • iframe和主页面共享连接池,而浏览器对相同区域有限制,所以影响性能

7.Doctype作用?严格模式和混杂模式如何区分,有何意义?

Doctype声明于文档的最前面,告诉浏览器以何种方式来渲染页面,有严格模式和混杂模式。

严格模式的排版和JS运作模式是以浏览器支持的最高标准进行。

混杂模式,向后兼容,模拟老式浏览器,防止浏览器无法兼容页面

CSS相关面试题

1. CSS3新增的特性

边框:
  • border-radios 添加圆角边框
  • border-shadow 给边框添加阴影
    • 可选值
      • 水平位移,垂直位移,模糊半径,阴影尺寸,阴影颜色,insetr(内、外部阴影)
  • boder-image 设置边框图像
  • border-image-source 边框图片的路径
  • boder-image-slice 图片边框向内偏移
  • border-image-width 图片边框的宽度
  • border-image-outset 边框图像区域超出边框的量
  • border-image-repeat 图片边框是否平铺
    • 可选值
      • repeat 平铺
      • round 铺满
      • stretch 拉伸
背景:
  • background-size 背景图片的尺寸
  • background-origin 规定background-positon属性相对于什么位置定位
  • background-clip 规定背景的绘制区域
    • 可选值
      • padding-box
      • border-box
      • content-box
渐变

渐变可以设置一些复杂的背景颜色,可以实现从一个颜色向其他颜色过渡的效果,渐变是图片,需要通过background-image来设置

  • linear-gradient 线性渐变

    颜色沿着一条直线发生变化,例如:linear-gradient(red,yellow) 红色在开头,黑色在结尾,中间是过渡区域,线性渐变的开头,我们可以指定一个渐变的方向。通过to left/right/bottom/top来指定。deg表示度数,turn表示圈。

  • radial-gradient 径向渐变

    该渐变是有中心向四周扩散,默认情况下径向渐变的形状是根据元素的形状来计算的,语法:radial-gradient(大小 at 位置,颜色 位置)

文本效果
  • word-break 定义如何换行
  • word-wrap 允许长的内容可以自动换行
  • text-overflow 指定当文本溢出包含它的元素干啥
  • text-show(水平位移,垂直位移,模糊半径,阴影颜色) 文字阴影
转换
  • transform 应用于2d,3d转换,可以将元素旋转,缩放,平移,倾斜
    • transform-origin 可以改变元素转换的位置(x,y,z轴)
    • transform-style 指定嵌套元素如何在三位空间中呈现
  • 2d转换方法
    • rotate旋转
    • translate(x,y) 指定元素在二维空间中的位移
    • scale(n) 定义缩放转换
  • 3d转换的方法
    • perspective(n) 为3d转换
    • translate
    • rotate
    • scale
过渡
  • transition-proprety 过渡属性名
  • transition-duration 完成过渡所花费的时间
  • transition-timing-function 指定切换效果的速度
  • transition-delay 指定何时开始切换效果
动画
  • animation-name 为@keyframes动画的名称
  • animation-duration 动画所花费的时间
  • animation-timing-function 动画如何完成一个周期
  • animation-delay 动画的延迟时间
  • animation-iteration-count 动画的播放次数
  • animation-direction 是否轮流反向播放动画

2.简述一下盒子模型

一个盒子从外到内可分为4个部分,margin外边距,border边框,padding内边距,content内容。默认情况下,盒子的宽高属性只是设置content的宽和高。

padding和border会影响可见框的总尺寸,margin不会。

盒子真正的高度应该是:内容高度+padding+border

画图如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-58aKVEgD-1630298526690)(D:\fore-end\Preparation for Job interview\images\盒子模型.jpg)]

3.清除浮动的方式有哪些?以及其各自的优点

高度塌陷问题:父元素没有设置高度,则父元素的高度默认被子元素撑开,当所有子元素开启浮动的时候,子元素就会脱离文档流,则父元素就会产生高度塌陷的问题,影响页面的布局。

清除浮动的方式:

  1. 给父元素单独定义高度

    • 优点:快速简单,代码少
    • 缺点:无法进行响应式布局
  2. 父元素开启Block Formatting Context 简称BFC

    • 设置元素浮动,使用这种方式开启,虽然可以撑开父元素,但是会导致父元素的宽度丢失,而且使用这种方式也会导致下边的元素上移,不能解决问题
    • 设置元素的绝对定位,直接写死,灵活度不够
    • 设置元素为inline-block,可以解决问题,但是宽度丢失
    • 将元素的overflow设置为一个非visible的值,将overflow设置为hidden是副作用最小的开启 BFC的方式,也是最推荐的方式
  3. 可以直接在高度塌陷的父元素的最后,添加一个空白的div,由于这个div并没有浮动,所以他是可以撑开父元素的高度的,然后对其进行清除浮动,这样可以通过这个空白的div来撑开父元素的高度。使用这种方式虽然可以结局问题,但是会在页面中添加多余的结构。

  4. 可以通过after伪类元素的最后添加一个空白元素,将其转换成一个块元素,然后对其清除浮动,这样做和添加一个div的原理一样,可以达到一个相同的效果,而且不会在页面中添加多余的div。为最推荐使用的方式。
    例如:.clearfix:after{
    content:"";
    display:block;
    clear:both;
    }

    经过修改的clearfix是一个多功能的,既可以解决高度塌陷,又可以确保父元素和子元素的垂直外边距不会重叠。
    .clearfix:before, .clearfix:after{
    content:"";
    display:table;
    clear:both;
    }

  5. 使用CSS的overflow属性,给浮动元素的容器添加overflow:hidden或者overflow:auto,在添加了overflow属性后,浮动元素又回到了容器层,把容器高度撑起,达到了清除浮动效果。

4. 谈谈定位

定位就是将指定元素摆放到网页的任意位置。通过position来设置元素的定位,position有以下可选值

  1. static,默认值,元素没有开启定位
  2. relative,开启元素相对定位,相对于自身定位
    • 相对定位是相对于元素在文档流中原来的位置进行定位,不设置偏移量时,元素不会发生变化
    • 开启相对定位后,元素不会脱离文档流,且会让元素提升一个层级
    • 相对定位不会改变元素的性质,块元素还是块元素,内联元素还是内联元素
  3. absolute,开启元素的绝对定位,相对于父级定位
    • 开启绝对定位,元素会脱离文档流,且会使元素提升一个层级,不设置偏移量,元素不会发生变化
    • 绝对定位是相对于离他最近的开启了定位的上级元素进行定位,如果上级元素都未开启定位,则会相对于浏览器的窗口进行定位
    • 绝对定位会改变元素的性质,内联元素会变成块元素,高度和宽度默认会被内容撑开
  4. fixed,开启元素的固定定位,固定定位也是一种绝对定位,它的大部分特点和绝对定位一样,不同的是:
    • 固定定位永远是相对于浏览器的窗口进行定位
    • 固定定位会固定在浏览器窗口的某个位置,不会随着滚动条滚动
  5. sticky,元素开启了粘滞定位,粘滞定位和相对定位的特点基本一致,不同的是:
    • 粘滞定位可以在元素到达某一位置时将其固定

5.子元素如何在父元素中居中

水平居中
  1. 子父元素宽度固定,子元素设置margin:auto,并且子元素不可以设置float,否则居中失效(子父元素都是块级元素)
  2. 子父元素宽度固定,父元素设置text-align:center,子元素设置display:inline-block,子元素不能设置float,否则居中失效
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<style>
  #box{
    width: 1000px;
    background-color: greenyellow;
  }
  #child{
    width: 100px;
    margin: auto;
    /* float: left; */
  }
  #box1{
    width: 1000px;
    background-color: yellowgreen;
    text-align: center;
  }
  #child1{
    width: 100px;
    display: inline-block;
    /* float: left; */
  }
</style>
<body>
  <div id="box">
    <div id="child">我是子元素</div>
  </div>

  <div id="box1">
    <div id="child1">我是子元素</div>
  </div>
</body>
</html>
垂直居中:
  1. 不知道父元素高度,子绝父相,子元素top:50%; transform:translateY(-50%)
  2. 弹性盒,父元素display:flex; 子元素 align-self:center;
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    #box1{
      width: 500px;
      height: 800px;
      background-color: yellowgreen;
      position: relative;
    }
    #child1{
      width: 100px;
      height: 100px;
      background-color: brown;
      position: absolute;
      top: 50%;
      transform: translateY(-50%);
    }

    #box2{
      width: 500px;
      height: 800px;
      background-color: yellowgreen;
      display: flex;
    }
    #child2{
      width: 100px;
      height: 100px;
      background-color: brown;
      align-self: center;
    }
  </style>
</head>
<body>
  <!-- 方式一 -->
  <div id="box1">
    <div id="child1"></div>
  </div>
  <hr>
  <!-- 方式二 -->
  <div id="box2">
    <div id="child2"></div>
  </div>
</body>
</html>
水平垂直居中:
  1. 父元素相对定位,子元素绝对定位,子元素top、left、right、bottom都设置为0,margin设置为auto
  2. 父元素设置display:table-cell;vertical-align:middle,子元素设置margin:auto
  3. 子元素相对定位,子元素top,left都为50%,translate(-50%,-50%)
  4. 子元素相对定位,父元素绝对定位,子元素top、left均为50%,translate(-50%,-50%)
  5. 父元素设置成弹性盒,display:flex; justfy-content:center; align-item:center;
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    
    #box1{
      width: 1000px;
      height: 400px;
      background-color: yellowgreen;
      position: relative;
    }
    #child1{
      width: 100px;
      height: 100px;
      background-color: pink;
      position: absolute;
      top: 0;
      right: 0;
      left: 0;
      bottom: 0;
      margin: auto;
    }
    #box2{
      width: 1000px;
      height: 400px;
      background-color: yellowgreen;
      display: table-cell;
      vertical-align: middle;
    }
    #child2{
      width: 100px;
      height: 100px;
      background-color: pink;
      margin: auto;
    }
    #box3{
      width: 1000px;
      height: 400px;
      background-color: yellowgreen;
    }
    #child3{
      width: 100px;
      height: 100px;
      background-color: pink;
      position: relative;
      top: 50%;
      left: 50%;
      transform:translate(-50%,-50%);
    }
    #box4{
      width: 1000px;
      height: 400px;
      background-color: yellowgreen;
      position: absolute;
    }
    #child4{
      width: 100px;
      height: 100px;
      background-color: pink;
      position: relative;
      top: 50%;
      left: 50%;
      transform:translate(-50%,-50%);
    }
    #box5{
      width: 1000px;
      height: 400px;
      background-color: yellowgreen;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    #child5{
      width: 100px;
      height: 100px;
      background-color: pink;
    }
  </style>
</head>

<body>
  
  <!-- 方式1 -->
  <div id="box1">
    <div id="child1"></div>
  </div>
  <hr>
  <!-- 方式2 -->
  <div id="box2">
    <div id="child2"></div>
  </div>
  <hr>
  <!-- 方式3 -->
  <div id="box3">
    <div id="child3"></div>
  </div>
  <hr>
  <!-- 方式4 -->
  <div id="box4">
    <div id="child4"></div>
  </div>
  <hr>
  <!-- 方式5 -->
  <div id="box5">
    <div id="child5"></div>
  </div>
  <hr>
</body>
</html>

6. CSS选择器有哪些,哪些属性可以继承,优先级如何计算?

选择器

元素选择器,语法:标签名{}

id选择器,通过元素的id属性值选中唯一的一个元素。语法:#id属性值{}

并集选择器,同时选中多个选择器对应的元素,语法:选择器1,选择器2,选择器N{}

交集选择器,可以选中满足多个选择器的元素,语法:选择器1选择器2选择器N{}

通配选择器,选中页面中的所有元素,语法:*{}

类选择器,为元素设立class属性值,通过元素的class属性值选中一组元素,语法:.class属性值{}

后代元素选择器,选中指定元素的指定后代元素,语法:祖先元素 后代元素{}

子元素选择器,选中指定父元素的指定子元素,语法:父元素>子元素{}

伪类选择器,伪类专门用来表示元素的一种特殊状态

  1. :link{}表示普通链接(未访问链接)
  2. :visited{}表示访问过的链接
  3. :hover{}伪类表示鼠标移入的状态
  4. :active{}表示的是超链接被点击的状态
  5. :focus{}表示文本框获取焦点以后,修改焦点样式
  6. :selection{}表示选中内容的样式
  7. :target{},代表一个特殊的元素,它的id是URI的片段标识符

涉及到超链接的伪类一共有4个,这四个的优先级是一样的,顺序为:link、visited、hover、active。

  • 伪元素:用来表示元素中的一些特殊位置
    • :first-letter{}为某个标签中第一个字符设置一个特殊样式
    • :first-line{}为某标签中第一行设置一个特殊样式
    • :before{content}表示某个标签中最前面的部分,一般与content这个样式一起使用,通过content可以向该标签最前面添加内容
    • :after{content}表示某个标签中最后面的部分,一般与content这个样式一起使用,通过content可以向该标签最后面添加内容

属性选择器:可以根据元素中的属性或属性值来选择去指定元素

语法:

  1. [属性名] 选取含有指定属性的元素
  2. [属性名=“属性值”] 选取含有指定属性值的元素
  3. [属性名^=“属性值”] 选取属性值以指定内容开头的元素
  4. [属性名$=“属性值”] 选取属性值以指定内容结尾的元素
  5. [属性名*=“属性值”] 选取属性值包含指定内容的元素

title属性,这个属性可以给任何标签指定。当鼠标移入到元素上时,元素中的title属性值会将作为提示文字显示。

子元素选择器

  1. :first-child{}表示选中第一个子元素
  2. :last-child{}表示选中最后一个子元素
  3. :nth-child(){}表示可以选中任意位置的子元素。该选择器后边()可以指定一个参数,指定要选中第几个元素。 even表示偶数位置的子元素。odd表示奇数位置的元素。
  4. :first-of-type , :last-of-type , nth-of-type与之前三种类似,只不过child是在所有的子元素中排序,而type是在当前类型的子元素中排序。

兄弟元素选择器

  1. 可以选中一个元素后紧挨着后一个的兄弟元素。语法:前一个标签+后一个标签{} 。
  2. 可以选中后边的所有兄弟元素。语法:前一个标签~后边标签{}

否定伪类:可以从已选中的元素中剔除某些元素

  • 语法: 标签名:not(选择器){}
选择器的优先级:
  • 内联样式,优先级1000

  • id选择器,优先级100

  • 类和伪类,优先级10

  • 元素选择器,优先级1

  • 通配,优先级0

  • 继承的样式没有优先级

  • 注:当选择器中包含多种选择器时,需要将多种选择器的优先级相加然后在比较,但是,选择器优先级计算不会超过他的最大数量级,如果选择器的优先级一样,则使用靠后的优先级。在样式的最后,添加一个!important,此时该样式将获得一个最高的优先级,优先于所有的样式甚至超过内联样式

样式的继承

CSS继承特性主要是文本方面

  • 所有元素可继承:visibility和cursor
  • 块级元素科可继承:text-indent和text-align
  • 列表元素可继承:list-style,list-style-type,list-style-position,list-style-image
  • 内联元素可继承:字母间距leeter-spacing,段落间距word-spacing,行高line-height,字体颜色color,字体种类font,文本修饰text-decoration,文本方向text-decoration

7.margin和padding的使用场景

  • margin外边距,自身边框到另一个边框的距离
  • padding内边距,自身边距到自身内容之间的距离
  • 需要在border外侧添加空白是用margin,需要在border内侧添加空白时用padding

8.简述弹性盒以及布局属性

flex弹性盒可以使元素具有弹性,让元素跟随页面大小的改变而改变。

布局原理:通过给父盒子添加flex属性,来控制盒子的位置和排列方式

使用弹性盒,必须先将一个容器设置为弹性容器,通过display来设置弹性容器。

主轴:弹性元素的排列方向为主轴

侧轴:与主轴反向垂直的方向为侧轴

  • display:flex 设置块级弹性容器
  • display-inline-flex 设置行内弹性容器

当设置为弹性容器时,可以指定以下属性

  • flex-direction 用来指定容器中弹性元素的排列方式,可选值

    • row,默认值,弹性元素在容器中水平排列(左到右),主轴 自左向右
    • row-reverse 弹性元素在容器中反向水平排列
    • column,弹性元素纵向排列(自上向下)
    • column-reverse 弹性元素方向纵向排列
  • flex-wrap 设置弹性盒子的子元素超出父容器时是否换行

    • nowrap,默认值,元素不会自动换行
    • wrap,元素沿着侧轴自动换行
    • wrap-reverse,元素沿着侧轴反方向换行
  • flex-flow, 是flex-direction和flex-wrap的简写形式

    • flex-flow:row wrap
  • justify-content用来分配主轴上的空白空间,可选值:

    • flex-start,元素沿着主轴始边排列
    • flex-end,元素沿着主轴终边排列
    • center,元素居中排列
    • space-around,空白分布到元素两侧
    • space-between,空白均匀分布到元素之间
    • space-evenly,空白分布到元素单侧
  • align-items,元素在侧轴上如何对齐,元素之间的关系

    • stretch,默认值,将元素的长度设置为相同的值
    • flex-start,元素不会拉伸,沿着侧轴始边对齐
    • flex-end,沿着侧轴的终边对齐
    • center,居中对齐
    • baseline,基线对齐

**弹性元素:**弹性容器的直接子元素,一个元素可以同时是弹性容器和弹性元素

弹性元素的属性

  • flex-grow 指定弹性元素的伸展系数,当父元素有多余的空间时,子元素会按照比例进行分配。

  • flex-shrink 指定弹性元素的收缩系数,当父元素的空间不足以容纳所有的子元素时,子元素会按照比例进行收缩。

  • align-self,用来覆盖当前弹性元素上的align-items。

  • flex-basis,指定的是元素在主轴上的基础长度,如果主轴是横向的,则该值指定的就是元素的宽度;如果主轴是纵向的,则该值指定的就是元素的高度;默认值是auto,表示参考元素自身的高度或宽度,如果传递了一个具体的数值,则以该值为准。

  • flex为简写属性,可以设置弹性元素的的三个样式。

    • flex:增长 缩减 基础;默认为initial
    • initial表示为"0 1 auto"
      auto表示为"1 1 auto"
    • none表示为"0 0 auto" 弹性元素没有弹性。
  • order 设置弹性元素的顺序。顺序越小,排列越靠前,默认为0

9.如何实现元素的垂直居中

  1. 父元素设置display:flex;align-items:center;
  2. 父元素table布局,子元素设置vertical-align:center
  3. 元素绝对定位,top:50%,margin-top:(高度/2)
  4. 高度不确定用transform:translateY(-50%)

10.overflow的原理

块格式化上下文是css可视化渲染的一部分,它是一块区域,规定了内部块盒的渲染方式,以及浮动相互之间的影响关系,当元素设置了overflow样式且值不为visible时,元素就构建了一个BFC,BFC在计算高度时,内部浮动元素的高度也计算在内,也就是说技术BFC区域内只有一个浮动元素,BFC的高度也不会发生塌陷,所以达到了清除浮动的目的。

11.画出三角形

三角形原理:边框的均分原理

div {
  width:0px;
  height:0px;
  border-top:10px solid red;
  border-right:10px solid transparent;
  border-bottom:10px solid transparent;
  border-left:10px solid transparent;
}

12.link标签和import的区别

link标签属于html标签,import是css提供的;页面被加载时,link会被同时加载,而import引用的css会等到页面加载结束后加载;link方式的样式权重高于import。

13.说一说BFC什么?

BFC块级格式化上下文,页面的隐含属性,全名:Block Formatting Context ,用于清除浮动,防止margin重叠等。

当开启BFC以后,元素会具备以下特性:

  1. 父元素的垂直外边距不会和子元素重叠
  2. 开启BFC的元素不会被浮动元素所覆盖
  3. 开启BFC的元素可以包含浮动的子元素

14.多行元素的文本省略号

display: -webkit-box
-webkit-box-orient:vertical
-webkit-line-clamp:3
overflow:hidden

15.visivility=hidden,opacity=0,display:none三者之间的区别

  • opacity=0,该元素影藏起来,不改变页面布局,如果元素绑定了事件,该元素也还会触发该事件
  • visivility=hidden,该元素影藏起来,不改变页面布局,元素绑定了事件,不会触发事件
  • display:none,把元素影藏起来,会改变页面布局

16.inline-block、inline和block的区别

  • block是块级元素,能设置宽高,margin、padding水平垂直方向都有效
  • inline,设置宽高无效,margin在竖直方向上无效,padding在4个方向都有效
  • inline-block,能设置宽高,margin、padding4个方向均有效

17.了解重绘和重排吗,知道怎么去减少重绘和重排吗

DOM的变化影响到了预算的几何属性,比如宽高,浏览器重新计算元素的几何属性,其他元素的几何属性也会受到影响,浏览器需要重新构造渲染树,这个过程称之为重排;浏览器将受到影响的部分重新绘制在屏幕上的过程称为重绘。

引起重排重绘的原因:

  • 添加或者删除DOM元素
  • 元素尺寸位置改变
  • 浏览器页面初始化
  • 浏览器窗口大小发生改变

重排一定导致重绘,重绘不一定导致重排

减少重绘重排的方法:

  • 不在布局信息改变时做DOM查询
  • 对于多次重排的元素,比如说动画,使用绝对定位使其脱离文档流,使其不影响其他元素

JavaScript相关面试题

1. JavaScript的数据类型有哪些?并且他们如何判断?

  • 基本数据类型:

    • 字符串——String,typeof 判断
    • 数字——Number,typeof 判断
    • 布尔——Boolean,typeof 判断
    • null——null,=== 判断
    • undefined——undefined,typeof、=== 判断
  • (对象)引用类型

    • 对象Object—— typeof、instanceof 判断
    • 数组 Array—— instanceof 判断
    • Function —— typeof 判断

判断:

  • typeof:

    • 可以判断:undefined、数字、字符串、布尔值
    • 不能判断: null与object object与array
  • instanceof:

    • 可以判断对象的具体类型
  • ===

    • 可以判断undefined、null

undefined与null的区别?

  • 相同点
    • 用if判断时,两者都被转成false
  • 不同点
    • undefined表示定义未赋值
    • null表示赋值了,值为null
    • number的转值不同,number(null)为0,number(undefined)为NaN

2. 原生发送ajax的步骤,以及常见的ajax响应状态码

  1. 创建XMLHTTPRequest对象

    var xhr = new XMLHttpRequest()
    
  2. 使用open设置请求信息

    xhr.open(method, url);
    //可以设置请求头,一般不设置
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    
  3. 使用send发送请求

    xhr.send(body) //get 请求不传 body 参数,只有 post 请求时传入
    
  4. 接收响应,更新页面

    //xhr.responseXML 接收 xml 格式的响应数据
    //xhr.responseText 接收文本格式的响应数据
    xhr.onreadystatechange = function (){
      if(xhr.readyState == 4 && xhr.status == 200){
        var text = xhr.responseText;
        console.log(text);
      }
    }
    

常见状态码

  • 100,这个状态码是告诉客户端应该继续发送请求,这个临时响应是用来通知客户端的,部分请求的服务器已经接受,但是客户端应继续发送请求,请求剩余的部分,如果请求已经完成,就忽略这个响应,而且服务器会在请求完成后向客户发送一个最终的结果==(告诉客户端继续发送请求,请求剩余的部分)==
  • 200:这个表示服务器已经成果接受请求,并将返回客户端所请求的最终结果==(服务器完成接收,并将客户端请求结果返回给客户端)==
  • 202:表示服务器已经接受了请求,但是还没有处理,而且这个请求最终会不会处理还没确定==(服务器已经接收了请求,还没有处理,会不会处理还不知道)==
  • 204:服务器成功处理了请求,但是没有返回任何实体内容,可能会返回新的头部元信息==(服务器成果处理了请求,但是没有返回任何实体内容)==
  • 301:客户端请求的网页已经永久移动到新的位置,当链接发生变化时,返回301代码告诉客户端链接的变化,客户端保存新的链接,并向新的链接发出请求,已返回请求结果
  • 404:请求失败,客户端请求的资源没有找到或者是不存在
  • 500:服务器遇到未知错误,导致无法完成客户端当前请求
  • 503:服务器由于临时的服务过载或者是维护,无法解决当前的请求

3. 如何判断一个数据是NaN

NaN 非数字,但是用typeof检测是number类型

  • 利用NaN是唯一 一个不等于自身的特点去判断 NaN === NaN ——false

  • 利用ES6的Object.is()方法,判断两个值是否相等

4.闭包是什么?有什么特性?对页面有什么影响?

闭包就是能够读取嵌套函数内部变量的函数,子函数所在的父函数的作用域不会被释放。

  • 如何产生闭包?

    • 当一个嵌套的内部(子)函数引用了嵌套的外部 (父) 函数的变量(函数)时,就产生了闭包。
  • 闭包是什么?

    • 理解一:闭包就是嵌套的内部函数
    • 理解二:包含被引用变量(函数)的对象

    注:闭包存在于嵌套的内部函数中

  • 常见的闭包

    • 将函数作为另一个函数的返回值
    • 将函数作为实参传递给另一个函数调用
  • 产生闭包的条件

    • 函数嵌套
    • 内部函数引用了外部函数的数据(变量、函数)
  • 闭包的特性

    • 函数嵌套函数
    • 内部函数可以引用外部函数的参数和变量
    • 参数和变量不会被垃圾回收机制回收
  • 使用:

    • 读取函数内部的变量
    • 这些变量的值始终保持在内存中,不会在外层函数调用后被自动清除
  • 优点:

    • 变量长期驻扎在内存中
    • 避免全局变量污染
    • 私有成员的存在
  • 缺点

    • 会造成内存泄漏,什么是内存泄漏?

      • 内存泄漏是指一块被分配的内存既不能使用又不能回收,直到浏览器进程结束

      • 释放内存的方法:赋值为null

5.JS中常见的内存泄漏

  1. 意外的全局变量
  2. 被遗忘的计时器或回调函数
  3. 脱离DOM的引用
  4. 闭包

6.事件委托是什么?如何确定事件源(Event.target 谁调用谁就是事件源)

事件委托就是利用事件冒泡,只制定一个事件处理程序,就可以管理某一类型的所有事件。

事件委托称事件代理,是JS中很常用的绑定事件的技巧,事件委托就是把原本需要绑定在子元素的事件委托给父元素,让父元素监听该事件,事件委托的原理就是DOM元素的事件的冒泡

7.什么是事件的冒泡?

当一个事件触发后,户籍在子元素和父元素之间传播,这种传播分为3个阶段

  • 捕获阶段
    • 从window对象传导到目标节点(从外到里),这个阶段不会响应任何事件
  • 目标阶段
    • 在目标节点上触发
  • 冒泡阶段
    • 从内目标节点导回到window对象(从里到外)

事件委托/事件代理 就是利用事件的冒泡机制把里层需要响应的事件绑定到外层

8.session、cookie、localStorage以及sessionStorage的区别

  • cookie的特点
    1. cookie由服务端产生,是保存在浏览器端的一小段文本信息
    2. cookie是以键值对的形式进行存储
    3. 浏览器在访问一个网站的服务器时,会自动在请求头中把和本网站相关的cookie发送给服务器
    4. cookie是基于域名安全的
    5. cookie有过期时间,默认关闭浏览器之后过期,也可以手动设置过期时间
  • session的特点:
    1. session数据保存在服务器端
    2. session以键值对方式存储
    3. session依赖于cookie,每个session信息对应的客户端标识保存在cookie中
  • localStorage是HTML5标准中新加入的技术,用于本地存储

session与cookie的区别和联系

区别:

  1. cookie是由服务端产生,保存到浏览器
  2. session是保存在服务器端

联系:

  1. 二者都是以键值对方式存储
  2. session依赖于cookie,每个session信息对应的客户端标识保存在cookie中

sessionStorage、localStorage和cookie的区别
共同点:都是保存在浏览器端、且同源的
区别:

  1. cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递,而sessionStorage和localStorage不会自动把数据发送给服务器,仅在本地保存。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下
  2. 存储大小限制也不同,cookie数据不能超过4K,同时因为每次http请求都会携带cookie、所以cookie只适合保存很小的数据,如会话标识。sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大
  3. 数据有效期不同,sessionStorage:仅在当前浏览器窗口关闭之前有效;localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie:只在设置的cookie过期时间之前有效,即使窗口关闭或浏览器关闭
  4. 作用域不同,sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面;localstorage在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的
  5. web Storage支持事件通知机制,可以将数据更新的通知发送给监听者
  6. web Storage的api接口使用更方便

浏览器本地存储与服务器端存储的区别
其实数据既可以在浏览器本地存储,也可以在服务器端存储
浏览器可以保存一些数据,需要的时候直接从本地存取,sessionStorage、localStorage和cookie都是由浏览器存储在本地的数据
服务器端也可以保存所有用户的所有数据,但需要的时候浏览器要向服务器请求数据。
1、服务器端可以保存用户的持久数据,如数据库和云存储将用户的大量数据保存在服务器端
2、服务器端也可以保存用户的临时会话数据,服务器端的session机制,如jsp的session对象,数据保存在服务器上,

实际上,服务器和浏览器之间仅需传递session id即可,服务器根据session id找到对应用户的session对象,会话数据仅在一段时间内有效,这个时间就是server端设置的session有效期

服务器端保存所有的用户的数据,所以服务器端的开销较大,而浏览器端保存则把不同用户需要的数据分别保存在用户各自的浏览器中,浏览器端一般只用来存储小数据,而非服务可以存储大数据或小数据服务器存储数据安全一些,浏览器只适合存储一般数据

9.var与let、const的区别

  1. var 声明的变量会挂载在window上,而let和const声明的变量不会
  2. var 声明的变量存在变量提升,let和const声明的变量不存在变量提升
  3. 同一作用域下,var可以声明同名的变量,let、const不可以
  4. let、const声明的变量都存在块级作用域
  5. const一旦声明就必须要赋值,不能用null占位,且声明后不可以再修改,如果声明的是对象或数组类型的数据,可以修改属性

10.什么是面向对象,请简述

面向对象是一种思想,是基于面向过程而言的,就是说面向对象是将功能等通过对象来实现,将功能封装进对象之中,让对象去实现具体的细节。这种思想是将数据作为第一位,这是对数据一种优化,操作起来更加方便,简化了过程

JS本身是没有class类型的,但是每个函数都一个prototype属性,prototype指向一个对象,当函数作为构造函数时,prototype就起到类似于class的作用

面向对象的三大特性

  • 封装(隐藏对象的属性和实现细节,对外提供公共的访问方式)
  • 继承(提高代码复用性,继承是多态的前提)
  • 多态(是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象)

11.构造函数与普通函数的区别

  1. 构造函数也是一个普通函数,创建方式和普通函数一样,但是构造函数习惯上首字母大写
  2. 调用方式不一样,普通函数直接调用,构造函数要用关键字new来调用
  3. 调用时,构造函数内部会创建一个新对象,就是实例,普通函数不会创建新对象
  4. 构造函数内部的this指向实例对象,普通函数内部的this指向调用函数的对象(如果没有对象调用,默认为window)
  5. 构造函数默认的返回值是创建的对象(也就是实例),普通函数的返回值由return语句决定
  6. 构造函数的函数名要与类名相同

12.简述原型、原型链、原型继承

原型prototype:任何对象实例都有一个原型,也叫原型对象,原型对象由对象的内置属性_proto_指向它构造函数的prototype指向的对象,即任何对象都是由一个构造函数创建的,但是不是每一个对象都有prototype,只有方法才有prototype

  • 每个函数都有一个prototype属性,它默认指向一个Object的空对象,即为原型对象
  • 原型对象中有一个属性constructor,它指向函数对象
  • 给原型对象添加属性(一般都是方法)
    • 作用:函数的所有实例对象自动拥有原型中的方法

显式原型与隐式原型

  • 每个构造function都有一个prototype,即为显式原型

  • 每个实例对象都有一个_proto_,即为隐式原型

  • 对象的隐式原型的值为其构造函数的显式原型的值

  • 总结:

    • 函数的prototype属性:在定义函数时自动添加,默认是一个空对象
    • 对象的_proto_属性:创建对象时自动添加,默认值为构造函数的prototype属性值
    • 在ES6之前,可以直接操作显式原型,不可直接操作隐式原型
    • 实例对象的_proto_隐式原型指向它构造函数的prototype显式原型
        <script>
           //  定义构造函数
           function Fn(){
               // 内部语句:this.prototype = {}
     
           }
     
           // 1.每个函数function都有一个prototype,即为显示原型属性,默认指向一个空的Object对象
           console.log(Fn.prototype);
     
           // 2.每个实例对象都有一个__protp__,可称为隐式原型
           // 创建实例对象
           var fn = new Fn();
           // 内部语句:this.__proto__ = Fn.prototype
           console.log(fn.__proto__);   
     
           // 3.对象的隐式原型的值为其对应构造函数的显示原型的值
           console.log(Fn.prototype === fn.__proto__); //true
     
           // 给原型对象添加方法
           Fn.prototype.test = function(){
               console.log("test()");
           }
     
           // 通过实例调用原型的方法
           fn.test(); 
     
        </script>
    

    显示原型与隐式原型的内存结构图
    在这里插入图片描述

原型链

当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去它的__proto__隐式原型上查找,即它的构造函数的prototype,如果还没有找到就会再在构造函数的prototype的__proto__中查找,这样一层一层向上查找就会形成一个链式结构,我们称为原型链

  • 原型链的核心就是依赖对象的_proto_的指向,当自身不存在某一属性时,就会沿着_proto_这条链去查找创建对象的构造函数,一直到Object,此时Object上就没有_proto_的指向了。

  • 因为_proto_实际上找的是prototype,所以我们只要找到这条链上的构造函数的prototype。原型链的尽头是Object,Object的prototype是没有_proto_属性的,它为null。

  • 每个构造函数都有一个原型对象,原型对象都包含了一个构造函数的指针,而实例都包含指向原型对象内部的指针。我们让原型对象1等于另一个原型对象的实例2,此时原型对象2将包含一个指向原型对象1的指针,再让原型对象2的实例等于原型对象3,这样层层递进就构成了实例和原型的链条,这就是原型链的概念。

  •     <script>
     
           //  console.log(Object);
           // console.log(Object.prototype);
           console.log(Object.prototype.__proto__);
            function Fn(){
                this.test1 = function(){
                   console.log("test1()");
                };
            }
     
            console.log(Fn.prototype);
            Fn.prototype.test2 = function(){
               console.log("test2()");
            };
     
            var fn = new Fn();
     
            fn.test1();
            fn.test2();
            console.log(fn.toString());
            console.log(fn.test3);
           //  fn.test3();
     
           /* 1.函数的显示原型指向的对象默认是空Object实例对象(但Object不满足) */
           console.log(Fn.prototype instanceof Object); //true
           console.log(Object.prototype instanceof Object); //false
           console.log(Function.prototype instanceof Object); //true
     
           /* 2.所有函数都是Function的实例(包含Function) */
           console.log(Function.__proto__ === Function.prototype);
     
           /* Object的原型对象是原型链的尽头 */
           console.log(Object.prototype.__proto__); //null
       </script>
    

    原型链图解
    在这里插入图片描述

原型继承

  • 原型继承是JS的一种继承方式,原型链作为实现继承的主要方法,其基本思路是利用原型让一个引用类型继承里一个引用类型的属性和方法。
  • 利用原型中的成员可以被和其他相关的对象共享这一特性,可以实现继承,这种实现继承的方式称为原型继承。

13.谈谈对Promise的理解

Promise是JS中进行异步编程的解决方案,相比回调函数和事件更加合理强大,从语法上讲,promise是一个构造函数,从功能上讲,promise对象用来封装一个异步操作并可以获取其结果。

Promise有3中状态

  • pending,也叫等待状态

  • fulfiled,成功状态

  • rejected,失败状态

    状态一旦改变,就不会再变,无论成功、失败,都会得到一个结果数据,成功的结果数据一般称为value,失败的结果数据一般称为reason

Promise的2个特点

  1. Promise对象的状态不受外界影响
  2. Promise的状态一旦改变,就不会再改变,状态不可逆

Promise的3个缺点

  1. 无法取消Promise,一旦新建就会立即执行,无法中途取消
  2. 如果不设置回调函数,promise内部抛出错误,不会反映到外部
  3. 当处于pending状态时,无法得知目前进展到哪一个阶段,是刚刚开始还是即将完成

用Promise来解决什么问题?

Promise是用来解决两个问题的:

  1. 支持链式调用,解决回调地狱,代码难以维护

    • 什么是回调地狱?

      回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调执行的条件。

      • 缺点
        • 不便于阅读
        • 不便于异常的处理
  2. promise可以支持多并发的请求,获取并发请求中的数据,这个promise可以解决异步的问题,本身不能说promise是异步的

Promise的几个关键问题

  1. 如何改变promise的状态?

    • resolve(value):如果当前是pending,就会改变为resolved
    • reject(reason):如果当前是pending,就会改变为rejected
    • 抛出异常:如果当前是pending,就会改变为rejected
  2. 一个promise指定多个成功、失败的回调函数,都会调用吗?

    当promise改变为对应状态时都会调用

  3. 改变promise状态和指定回调函数谁先谁后?

    • 都有可能,正场情况下是先指定回调函数再改变状态,但也可以先改变状态再指定回调
    • 如何先状态再指定回调?
      • 在执行器中直接调用resolve()\reject()
      • 延迟更长的事件才调用then()
    • 什么时候才能得到数据?
      • 如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据
      • 如果先改变状态,那当指定回调时,回调函数就会调用,得到数据
  4. promise.then()返回的新promise的结果状态由什么决定?

    简单表达:由then()指定的回调函数执行的结果决定

    详细表达:

    • 如果抛出异常,新promise变为rejected,reason为抛出的异常
    • 如果返回的是非promise的任意值,新promise变为resolved,value为返回的值
    • 如果返回的是另一个新的promise,此promise的结果就会成为新的promise的结果
  5. promise如何串联多个操作任务?

    • promise的then()返回一个新的promise,可以看成then()的链式调用
    • 通过then()的链式调用串联多个同步、异步任务
  6. promise的异常穿透?

    • 当使用then()的链式调用时,可以在最后指定失败的回调
    • 前面任何操作出现了异常,都会传到最后失败的回调中处理
  7. 中断promise链?

    当使用promise的then()的链式调用时,在中间中断,不在调用后面的回调函数。

    解决方案:在回调函数中返回一个pending状态的promise对象

async与await的用法

async

  • 函数的返回值为promise对象
  • promise对象的结果由async函数执行的返回值决定

awiat

  • await右侧的表达式一般为promise对象,但也可以是其他的值
  • 如果右侧表达式是promise对象,await返回的值就是promise成功的值
  • 如果表达式是其他值,直接将此值作为await的返回值

注:

  • await必须写在async函数中,但async函数可以没有await
  • 如果await的promise失败了,就会抛出异常,通过try…catch捕获处理

14.JS的new操作符做了哪些事情?

new操作符新建了一个空对象,这个对象原型指向构造函数的prototype,执行构造函数后返回这个对象

15.改变函数内部this指针的指向函数(bind,apply,call)的区别

apply:调用一个对象的方法,用另一个对象替换当前对象

call:调用一个对象的方法,用另一个对象替换当前对象

通过apply和call改变函数的this指向,他们两个函数的第一个参数都是表示要改变指向的那个对象,对于第二个参数,apply是数组,而call则是arg1,arg2…这种形式。

通过bind改变this作用域会返回一个新的函数,这个函数不会马上执行

16.JS的位置,clientHeight,scrollHeight,offsetHeight以及scrollTop,offsetTop,clientTop的区别?

clientHeight:表示的是可视区域的高度,不包含border和滚动条

offsetHeight:表示可视区域的高度,包含border和滚动条

scrollHeight:表示了所有区域的高度,包含了因为滚动被隐藏的部分

clientTop:表示边框border的高度,在未指定的情况下一般为0

offsetTop:当前元素距浏览器边界的偏移量

scrollTop:表示已经滚动到元素的上边界的高度

17.JS拖拽功能的实现

首先需要用到3个事件,mousedown,mousemove,mouseup

当鼠标点击按下的时候,需要一个tag标识此时已经按下,可以执行mousemove里面的具体方法。clientX,clientY标识的是鼠标的横纵坐标,并且我们用offsetX和offsetY来表示元素的初始坐标。

移动的距离应该是:鼠标移动时候的坐标-鼠标按下去的坐标

定位的信息为:鼠标移动时候的坐标-鼠标按下去的坐标+元素初始情况下的offsetLeft

**注:**拖拽的同时是绝对定位,我们改变的是绝对定位条件下的left以及top等值

18.JS中的垃圾回收机制

为什么需要垃圾回收机制?

由于字符串、对象、数组没有固定的大小,所以当他们的大小已知时,才能对他们进行动态的存储分配。JavaScript每次创建字符串、数组、对象时,解释器都必须分配内存来储存那个实体,只要像这样动态地分配了内存,最终都要释放这些内存以便他们能够被再用,否则JavaScript的解释器将会消耗完系统中所有的可用内存,造成系统崩溃。

JavaScript有自己的一套垃圾回收机制,JavaScript的解释器可以检测到任何时候程序不再使用一个对象,当他确定了一个对象是无用的时候,他就不需要这个对象,可以把它所占用的内存释放掉了。

垃圾回收机制的方法?

  1. 标记清除

    这是最常见的垃圾回收方式,当变量进入环境时,就标记这个变量为“进入环境”,从逻辑上讲,永远不能释放进入环境的变量所占的内存,永远不能释放进入环境变量所占用的内存,只要执行流程进入相应的环境,就可以用到他们,当离开环境时,就标记为离开环境。

    垃圾回收期在运行的时候会给存储在内存中的变量都加上标记,然后去掉环境变量中的变量,以及被环境变量中的变量所引用的变量,删除所有被标记的变量,删除的变量无法在环境变量中被访问,最后垃圾回收期完成了内存的清除工作,回收他们所占用的内存。

  2. 引用计数法

    当声明一个变量,并用一个引用类型的值赋给变量时,这个值的引用次数为1;相反,如果包含了对这个值引用的变量又取得了另外一个值,则原先的引用之引用的次数就减1,当这个值的引用次数为0时,说明没办法再访问这个值了,就会把它所占的内存给回收劲来,这样垃圾收集器再运行的时候,就会释放引用次数为0的这些值。

    但是用该方法会存在内存泄漏:

    function problem(){
    	var objA = new Object();
    	var objB = new Object();
    	objA.someOtherObject = objB
    	objB.anotherObject = objA
    }
    

    上述例子:objA和objB通过各自的属性相互引用,这样的话,两个对象的引用次数都为2,再该方式中,由于函数执行之后,这两对象都离开了作用域,函数执行完之后,因计数不为0,这样的相互引用如果大量存在就会导致内存泄露。

19.JS监听对象属性的改变

假设有一个user对象

在ES5中可以通过Object.defineProperty来实现已有属性的监听

Object.defineProperty(user,'name',{
	set:function(key,value){}
})

缺点:如果id不存在user对象中,则不能监听id的变化

在ES6中可以使用Proxy实现

var user = new Proxy({},{	
	set:function(targer,key,value,receiver){		
}})

这样,即使属性id不在user中,通过user.id来定义也同样可以监听该属性的变化

20.实现一个bind函数

原理:通过apply或者call方法

// 初始版本
Function.prototype.bind = function (obj,arg) {
  var arg = Array.prototype.slice.call(arguments,1)
  var context = this
  return function (newArg) {
    arg = arg.concat(Array.prototype.slice.call(newArg))
    return context.apply(obj.arg)
  }
}

// 考虑到原型链
// 因为在new一个bind过程中生成的新函数,必须的条件是要继承原函数的原型
Function.prototype.bind = function (obj,arg) {
  var arg = Array.prototype.slice.call(arguments,1)
  var context = this
  var bound = function (newArg) {
    arg = arg.concat(Array.prototype.slice.call(newArg))
    return context.apply(obj,arg)
  }
  var F = function () {
    F.prototype = context.prototype
    bound.prototype = new F()
    return bound
  }
}

21.讲讲JS语言的特性

JS运行在客户端浏览器上,不用预编译,直接解析执行代码;是弱类型语言,较为灵活;与操作系统无关,跨平台的语言;脚本语言、解释性语言

为什么js是弱类型语言:弱类型语言实现是相对于强类型语言来说的,在强类型语言中,变量的类型有多种,不同的类型相互转换有时需要强制转换,而JS只有一种类型var,为变量赋值时会自动判断类型并转换,所以是所类型的。

22.JS中的this指向

在JavaScript中,this通常指向的是我们正在执行的函数本身,或者是,指向该函数所属的对象。

全局的this ==> 指向的是window

对象中的this ==> 指向的是其本身

事件中的this ==> 指向的是事件对象

23.JS数组的去重方式

  • 使用indexof()、lasrindexof()方法
  • 使用ES6的set结构和扩展运算符,set不接受重复数据Array.from(new Set(array))
  • 使用sort方法先将原数组排序,然后与相邻的比较,如果不同则存入新的数组
  • 使用filiter和indexof方法
  • 使用set和Array.from()方法,该方法可以将set结构转成数组
  • 使用splice和双层循环
  • 使用includes方法

24.深浅拷贝是什么?如何实现?

深拷贝:指针赋值,并且内容拷贝

浅拷贝:知识简单的指针赋值

数组浅拷贝:如果是数组,可以使用数组的一些方法实现,slice(),concat()返回一个新数组的特性实现拷贝

数组深拷贝:用扩展运算符spread实现数组深拷贝,JSON.parse(JSON.stringify())不仅适用于数组,还适用于对象,不能拷贝函数、undefined,symbol。

25.for循环和map循环有什么区别?

for遍历自身对象和继承可枚举的属性,也就是说会包括那些原型链上的属性

map方法不会对空数组进行检测,map会返回一个新数组,不会对原数组产生影响。

27.同步与异步的区别/阻塞与非阻塞的区别

同步(阻塞)

异步(非阻塞)

就好比两人一块上班:

  • 同步:到饭点了,我喊你去吃饭,你在工作,我就等你工作完一块去吃饭
  • 异步:到饭点了,我喊你去吃饭,你在工作,我就自己先去了,你工作完再去

同步和异步这两个关注的是在等待调用结果时的状态

28、重绘和回流是什么?

回流:当render tree中的一部分或者全部因为元素的规模尺寸,布局,隐藏等改变,而需要重新构建,这就叫回流,每个页面至少需要一次回流,就是在页面第一次加载的时候,这个时候一定会发生回流,因为要构建render tree。

重绘:在回流时,浏览器会使渲染树中受到影响的部分失效,重新构造这部分的渲染树,完成回流后,浏览器会重新绘制受影响的部分到屏幕中,这就是重绘。

当render tree中的一些元素需要更新属性而这些属性只是影响元素的外观,不影响布局,这就叫重绘。

29.箭头函数与普通函数的区别?

  1. 箭头函数是匿名函数,不能作为构造函数,不能使用new
  2. 箭头函数不能绑定arguments,要用rest参数解决
  3. 箭头函数没有原型属性
  4. 箭头函数的this永远指向其上下文的this
  5. 箭头函数不能绑定this,会捕获所在的上下文this的值,作为自己的this的值

30.Js的函数节流和函数防抖的区别?

函数节流:指的是一定时间内js方法只执行一次;只允许一个函数在 X 毫秒内执行一次。

<style>
    body{
      height: 5000px;
    }
  </style>
<body>
  <script>
    // 简单的节流函数
    function throttle(func,wait,mustRun) {
      var timeout
      var startTime = new Date()

      return function(){
        var context = this
        var args = arguments
        var curTime = new Date()

        clearTimeout(timeout)

        // 如果达到了规定的触发事件,触发函数
        if (curTime - startTime >= mustRun) {
          func.apply(context, args)
          startTime = curTime
        }else{
          // 没到到触发间隔,重新设定定时器
          timeout = setTimeout(func,wait)
        }
      }
    }
    // 正常的事件
    function Scroll(){
      console.log('scroll ...');
    }
    // 采用节流函数
    // window.addEventListener('scroll',throttle(Scroll,1000,2000))

    // 不采用节流
    window.addEventListener('scroll',Scroll)
  </script>
</body>

函数防抖:是指频繁触发的情况下,只有足够的空闲时间,才执行代码一次;把多个顺序地调用合并成一次,也就是在一定时间内,规定事件被触发的次数。

<style>
    body{
      height: 2000px;
    }
  </style>
<body>
  <script>
    // 简单的防抖函数
    function debounce(func,wait,immediate){
      // 声明定时器变量
      var timeout
      return function(){
        // 每次触发scroll handler时先清除定时器
        clearTimeout(timeout)
        // 指定 xx 秒后 触发真正想要的操作
        timeout = setTimeout(func,wait)
      }
    }

    // 正常的scroll事件 handler
    function Scroll(){
      console.log('scroll ...');
    }

    // 测试
    // 采用防抖
    // window.addEventListener('scroll',debounce(Scroll,1000))

    // 不采用防抖
    window.addEventListener('scroll',Scroll)
  </script>
</body>

区别:

  • 函数节流是,声明一个变量当标识位,记录当前代码是否在执行,如果正在执行,取消这次方法执行,直接return,如果空闲,正常触发方法执行
  • 函数防抖是需要一个延时器来辅助实现,延迟执行需要执行的代码,如果方法被多次触发,把上次记录的延迟执行的代码用cleartimeout清除掉,重新开始,如果计时完毕,没有方法来访问触发,就执行代码。
  • 节流函数最主要的不同在于他保证在X毫秒内至少执行一次我们希望触发的事件

31.前端中的事件流

HTML和JavaScript交互式通过事件的驱动来实现的,例如鼠标的点击事件,页面的滚动事件等,可以向文档或者文档中的元素添加事件侦听器来预定事件。

什么是事件流

事件流就是描述从页面中接收事件的顺序,DOM 2级事件流主要包括以下几个阶段

  • 事件捕获阶段
  • 处理目标阶段
  • 事件冒泡阶段

addEventListener是DOM 2级事件新增的指定事件处理程序的操作,这个方法接收3个参数

  • 第一个是要处理的事件名
  • 第二个是处理程序的回调函数
  • 第三个是一个布尔值,如果这个布尔值为true,表示在捕获阶段执行回调函数,如果是false,表示在冒泡阶段执行回调函数。

32.mouseover和mouseenter的区别?

mouseover:当鼠标移入元素或者子元素都会触发事件,对应的移除事件是mouseout

mouseenter:当鼠标移入元素本身(不包含子元素)会触发事件,不会冒泡,对应的移除事件是mouseleave

33.如何理解前端模块化

前端模块化就是将复杂的文件变成一个个独立的模块,每个模块含有对应的功能;分成独立的模块有利于代码的重用和日后的维护,这样就引来了模块之间的相互依赖。

34.JS的深度拷贝

在JS中,数据类型分为基本数据类型和引用数据类型两种,对于基本数据类型来讲,它的值直接存储在栈内存中,而对于引用类型来说,他在栈内存中仅仅存储了一个引用,真正的数据在堆中存储。

当我们对基本类型的数据操作时,会发生如下情况:

		var a = 3
		var b = a    
		b = 5    
		console.log(a);//3    
		console.log(b);//5

对于基本数据类型来说,我们将一个基本数据类型赋予a变量,接着将a变量的值赋值给了b,然后我们修改b,可以看到b的值修改了,而a的值没有修改,两个变量使用的都是独立的数据。

当我们堆引用类型的数据操作时,会发生如下情况:

/* 引用数据类型 */    
var obj1 = {      
	a:1,      
	b:2,      
	c:3    
}    
var obj2 = obj1    
obj2.a = 5    
console.log(obj1.a); //5    
console.log(obj2.a);//5

可以看到,两个对象的值全被修改了。

对象是引用类型的值,对于引用类型来说,我们将obj1赋予obj2的时候,我们其实仅仅只是将obj1存储在栈中的引用赋予了obj2,两个对象此时指向的是在堆内存中的同一个数据,所以当我们修改任意一个值的时候,修改的都是堆内存中的数据,而不是引用,所以只要修改了,同样引用的对象的值也发生了改变。

上述的例子就是一个简单的浅拷贝!

对于浅拷贝而言,就是只拷贝对象的引用,而不是深层次的拷贝对象的值,多个对象指向堆内存中的同一个对象,任何一个修改都会使得所有对象的值修改,因为他们公用一条数据。

深拷贝

深拷贝作用在引用类型上,例如:Object,Array

深拷贝不会拷贝引用类型的引用,而是将类型的值全部拷贝一份,形成一个新的引用类型,这样就不会发生引用错乱等问题,使得我们可以多次使用同样的数,而不用担心数据之间会起冲突。

深拷贝的实现

  1. 首先使用JSON.stringify()以及JSON.parse()

    // 深拷贝的实现    
    // 1. 乞丐版    
    var o1 = {
          a:1,      
          b:2,      
          c:3    
    }    
    var o_String = JSON.stringify(o1)    
    var o2 = JSON.parse(o_String)    
    o2.a = 5    
    console.log(o1.a);//1    
    console.log(o2.a);//5
    

    可以看到没有发生引用问题,修改o2的数据,并不会对o1产生影响,为什么说它是乞丐版的?那是因为用JSON.stringify()以及JSON.parse()它是不可以拷贝undefined、function、regExp等等类型

  2. 使用Object.assign(target,source)

    // 2.使用Object.assign(target,source)   
     var o3 = {      a:1,      b:2,      c:3    }   
     var o4 = Object.assign({},o3)    
     o4.b = 5    
     console.log(o3.b);//2    
     console.log(o4.b);//5
    

    使用这种方式本身没有问题,但是这个一层对象,如果是多层对象就会出现问题

    var o5 = {      a:1,      b:2,      c:[1,2,3]    }   
     var o6 = Object.assign({},o5)   
      o6.c[0] = 100    
      console.log(o5.c);// [100, 2, 3]    
      console.log(o6.c);// [100, 2, 3]
    

    可以看到对于一层对象来说是没有任何问题的,但是如果对象的属性对应的是其他引用类型的数据的话,修改还是有问题

  3. 递归拷贝

     // 3.递归拷贝
     function deepClone(target) {
       let result
       // 如果当前拷贝的对象是一个对象的话
       if (typeof target === 'object') {
         // 如果当前对象是一个数组的话
         if (Array.isArray(target)) {
           result = []
           for (const i in target) {
             // 递归克隆数组中的每一项
             result.push(deepClone(target[i]))
           }
           // 如果当前的值为null的话,直接赋值为null
         } else if (target === null) {
           result = null
           // 如果当前的值是一个正则对象的话,直接赋值
         } else if (target.constructor === RegExp) {
           result = target
         } else {
           // 否则为普通对象,直接for in 循环,递归赋值对象的所有值
           result = {}
           for (const i in target) {
             result[i] = deepClone(target[i])
           }
         }
         // 如果不是对象的话,就是基本数据类型,直接赋值
       } else {
         result = target
       }
       // 返回最终结果
       return result
     }
    
     // 测试
     let object1 = {
       a:{
         c:/a/,
         d:undefined,
         b:null
       },
       b:function(){
         console.log(this.a);
       },
       c:[
         {a:'c',b:/b/,c:undefined},
         'a',
         3
       ]
     }
     let object2 = deepClone(object1)
     console.log(object2);
    
  • 7
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值