H5

一、 html

1. 行内元素。块级元素。空元素?

  • 行内元素一行可以放很多个,(设置不了宽高)
    • span、img、input、..
  • 块级元素一行只能放一个
    • h1、h2、div、ul、li、footer、header、p..
  • 空元素
    • br、hr、

2. 元素之间转换?

  • 使用CSS的display属性

    1
    2
    3
    display:inline // 转为行内
    display:inline-block // 行内块
    display:block // 块级元素
  • 先有的link,后有@import
  • link是标签,@import是一个Css文件中的语法,代表着引入另一个css
  • @import只能写在css文件最上面
  • 先加载link,后加载import

4. title与h1、b与strong、i与em的区别

-

  • title是:网站的标题,可以告诉搜索引擎这个网站有什么内容,对Seo而言title比h1更加重要

  • h1是显示在网页内容上的

  • b:单纯的给字加粗,实体标签

  • strong:可以加粗文字,并且强调字符。利于SEO。

  • i:单纯的给字倾斜

  • em:标签内字符重要,用以强调

  • strong比em更加强调

5. img标签的title、alt

  • title:当鼠标移上去一段时间就会出来一段描述(所有标签都有该属性)
  • alt:当图片没有加载成功,就会显示一段文字来代替图片
  • 在Seo层面上,蜘蛛抓不到图片的内容,所以需要加入alt属性来描述这张图是什么内容

6. png、jpg、gif是什么

  • png:无损压缩,体积要比jpg大,适合做小图标
  • jpg:有损压缩,适合做大背景图
  • gif:动图
  • webp:支持有损或无损压缩。用于更小的体积,兼容性不太好

7. 什么是语义化标签

  • 去掉样式后页面呈现清晰的结构

  • 盲人使用读屏器更好地阅读

  • 对SEO更友好

  • 易读性和维护性更高

  • ie8不兼容html5标签。可以通过html5shiv.js 去处理

  • 有哪些新增的语义化标签

    • h5新增的:navheaderfootersectionbutton

8. ios上有300ms延迟,如何解决

  • 可以直接禁用缩放
    • <meta name="viewport" content="width=device-width, user-scalable=no">
  • 可以使用FastClick
    • 原理是检测到touchend事件后立即发出模拟click事件,并把300ms延迟取消掉

9. 如何优化性能?

  • 减少http请求次数

  • 请求的结果用变量保存,没有新数据时用本地数据

  • innerHTML代替dom操作

  • 需要设置很多样式时,应该用className而不是Style

  • 少用全局变量,因为不会被垃圾回收

  • 避免使用css表达式

  • 尽量使用transform来代替js动画

10. 如何使用语义化标签

  • 在设置头部,导航,底部,都有对应的harder,nav,footer。
  • 少使用div和span
  • 不使用样式标签,例如b标签
  • css类名也要语义化等

11. 优化网站

优化网站首先在打包之后要确保包的体积更小,这样首次加载起来就会更快。这就涉及到了优化图片,优化和压缩css和js。优化的话体现在代码的复用和合并,比如公共的css和js可以抽离出来。然后还可以减少http请求。资源的懒加载。

  • 如何对网站的文件和资源进行优化?

    • 文件合并

    • 文件压缩

    • 使用 CDN 托管

  • 减少页面加载时间的方法

    • 优化图片,减少体积
    • 优化css、压缩合并css
    • 减少http请求
    • 资源懒加载

12. 判断环境时Pc还是移动端

  • 判断 navigator.userAgent,对于 Android/iPhone 可以匹配以下正则

    1
    2
    3
    4
    5
    const appleIphone = /iPhone/i;
    const appleIpod = /iPod/i;
    const appleTablet = /iPad/i;
    const androidPhone = /\bAndroid(?:.+)Mobile\b/i; // Match 'Android' AND 'Mobile'
    const androidTablet = /Android/i;
  • 可以用一个库来判断:ismobilejs

    1
    2
    3
    import isMobile from "ismobilejs";

    const mobile = isMobile();

二、CSS

0. 100px的盒子实际显示的就是100个像素吗?

在我们不同的显示器,可能分辨率不同,实际看到的大小也不同。在低分辨率的屏幕上看到的图像会更大。回到这个问题上,一般而言,显示的像素大小是不会有误的。

但微软想到了,可能在一些大屏显示器上看到的图像非常小,不利于用户观察,于是在设置里加入了 屏幕缩放 功能,正常而言是 100% 的缩放,如果你设置为了 125% 那就会导致 100 * 100 像素的盒子实际显示出 125 * 125 像素大小,从而导致误差

1. css盒子模型

  • 标准盒子模型:content-box;大小 = 内容 + 边框 + 内边距 宽度
  • 怪异盒子模型:border-box;大小 = (定义的宽度):(内容 + 边框 + 内边距)
  • 如何转换:
    • box-sizing: content-box / border-box

2. line-height 与height区别

  • line-height:行高,文字每一行的高度,例如br换行或添加换行样式word-wrap: break-word;
  • height:设置行内块或块级元素的高度

3. css选择器

选择器 语法 意义
通配符选择器 * 选择所有元素
标签选择器 标签名 选择所有相关的标签
类选择器 .class 选择所有相关的class
id选择器 #id 选择一个相关的id
分组选择器 element, element 选择被定义的所有元素
后代选择器 element element 选择该元素下的所有相关元素
子代选择器 element > element 选择该元素下的第一层相关元素
相邻选择器 element + element 选择该元素的下一个元素
通用兄弟选择器 element ~ element 选择该元素之后的所有元素
属性选择器 element[attribute=value] 选择包含该属性的所有元素
伪类选择器 element :nth-child(n) 选择该元素的第n个元素

4. css优先级如何计算

类别 权重
* 或 继承 1
.class 10
id 100
行内 1000
!important 无穷

注意:十个class,并不会覆盖一个id

5. css画三角形

1
2
3
4
5
6
7
/* 用边框做 */
div {
width: 0;
height: 0;
border: 100px solid transparent;
border-top: 100px solid #ccc;
}

6. 不给宽高实现水平垂直居中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<div class="container">
<div class="main"></div>
</div>

/* 方式一:position + transifrom */
<style>
.container {
position:relative;
width:500px;
height:500px;
}
.main {
position:absolute;
top:50%;
left:50%;
transifrom:translate(-50%,-50%)
}
</style>

/* 方式二:flex */
<style>
.container {
display:flex;
justify-content:center;
align-items:center;
width:500px;
height:500px;
}
.main {
background:red;
}
</style>

7. display的值

作用
none 不显示
block 转为块级元素
inline-block 转为行内块元素
inline 转为行内元素
table 作为块级表格显示

8. BFC是什么

  • 块级格式化上下文(block formatting context),与之对应的还有IFC
  • BFC是一种规则,这个规则规定了很多东西,就像你开车一样,必须遵循交通道路法的规则一样。所有块级元素都要遵循bfc的规则,例如块级元素一行只能放置一个、当给块级元素赋值宽度200后,默认是靠左显示的、默认是从上往下进行排布的、当上下外边距都有的时候会应用最大值。这些都是bfc的规则在生效。与之对应的还有IFC,他会规定行内元素的布局方式
  • 说简单点就是在块级盒子内的元素会按照一定规则来进行排布,这就是BFC

误区:

  • 是bfc环境里规定着块级元素的排布方式,而不是块级元素里有bfc环境。

  • 而开启bfc环境需要一些特殊的属性才能开启

  • 不同bfc环境的东西不会互相干扰,就像js中不同函数作用域一样

  • 如何触发BFC?

    • 根元素: html
    • 浮动元素:float的值非none
    • 绝对定位:overflow的值非visible
    • display:非行内的属性
    • overflow:非visible
    • position的值为:absolute、flex
  • 如何触发IFC?

    • 在块级元素中使用 display: inline-block; 属性
    • 父元素中使用 display: inline-block; 或者 display: inline-table;
    • 父元素中使用 display: flex; 或者 display: inline-flex;

9. 清除浮动的方式

  • 浮动是如何产生的?

    • 父元素未设置高度
    • 子元素使用了float
  • 使用BFC可清除,但需要满足两个条件

    • 浮动元素的父元素触发BFC
    • 浮动元素父元素的 heightauto
    • 为什么添加BFC后可以有浮动呢?
      • 因为在BFC的规则中,浮动的元素的高度是被计算在内的,因此可以清除浮动。而定位元素的高度会被忽略,所以定位的元素不会计算在内
  • .在子元素最后再加一个标签,给这个标签添加 clear:both

  • 给外部容器添加伪元素,给伪元素添加 clear:both

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <style>
    ul::after,ul::before {
    content:"";
    display:block;
    clear:both;
    }

    li {
    float:left;
    }
    </style>
    <ul>
    <li>1</li>
    <li>2</li>
    </ul>

10. 网页中使用奇数还是偶数的字体?

  • 使用偶数
    • 在布局上:移动端开发要将宽度除以2,偶数就很方便计算
    • 在显示上:在奇数的文字可能文本段落无法对齐

11. position的值有哪些

作用
static 无(默认)
sticky 粘性定位,屏幕高度低于top值时,是absolute的效果,高于top值时,是距离浏览器顶部top距离(fixed效果)
relative 正常位置的定位,生成相对的定位元素(不脱离文档流)
absolute 相对于static定位以外的第一个父元素进行定位(脱离文档流)
fixed 根据浏览器窗口定位(脱离文档流)

12. 双飞翼布局

  • 左中右布局占满屏幕,左右200px,中间自适应,要求先加载中间

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    <style>
    .container div {
    float: left;
    }

    .left {
    margin-left: -100%;
    width: 200px;
    background-color: pink;
    }

    .right {
    margin-left: -200px;
    width: 200px;
    background-color: pink;
    }

    .center {
    width: 100%;
    background-color: skyblue;
    }

    .main {
    margin: 0 200px;
    text-align: center;
    }
    </style>

    <body>
    <div class="container">
    <div class="center">
    <div class="main">

    </div>
    </div>
    <div class="left"></div>
    <div class="right"></div>
    </div>
    </body>

13. reset.css与normalize.css是什么

14. 雪碧图 / 精灵图 / sprite 是什么

  • 将多个小图标,放在一个大图片中
  • 有什么好处?
    • 只需要请求一次,就可以拿到很多图标
  • 缺点?
    • 如果图片被改动了,可能图标会错位(维护较差)
    • 需要对图标进行测量

15. display: none 与 visibility: hidden的区别

  • display: none 不占位置
  • visibility: hidden 占用位置
  • 为什么占用位置和不占用位置?
    • 因为display: none会产生一次回流:会重新计算页面位置,因此隐藏不占位置
    • 而visibility: hidden只会产生重绘:设置隐藏,但不改变位置。(可以理解为透明度0的盒子)

16. opacity 与 rgba(x,x,x,0) 的区别

  • opacity是将整个元素透明度降低,子元素也会受影响,取值范围 0~1
  • rgba(0,0,0,0) 是将某个属性的透明度降低 ,取值范围 0~1

opacity 与 visibility: hidden 的区别

  • opacity设置为0后,进行点击操作是可以响应的,并且可以用transition过渡
  • visibility: hidden 不能进行点击事件,不能用transition过渡

17. 浏览器解析HTML过程

  • 从浏览器接收Url到开启网络请求线程

    点我

  • 收到html数据后开始解析

    • 解析html,创建Dom树;同时解析Css,创建css树
    • 合并DOM树和CSS规则树,生成Render树
    • 布局Render树(layout重绘 / reflow回流),负责各元素的尺寸,位置计算。
    • 绘制render树(paint)
    • 将各层的信息发给GPU。GPU会将各层合成(composite),显示在屏幕上

18. 回流与重绘

  • 回流(重排)是什么

    • 引起Dom树结构变化、元素尺寸、位置、布局需要重新构建就叫回流
    • 浏览器窗口大小发生改变、字体大小变化、增加或移除样式表、内容变化、激活伪类、增加或删除可见DOM元素、计算offsetWidth与offsetHeight、设置style属性。页面首次渲染、元素尺寸或位置改变等…都会产生回流
  • 重绘是什么

    • 单纯的改变样式,例如颜色、背景颜色。不影响布局的就是重绘
  • 产生回流一定会造成重绘,但是重绘不一定会回流

  • 重绘与回流会造成什么影响

    • 会重新计算,影响性能
    • 回流比重绘的代价更高
  • 如何避免回流与重绘

    • 避免使用table布局
    • 尽可能在DOM树的最末端改变class
    • 避免设置多层内联样式
    • 将动画效果应用到position属性为absolute或fixed的元素上
    • 避免使用CSS表达式(例如:calc())
    • 避免频繁操作样式
    • 避免频繁操作DOM
    • 可以先为元素设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘

19. 伪元素的单冒号和双冒号有什么区别

  • 单冒号是伪类

    • :hover / :action
  • 双冒号是伪元素

    • ::before, ::after
  • 虽然也可以直接写:before,但尽量写双冒号,因为这是规范

20. ::before / ::after 有什么作用

  • ::before

    • 在元素之前
  • ::after

    • 在元素之后
  • 作用

    • 一般用于放置共同图标,或清除浮动

21. 如何关闭ios键盘首字母自动大写

  • 给input表单加一个 autocapitalize=’off’ 属性即可

22. 怎么让浏览器支持小于12px的文字

  • 直接设置font-size是不能小于12px的。
  • 可以设置transform: scale(0.5),让元素缩放

24. rem与em的区别

  • rem是依照html根节点的字体大小进行改变的

    1
    2
    3
    4
    5
    6
    html {
    font-size:20px;
    }
    box {
    width:2rem; /* 40px */
    }
  • em是根据自身父元素的字体大小进行改变的

    1
    2
    3
    4
    5
    6
    Father {
    font-size:20px;
    }
    Son {
    width:2em; /* 40px */
    }

25. ios 元素被触摸时产生的半透明遮罩怎么去掉

  • 添加 -webkit-tap-hightlight-color:rgba(0,0,0,0) CSS属性
1
2
3
4
5
6
7
8
9
<style>
button,a,input {
-webkit-tap-hightlight-color:rgba(0,0,0,0)
}
</style>

<button>1</button>
<a>1</a>
<input>1</input>

26. 输入框的placeholder的颜色能变吗

  • 可以通过input::placeholder 选择修改

    1
    2
    3
    input::placeholder {
    color:red;
    }

27. 禁止触发系统菜单和长按选择

  • 禁止触发系统菜单

    • touch-callout: none

      1
      2
      3
      4
      html,body {
      touch-callout: none;
      -webkit-touch-callout: none;
      }
  • 禁止选中

    • user-select: none

      1
      2
      3
      4
      html,body {
      user-select: none;
      -webkit-user-select: none;
      }

28. 自适应布局(适配)

  • 什么是自适应布局:

    • 就是宽度自适应,在不同大小设备下网页以等比例缩放,呈现同样的主体和布局
  • 有哪些方案

    • 可以通过 rem+媒体查询

    • 使用 淘宝的flexible.js

      • 原理:根据页面大小,分成数份,取一个值赋值给html节点的font-size
    • vw / vh单位

29. 响应式布局

  • 什么是响应式布局:

    • 就是根据屏幕大小变化,内容排版会自动改变,呈现最好的用户体验
      • 优点:一个url响应多端
      • 缺点:内容庞大时会卡顿
  • 有哪些方案

    • bootstrap框架

    • 媒体查询

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      /* 
      媒体查询语法
      only: 用于排除不支持媒体查询的浏览器
      screen: 设备类型
      and (): 可以设置max-width / min-width / max-height / min-height
      */
      @media only screen and (max-width: 1000px){
      /* 处于小于等于1000px时做什么 */
      }
      @media only screen and (max-width: 700px){
      /* 处于小于等于700px时做什么 */
      }
    • 对不同设备来显示不同图片

      1
      2
      3
      4
      5
      <picture>
      <source srcset="3.jpg" media='(min-width:1000px)'> /* 小于1000加载这 */
      <source srcset="2.jpg" media='(min-width:700px)'> /* 小于700加载这 */
      <img srcset="1.jpg"> /* 默认情况下加载 */
      </picture>
  • 布局方案

    • 什么情况下使用响应式布局?

      • 数据不是特别多,用户量不是特别大,纯展示类的项目适合响应式
    • pc布局+移动端布局

      • 数据特别多时就需要两套界面了,例如淘宝、京东

30. 垂直塌陷

  • 什么是垂直塌陷?

    • 上下两个div,给上div一个下外边距,给下div一个上外边距,总会应用一个最大值,而不会相加
    • 解决办法:
      • 尽量给只给一个盒子添加margin值,避免就好
      • 给其中一个div外层再套一层div,给外层div启动BFC
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <style>
    .box1 {
    margin-bottom:200px;
    }
    .box2 {
    margin-top:300px;
    }
    </style>

    <div class='box1'></div>
    <div class='box2'></div>

31. 包含塌陷

  • 什么是包含塌陷

    • 父子两个div,给子div一个上外边距,这时父跟着子一起向下移动了
  • 出现原因

    • 因为子元素的margin-top值把父元素顶部的边界顶下去了,导致看起来像是父元素的高度变高了
    • 具体来说,子元素的margin-top值实际上是添加在父元素和子元素之间的外边距上的,而外边距会有折叠的现象,即取两个相邻外边距中的较大值。因此,父元素的高度会自动增加,以包含子元素的margin-top值,导致出现这种情况。
  • 解决办法:

    • 给父元素添加一个边框。边框也会创建一个新的块级格式化上下文,从而避免外边距的折叠
    • 使用padding-top替代margin-top。因为内边距不会参与折叠
    • 给父盒子设置 overflow: hidden,创建一个新的块级格式化上下文(单独计算子元素的边距),从而避免外边距的折叠
    • 给父元素添加一个定位属性。比如可以设置position: relativeposition: absolute。定位元素也会创建一个新的块级格式化上下文,从而避免外边距的折叠
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <style>
    .box1 {
    width: 400px;
    height: 400px;
    background-color: red;
    }
    .box2 {
    width: 200px;
    height: 200px;
    background-color: pink;
    margin: 100px;
    }
    </style>
    <div class="box1">
    <div class="box2"></div>
    </div>

32. css伪类选择器有哪些

选择器 作用
:active 点击
:hover 鼠标悬停
:first-letter 所有文字当中的第一个
:first-child 第一个子元素
:nth-child() 获取子元素
:nth-of-type 获取指定类型的子元素

33. 元素不可见的方式

  • opacity:0
  • visiblety:0
  • position:absolute;top:-10000px;left:-10000px;
  • display:none
  • transform: scale(0)

34. 如何避免样式冲突

  • 可以根据不同的组件,使用对应的名字,名字可以起的细一点。例如header-title,header-img
  • 例如vue可以使用scoped,只在当前组件生效
  • React css module。他会对类名哈希化

35. 画一个 100x100 的方框,其中文字可以正常换行,文字过多超过方框显示滚动条

  • 主要使用 word-wrap: break-word 或 break-all 和 overflow: scroll 实现

36. CSS 如何设置一行超出显示省略号

1
2
3
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;

37. CSS 如何设置多行超出显示省略号

1
2
3
4
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;

38. flex 布局中 order 有何作用

  • order 属性定义 Flex 布局中子元素的排列顺序,数值越小,排列越靠前,默认为 0。

39. flex 布局中 align-content 与 align-items 有何区别

  • align-content 作用于纵轴多行元素,一行元素不起作用
  • align-items 作用于纵轴单行元素

40. CSS 如何实现固定长宽比的元素

可用于flex布局是,使用flex时宽度需要自适应时,可以用该属性长宽比固定

  • aspect-ratio

    1
    2
    3
    div {
    aspect-ratio: 16/9; /*16比9*/
    }

41. 行内元素padding的问题

行内元素无法设置上下内边距(padding)。如果要设置上下内边距,可以将元素的 display 属性设置为 inline-blockblock

这是因为ifc导致的,ifc规定着所有行内元素的格式化上下文。其中就有一条规定行内元素不能设置上下内边距。还有一些其他特性

  • 行内元素默认沿着基线对齐(baseline alignment)。
  • 行内元素的宽度只能受到内容和内边距的影响,无法设置外边距。
  • 行内元素不能设置上下内边距,但可以设置左右内边距(这一点在早期版本的 CSS 中是不被允许的)。

除了这些规定外,IFC 还会影响一些其他的布局特性,例如文本流的方向、文本换行、文本溢出等等。因此,了解 IFC 对于正确理解行内元素的布局和渲染非常重要。

三、JavaScript

一、script

1. script 标签有三种执行机制

执行 作用
<script> 依照书写顺序来执行、加载时DOM会暂停
<script defer>  和DOM一起加载、但延迟到整个页面都解析完毕后再运行
<script async> 和DOM一起加载、但执行时DOM暂停(谁先加载完谁执行)

2. script 缓存机制

  • 浏览器会根据特定的设置缓存所有外部链接的 JavaScript 文件,这意味着如果两个页面都 用到同一个文件,则该文件只需下载一次。这最终意味着页面加载更快

3. noscript

  • 早期浏览器不支持 javascript 需要降级处理,最终出现了 <noscript>,用于给不支持 JavaScript 的浏览器提供替代内容

    1
    2
    3
    4
    5
    <body> 
    <noscript>
    <p>您的浏览器不支持javascript</p>
    </noscript>
    </body>

4. 数据类型有哪些

  • 基本数据类型:NumberStringBooleannullundefinedsymbolbigint

  • 引用数据类型:Object

    1
    2
    3
    4
    5
    6
    // 考题
    alert( true + 1 ) // 2
    alert( 'name' + true ) // nametrue
    alert( undefined + 1 ) // NaN
    alert( typeof null ) // object
    alert( typeof function(){} ) // function

5. null 与 undefined 区别

  • null 是一个表示空的对象,转为数值是0;undefined是一个空的初始值、转为数值是NAN
  • 在设置对象的初始值时需要设置为 null 。在变量定义未赋值情况下默认是 undefined

6. == 与 === 区别

  • ==

    • 会判断两端值是否一样

    • 判断规则:

      • 如果任一操作数是布尔值,则将其转换为数值再比较是否相等。false 转换为 0,true 转换 为 1。
      • 如果一个操作数是字符串,另一个操作数是数值,则尝试将字符串转换为数值,再比较是否相等。
      • 如果一个操作数是对象,另一个操作数不是,则调用对象的 valueOf() 方法取得其原始值,如果valueof得到的还不是原始类型,则调用toString 方法转换后,再根据前面的规则进行比较。
      1
      2
      3
      4
      5
      6
      7
      8
      console.log( 1 == '1' ) // true
      console.log( true == 1 ) // true
      console.log( null == undefined ) // true
      console.log( [1,2] == 1,2 ) // true
      console.log([] == '') // true
      console.log([] == false) // true
      console.log(42 == true) // false, 除了1之外都是false
      typeof document.all // undefined
  • ===

    • 除了比较值,还比较数据类型

      1
      console.log( undefined === null ) // false

7. 宏任务与微任务

因为js是单线程语言,它按照事件循环的机制来执行,它会先把同步任务全部执行完毕,例如if语句,for循环,全部执行完毕后,就会去寻找异步任务的代码,异步任务又分为宏任务和微任务,事件循环会先去执行微任务任务的代码,执行完毕后会去寻找宏任务的代码。执行完一个宏任务后又会去清空所有微任务,就这样循环下去

  • js 为什么是单线程?

    • 因为操作 Dom 不能有两个线程同时操作,不然就不知道听哪个线程为准了
  • 事件循环与事件队列是什么

    • 事件循环

      • 事件循环中包含宏任务、微任务
      • 同步任务 -> 事件循环 [先执行所有的微任务再执行一个宏任务] ->清空微任务 -> 一个宏任务 -> 清空微任务 -> 一个宏任务… 
    • 同步任务全部执行完毕后、才执行事件循环里面的代码

    • 事件队列

      • 即执行事件循环的场所
  • 宏任务与微任务

    • 常见的宏任务
      • 定时器、事件绑定、AJAX/跨域(HTTP请求)
    • 常见的微任务
      • queueMicrotask() 直接往微任务队列插入一条任务
      • Promise相关—— .then(),catch,await、Generator中的 yield等。
      • 注意:Promise本身以及resolve、reject方法并不是异步的。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 考题 ---> 几秒打印几?
    for(var i = 0; i<3; i++){
    setTimeOut(()=>{
    console.log(i)
    },1000*i)
    }

    // 与var相关,var没有块级作用域,i是全局变量
    // 先执行事件队列的所有任务,即for循环全部执行完毕,i变成了3,同时创建了三个定时器,分别是
    // 1000 * 0的、1000 * 1的、和 1000 * 2
    // 因此每隔一秒打印一次 3

    // 如果把var改为let 那么就是每隔一秒分别打印 0 1 2

8. 作用域

什么是作用域?

可以理解为一个独立隔离的空间,例如函数作用域,在函数外部通常访问不到函数内部的数据,而函数内部可以访问到函数外部的数据的

  • 什么是作用域

    • 作用域理解成一个单独独立的空间,在空间内部可以访问外部的变量,而外部不能访问内部的
  • 除了函数外,js没有块级作用域

  • 作用域链:

    • 函数内部可以访问函数外部的变量

    • 函数外部不可访问函数内部的变量

    • 函数内部寻找变量时,会一层一层往外寻找,直到window

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    // 注意点
    function fx(){
    var fn = 1;
    function fn(){}
    console.log( fn ) // 1
    // 普通声明和函数名冲突时,永远会打印变量。如果函数是变量声明的,则依照书写顺序覆盖
    }

    // 考题1
    function fx(b) {
    var b = 0
    console.log(b); // 0,优先使用本地的,当使用let或const时将报错
    }
    fx(100)

    // 考题2
    function fx() {
    var a = b = 10;
    // 相当于var a = 10; window.b = 10;
    // var a = 10, b = 10 这样才是都声明
    }
    fx()
    console.log(b) // 10

    // 考题3
    function fx(){
    var b = 1;
    function fn(){
    console.log( b ); // undefined 因为本作用域下的b变量提升
    var b = 2;
    console.log( b ); // 2
    }
    fn()
    console.log( b ) // 1
    }
    fx()

    // 考题4
    var name = 'a'
    (function(){
    if( typeof name == 'undefined'){
    var name = 'b';
    console.log( '111' + name ); // 111b
    // 在该作用域下,已经有var声明的name变量,在判断之前是undefined,因此会进入第一个语句
    // 如果是let声明的关键字,具有块级作用域,name是String类型,会进入else语句打印222a
    } else {
    console.log( '222' + name );
    }
    })()

    // 考题5
    console.log(a); // undefined
    if (false) {
    var a = 10; // 没有进入判断也会执行变量提升
    }
    console.log(a); // undefined

    // 考题6
    function outFn() {
    this.num = 10;
    function inFn(){
    var num = 100;
    alert(this.num); // 10
    }
    alert(num); // 10
    return inFn;
    }

    outFn()(); // 两次都是在全局调用的,找的是window下的10

9. 对象考题

  • 对象是通过new操作符构造出来的,每一个对象都是一个新的对象,所以对象之间不相等

  • 对象的key都是字符串类型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    [1,2,3] === [1,2,3]  // false

    // 考题1
    var a = { a: 1 }
    var b = { b: 1 }
    a[b] = '123';
    console.log(a)
    // a:1, [object Object]: "123"
    // 以[] 给对象添加对象时,会直接变成字符串 Object

    // 考题2
    var a = { }
    var b = { key: 1 }
    var c = { key: 2 }
    a[b] = '123'
    a[c] = '456'
    console.log( a[b] ) // 456
    // 以[] 给对象添加对象时,会直接变成字符串 Object, 因此后者覆盖前者就是456

10. 原型与原型链

  • 什么是原型

    • 在js中,对象是通过对应的构造函数来创建的,构造函数和对象实例之间的关系就叫原型
  • 什么是原型链

    • 原型链就是链接构造函数和对象实例之间的链条
    • 实例对象本身是没有任何属性和方法的,需要我们自己定义。而构造函数上有一些js提供的方法,我们可以通过原型链查找到构造函数上的方法进行使用,如果方法找不到就会去原型对象中查找,一直找到顶层的null为止。这就是原型链
  • 每一个函数都拥有prototype(显式原型)

  • 每一个对象都拥有__proto__(隐式原型)

  • 原型链是什么?

    • 将实例对象和原型对象的链接链条,可以通过原型链往上级查找
  • 原型链解决了什么问题

    • 对象共享属性和共享方法
  • 原型链的查找规则

    • 先在对象本身找 -> 对象.prototype -> Object -> Object.prototype -> null
  • ![原型链](G:\下载\原型链 (1).png)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    function Fun(){ };
    let f = new Fun();
    console.log( Fun.prototype === f.__proto__ ) // true

    // 考题1, 考点:如果函数不调用,是不会执行函数内部代码的
    function Foo(){
    getName = function(){ alert(1) }
    return this;
    }
    Foo.getName = function(){ alert(2) }
    Foo.prototype.getName = function(){ alert(3) }
    var getName = function(){ alert(4) }
    function getName(){ alert(5) }

    Foo.getName() // 2,因为函数内部的没有使用关键字声明,因此使用的是全局的。
    getName() // 4,执行外部定义的
    Foo().getName() // 1,函数执行后,全局的getName被重新赋值了1,往外找就是1
    getName() // 1,执行刚刚被覆盖了的函数1
    new Foo().getName() // 3,找原型上的3


    // 考题2
    var o = {
    a:10,
    b:{
    fn:function(){
    console.log( this.a ); // undefined
    console.log( this ) // b
    }
    }
    }
    o.b.fn()


    // 考题3
    window.name = 'name'
    function A(){
    this.name = 123;
    }
    A.prototype.getA = function(){
    console.log( this )
    return this.name + 1
    }
    let a = new A()
    let funcA = a.getA // 此处没有执行,因此是把函数赋值过去
    console.log( funcA() ) // 这里是window调用的,因此是name+1 ;name1


    // 考题4
    var length = 10;
    function fn(){
    return this.length + 1;
    }
    var obj = {
    length:5,
    test1(){
    return fn()
    }
    }
    obj.test2 = fn; // 此处obj.test2 的this指向了obj
    console.log( obj.test1() ) // 11
    console.log( fn() === obj.test2() ) // 11 === 6,false
    console.log( obj.test1() == obj.test2() ) // 11 === 6,false

11. 判断数组的方式

  • isArray:Array.isArray(arr)

    1
    2
    Array.isArray([]) // true
    Array.isArray({}) // false
  • instanceof:arr instanceof Array

    • instanceof也会将对象判断为数组
    1
    2
    3
    {} instanceof Array // false
    {} instanceof Object // true
    [] instanceof Object // true
  • prototype:Object.prototype.toString.call( arr ).indexOf(‘Array’) != -1

    1
    2
    Object.prototype.toString.call([]).indexOf('Array') != -1 
    // true,数组打印是[Object Array]
  • isPrototypeOf:Array.prototype.isPrototypeOf( arr )

    1
    Array.prototype.isPrototypeOf( [] ) // true
  • constructor:arr.constructor.toString().indexOf(‘Array’) != -1

    1
    [].constructor.toString().indexOf('Array') != -1 // true

12. slice是干嘛的,splice是否改变原数组

  • slice(截取,包括第一项,不包括最后一项),返回的是新数组,不影响原数组

    1
    2
    3
    4
    var arr = [1, 2, 3, 4, 5]
    console.log(arr.slice(1, 2)) // [2]
    console.log(arr.slice(-2)) // [4, 5]
    console.log(arr.slice(2)) // [3, 4, 5]
  • splice(添加或删除数组中指定的元素),返回的是被删除的元素,会影响原数组

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var arr = [1, 2, 3, 4, 5]
    arr.splice(2, 2) // n,n 删除元素
    console.log(arr); // [1, 2, 5]

    var arr = [1, 2, 3, 4, 5]
    arr.splice(1, 0, '500') // n,0,xxx 添加元素
    console.log(arr); // [1, '500', 2, 3, 4, 5]

    var arr = [1, 2, 3, 4, 5]
    arr.splice(1, 1, '500') // n,1,xxx 修改元素
    console.log(arr); // [1, '500', 3, 4, 5]

13. 找出多维数组的最大值

1
2
3
4
5
6
7
8
// 1
let arr = [[1, 2, 3, 4], [11, 22, 33, 44], [111, 222, 333, 444]]
arr.forEach(item => {
console.log(Math.max(...item)); // 4、44、444
})

// 2
Math.max(...[1,2,3,[2,34,6]].flat()) // 34

14. 输入一串字、输出时拼接指定内容

1
2
3
4
String.prototype.addS = function () {
return '---' + this + '---'; // 谁调用了谁就是this,因此this是123
}
console.log('123'.addS()); // ---123---

15. 找出字符串出现最多的字符以及字数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 统计
let str = '11aaaaaaaaaaaaaaadddaabbbcccdddcdddddd'
let obj = {}
for (let i = 0; i < str.length; i++) {
let At = str[i]
if (obj[At]) {
obj[At]++;
} else {
obj[At] = 1
}
}
console.log(obj);
// 最大值
let max = 0;
for (let k in obj) {
if (obj[k] > max) {
max = obj[k]
}
}
// 最多的字符
for (let k in obj) {
if (obj[k] == max) {
console.log("最多的字符是" + k);
console.log("出现的次数是" + max);
}
}

16. new操作符做了什么

  • 创建了一个空对象
  • 将空对象的 隐式原型__proto__ 指向构造函数的 显式原型prototype
  • 将空对象的this指向构造函数
  • 对构造函数返回值进行判断(如果返回的是对象就返回对象,不进行new操作,基本数据继续new)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 模拟
function Create(fn, ...args) {
// 1. 创建空对象
var obj = {}
// 2. 将空对象的隐式原型 指向构造函数的显式原型
Object.setPrototypeOf(obj, fn.prototype)
// 3. 将实例的this指向构造函数
var result = fn.apply(obj, args)
// 4. 对返回值进行判断处理
return result instanceof Object ? result : obj
}

function Fun(age, name) {
this.age = age;
this.name = name;
}
console.log(Create(Fun, 18, '张三'));

17. 闭包

  • 闭包是什么

    • 闭包是一个函数返回了另一个函数,并且返回的这个函数使用了外部函数的变量

      1
      2
      3
      4
      5
      6
      7
      function fx(){
      var a = 10;
      return function fn(){ // fn 就是一个闭包函数
      console.log(a)
      }
      }
      fx()() // 10
  • 闭包解决了什么问题(闭包的作用)

    • 外部可以访问内部的变量
    • 让这些变量不被销毁,始终保存在内存中
  • 闭包的使用场景

    • 一个Ajax请求的成功回调
    • 给多个绑定事件回调的方法
    • 一个setTimeout的延迟回调
    1
    2
    3
    4
    5
    6
    7
    8
    9
    var lis = document.querySelectorAll('li')
    for (var i = 0; i < lis.length; i++) {
    (function (i) {
    // 函数内部使用了外部的 i,并且是回调函数
    lis[i].onclick = function () {
    alert(i)
    }
    })(i)
    }
  • 闭包的缺点

    • 变量会驻留在内存中,造成内存损耗
      • 解决:把闭包函数设置为 null
    • 内存泄漏(只存在于ie老版本)

18. 原型继承方式

  • ES6继承方式:extends xxx

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class P {
    constructor (){
    this.age = 18
    }
    }
    class S extends P { // 使用 extends P
    constructor (){
    super()
    this.name = '砂糖'
    }
    }
    let o1 = new S()
  • 原型继承:s.prototype = new S()

    1
    2
    3
    4
    5
    6
    7
    8
    function P(){
    this.age = 20
    }
    function S(){
    this.name = '砂糖'
    }
    s.prototype = new S() // 将原型执行另一个实例,就可以访问另一个原型链的方法了
    let o1 = new S()
  • call:P.call(this)

    1
    2
    3
    4
    5
    6
    7
    8
    function P(){
    this.age = 20
    }
    function S(){
    this.name = '砂糖'
    P.call(this) // 改变P的this指向S
    }
    let o1 = new S()
  • 组合式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function P(){
    this.age = 20
    }
    function S(){
    P.call(this) // 2.
    this.name = '砂糖'
    }
    s.prototype = new S() // 1.
    let o1 = new S()

19. call、apply、bind 的区别

  • 都可以改变this指向,区别在于使用场景不同

  • .call()

    • 会立即执行
    • 多个参数需要挨个写
    • 日常情况下,需要直接修改this,参数也是一个个时使用
  • .apply()

    • 会立即执行

    • 第二个参数是数组

    • 使用场景:数据是数组的时候

      1
      2
      3
      4
      // 例如需要借用数据方法时
      let arr = [1,2,3,4,5,999]
      Math.max( arr ) // NaN, max方法需要一个个值
      Math.max.apply( null, arr ) // 999
  • .bind()

    • 不会立即执行,返回的是一个函数,需要再次调用

    • 多个参数需要挨个写

    • 使用场景:使用回调函数时

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      // 使用回调函数时
      let btn = document.querySelector('botton')
      let div = document.querySelector('div')

      btn.onclick = function(){
      console.log( this ) // btn
      }

      btn.onclick = function(){
      console.log( this ) // div
      }.bind(div)

20. sort 原理

  • 默认根据Unicode字符码点进行排序,可以传递一个函数

  • 原理:使用了InsertionSort(插入排序) 与 QuickSort(快速排序)排序

  • 数量小于10的数组用InsertionSort、大于10的数组使用QuickSort

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    let arr = [1,2,3,222,4,5,111]
    let arr1 = arr.sort(function(a,b){
    return a - b; // a-b升序,b-a降序
    })
    console.log(arr1) // [1,2,3,4,5,111,222]


    let arr = [{age:1},{age:20},{age:5}]
    let arr1 = arr.sort(function(a,b){
    return a.age - b.age;
    })
    console.log(arr1) // [{age:1},{age:5},{age:20}]

21. 深拷贝与浅拷贝

  • 浅拷贝是将地址值赋值过去

    1
    2
    3
    let obj1 = {a:1}
    let obj2 = obj1 // 这就是浅拷贝
    Object.assign() // 也是浅拷贝
  • 深拷贝是将每个值都复制过去

    • 使用JSON.parse(JSON.stringify())有一些问题
      • 拷贝obj中的时间对象 - 时间对象会变成字符串
      • 拷贝obj中的正则对象 / Error对象 - 序列化后会变成空对象
      • 拷贝obj中的函数 / undefined - 序列化后会丢失
      • 拷贝obj中的NaN、Infinity和-Infinity - 序列化的结果会变成null
      • 如果对象中存在循环引用的情况也无法正确实现深拷贝
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    // 1.
    let obj1 = {a:1}
    let obj2 = JSON.parse(JSON.stringify( obj1 ))
    console.log(obj1 === obj2) // false

    // 2.
    // 传递两个对象,一个新对象,一个旧对象
    function deepCopy(newObj, oldObj) {
    for (var k in oldObj) {
    // 判断值是否是数组
    if (oldObj[k] instanceof Array) {
    newObj[k] = [];
    deepCopy(newObj[k], oldObj[k])

    // 判断是否是函数, 因为函数输入对象,因此需要在对象之前进行判断,否则会被放入对象中
    } else if (oldObj[k] instanceof Function) {
    newObj[k] = oldObj[k];

    // 判断值是否是对象
    } else if (oldObj[k] instanceof Object) {
    newObj[k] = {};
    deepCopy(newObj[k], oldObj[k])

    // 属于简单数据类型
    } else {
    newObj[k] = oldObj[k];
    }
    }
  • 深拷贝函数:structuredClone() https://developer.mozilla.org/zh-CN/docs/Web/API/structuredClone

    • 该函数可以克隆绝大部分数据、如reg正则对象、date对象
    • 但不能复制构造函数与类,如Object、Class

22. localStorage、sessionStorage、cookie的区别

分类 有效期
localStorage 存入的数据只能手动销毁,5M左右
sessionStorage 存入的数据关闭浏览器就销毁,5M左右
cookie 可以设置一个过期时间,在过期时间之前都不会销毁,4kb左右
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 本地存储
localStorage.set(key,value) // 存储
localStorage.get(key) // 查询
localStorage.removeItem(key) // 删除
localStorage.clear() // 清空

// cookie,需要在线上环境中使用
document.cookie // 获取cookie
document.cookie = 'name=123;age=456' // 设置cookie

var date = new Date()
var time = 5000;
time = date.getTime() + time
date.setTime(time)
document.cookie = 'name=123;age=123;Expires=' + date.toUTCString() + '' // 设置过期时间
// 只需要将过期时间设置为过去,即可删除cookie

23. 把多维数组变为一维数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//1. 递归
let arr = [1, 2, 3, [4, 5, [6, 7]], [8, 9, [0]]]
function flatten(arr) {
var result = [];
for (var i = 0, len = arr.length; i < len; i++) {
if (Array.isArray(arr[i])) {
result = result.concat(flatten(arr[i]))
}
else {
result.push(arr[i])
}
}
return result;
}
console.log(flatten(arr));

// 2. toString
let arr = [1, 2, 3, [4, 5, [6, 7]], [8, 9, [0]]]
function flatten(arr) {
return arr.toString().split(',').map(function (item) {
return +item
})
}
console.log(flatten(arr));

// 3. 扩展运算符
let arr = [1, 2, 3, [4, 5, [6, 7]], [8, 9, [0]]]
function flatten(arr) {
while (arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
}
console.log(flatten(arr));

// 4. flat(数组重塑),里面可以传值,代表平铺几维数组
let arr = [1, 2, 3, [4, 5, [6, 7]], [8, 9, [0]]]
console.log(arr.flat(n)) // [1,2,3,4,5,6,7,8,9,0]

24. 写出冒泡排序

  • 冒泡排序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function bubbleSort(data) {
    var temp = 0;
    for (var i = data.length; i > 0; i--) {
    for (var j = 0; j < i - 1; j++) {
    if (data[j] > data[j + 1]) {
    temp = data[j];
    data[j] = data[j + 1];
    data[j + 1] = temp;
    }
    }
    }
    return data;
    }

25. 什么是BOM,有哪些常用属性

  • location

    属性 作用 举例
    location.href 返回或设置当前文档的URL 例如 http://www.baidu.com?xxx=xx
    location.search 返回URL中的查询字符串部分 例如 ?id=5&name=dreamdu
    location.hash 返回URL#后面的内容 例如 /#/xxx=xxx
    location.host 返回URL中的主域名部分 例如 http://www.baidu.com
    location.hostname 返回URL中的主域名部分 例如 dreamdu.com
    location.pathname 返回URL的域名后的部分 例如 /abc/
    location.port 返回URL中的端口部分 例如 8080
    location.protocol 返回URL中的协议部分 例如 http
    location.assign 设置当前文档的URL
    location.replace() 设置当前文档的URL,并移除历史记录
    location.reload() 重载当前页面
  • history

    属性 作用
    history.go() 前进或后退指定的页面数
    history.back() 后退一页
    history.forward() 前进一页
  • Navigator

    属性 作用
    navigator.userAgent 返回用户浏览器版本信息等
    navigator.cookieEnabled 返回浏览器是否支持(启用)cookie

26. 如何使用biglnt类型

  • 只需要在数值后面加一个n即可,例如:123456n

27. 如何判断数组包含某个值

  • indexOf

    1
    console.log([1,2,3,4,5].indexOf(1)) // 找到了就返回索引,没有找到就返回-1
  • include

    1
    console.log([1,2,3].include(2)) // 找到返回true

28. 0.1 + 0.2 != 0.3是为什么

  • 因为计算机是二进制的,逢二进一
  • 在表示1/2,4/1,8/1,16/1是没有问题的
  • 但在表示0.1时,是一个无限循环小数,在计算的时候,计算机会对其截取一定位小数,因此丢失了精度

29. promise API

  • promise.all
  • promise.any
  • promise.rece
  • promise.resolve

30. 类数组,如何转为数组

  • 一个简单的定义,如果一个对象有 length 属性值,则它就是类数组

  • 那常见的类数组有哪些呢?

    • document.getElementsByTagNamedocument.querySelectorAll 等等。除了 DOM API 中,常见的 function 中的 arguments 也是类数组
  • 类数组转为数组

    1
    2
    3
    4
    Array(...all)
    Array.from(arrayLike);
    Array.apply(null, arrayLike);
    Array.prototype.concat.apply([], arrayLike);

31. 事件冒泡与事件捕获

  • 事件冒泡
    • 从目标元素向window一层一层获取,例如给目标元素和window都设置了click事件,点击了目标元素后会触发目标元素,同时也会触发window
  • 事件捕获
    • 从window一层一层向目标元素获取,先执行window,同时触发目标元素
  • 默认是事件冒泡,当addEventListener第三个参数为true时,就是事件捕获

32. input中监听值的变化的事件

  • keypress:按下和抬起都会触发

  • keyup:键盘抬起

  • keydown:键盘按下

  • input:值发生变化时

  • change:失去焦点

33. load 事件与 DomContentLoaded 事件的先后顺序

  • 当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像和子框架的完全加载.
  • 当整个页面及所有依赖资源如样式表和图片都已完成加载时,将触发load事件

34. DOM 中如何阻止事件默认行为,如何判断事件否可阻止?

  • e.preventDefault(): 取消事件
  • e.cancelable: 事件是否可取消

35. 事件委托(委派)是什么

  • 如果有很多标签都要监听,将事件监听器绑定在父元素进行监听,此时数百个事件监听器变为了一个监听器,提升了网页性能。

    1
    2
    3
    4
    5
    var ul = document.querySelector('ul')
    ul.addEventListener('click', function (e) {/*
    alert('成功') */
    e.target.style.backgroundColor = 'pink'
    })

36. 获取浏览器所有元素

  • document.querySelectorAll('*')
  • document.getElementsByTagName('*')
  • $$('*'),可在浏览器控制台使用
  • document.all,已废弃,不建议使用

37. 监听 localStorage 的变动

  • window.onstorage = function(){}

38. 浏览器中监听事件函数 addEventListener 第三个参数有那些值

  • capture。监听器会在时间捕获阶段传播到 event.target 时触发。
  • passive。监听器不会调用 preventDefault()。
  • once。监听器只会执行一次,执行后移除。
  • singal。调用 abort()移除监听器。

39. 防抖与节流

  • 防抖

    • 触发高频事件后 n 秒内函数只会执行一次,如果 n 秒内高频事件再次被触发,则重新计算时间

      • 登录、发短信等按钮避免用户点击太快,以致于发送了多次请求,需要防抖
      • 调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖
      • 文本编辑器实时保存,当无任何更改操作一秒后进行保存
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      // 防抖函数
      function debounce(fn, wait) {
      let timer;
      return function () {
      let _this = this; // 保存当前调用者的this
      let args = arguments;
      if (timer) {
      clearTimeout(timer);
      }
      timer = setTimeout(function () {
      fn.apply(_this, args);
      }, wait);
      };
      }
      // 使用
      window.onresize = debounce(function () {
      console.log("resize");
      }, 500);
  • 节流

    • 高频事件触发,但在 n 秒内只会执行一次,所以节流会稀释函数的执行频率

      • scroll 事件,每隔一秒计算一次位置信息等
      • 浏览器播放事件,每个一秒计算一次进度信息等
      • input 框实时搜索并发送请求展示下拉列表,每隔一秒发送一次请求 (也可做防抖)
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      // 方式1: 使用时间戳
      function throttle1(fn, wait) {
      let time = 0;
      return function () {
      let _this = this;
      let args = arguments;
      let now = Date.now();
      if (now - time > wait) {
      fn.apply(_this, args);
      time = now;
      }
      };
      }
      // 方式2: 使用定时器
      function thorttle2(fn, wait) {
      let timer;
      return function () {
      let _this = this;
      let args = arguments;

      if (!timer) {
      timer = setTimeout(function () {
      timer = null;
      fn.apply(_this, args);
      }, wait);
      }
      };
      }


40. 数组去重

  • Map

    1
    2
    3
    4
    function unique(arr) {
    if (!Array.isArray(arr)) throw new TypeError();
    return [...new Set(arr)];
    }
  • filter

    1
    2
    3
    4
    5
    6
    7
    8
    var arr = [1, 1, 8, 8, 12, 12, 15, 15, 16, 16];
    function unlink(arr) {
    return arr.filter(function (item, index, arr) {
    // 检测该元素是否存在
    return arr.indexOf(item, 0) === index;
    });
    }
    console.log(unlink(arr));

41. 进程与线程

  • 进程是程序的一次执行过程,是一个动态概念,是操作系统资源分配的基本单位
  • 线程是任务调度和执行的基本单位,它可与同属一个进程的其他的线程共享进程所拥有的全部资源
    • 一个程序至少有一个进程,一个进程至少有一个线程
    • 线程的划分尺度小于进程,使得多线程程序的并发性高
    • 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率
    • 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制
    • 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别

42. IIFE 立即执行函数

  • 是什么

    • 声明一个函数,并马上调用这个匿名函数就叫做立即执行函数;也可以说立即执行函数是一种语法,让你的函数在定义以后立即执行
  • 作用

    • 不必为函数命名,避免了污染全局变量
    • 立即执行函数内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量
    • 封装变量
  • 使用场景

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var list = document.getElementById("list");
    var li = list.children;
    for(var i = 0 ;i<li.length;i++){
    (function(j){
    li[j].onclick = function(){
    alert(j);
    })(i); //把实参i赋值给形参j
    }
    }

43. 函数柯里化

  • 什么是函数柯里化

    • 说简单点就是把多参数函数,转化为单参数函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 柯里化之前
    function add (x, y) {
    return x + y
    }
    add(1, 2) // 3

    // 柯里化之后
    function getAdd (y) {
    return function (x) {
    return x + y
    }
    }
    getAdd(2)(1) // 3
  • 函数柯里化应用

    • 参数复用(固定易变因素)
    • 延迟执行
    • 提前返回
    • 利用柯里化制定约束条件,管控触发机制
    • 处理浏览器兼容(参数复用实现一次性判断)
    • 函数节流防抖(延迟执行)
    • ES5前bind方法的实现

44. 垃圾回收机制

什么是垃圾
  • 没有被引用的对象或变量
  • 无法访问到的对象(几个对象引用形成一个环,互相引用)
可达性
  • 可达性是指那些以某种方式可以访问到或可以用到的值,它们被保证存储在内存中。
  • 有一组基本的固有可达值,由于显而易见而无法删除:
    • 本地函数的局部变量和参数
    • 嵌套调用链上的其他函数的变量与参数
    • 全局变量
垃圾回收机制
  • JS的垃圾回收机制是为了以防内存泄漏,内存泄漏的含义就是当已经不需要某块内存时这块内存还存在着,没有被释放,导致该内存无法被使用,垃圾回收机制就是间歇的不定期的寻找到不再使用的变量,并释放掉它们所指向的内存
垃圾回收方式
  • 标记清除(mark and sweep)

    • 有变量进入执行环境的时候,就标记这个值进入环境,离开的时候,标记为离开环境
    • 离开环境之后还存在的变量则是需要被删除的变量
  • 引用计数(reference counting)

    • 机制就是跟踪一个值的引用次数,当声明一个变量并将一个引用类型赋值给该变量时该值引用次数加1,当这个变量指向其他一个时该值的引用次数便减一

    • 当该值引用次数为0时,则说明没有办法再访问这个值了,被视为准备回收的对象,每当过一段时间开始垃圾回收的时候,就把被引用数为0的变量回收

    • 用计数方法可能导致循环引用,类似死锁,导致内存泄露,例如

      1
      2
      3
      4
      5
      6
      7
      function problem() {
      var objA = new Object();
      var objB = new Object();

      objA.someOtherObject = objB;
      objB.anotherObject = objA;
      }

45. setImmediate

  • 执行时机:

    • 会在同步任务执行完毕之后、微任务执行之前执行,也就是说会比 queueMicrotask 执行得更快
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /* 
    setImmediate
    - 参数
    - 必选:回调函数,用于执行
    - 可选:...[] 用于传递参数给回调函数
    - 在当前的任务队列执行完成后,在 I/O 事件的回调之前执行
    */
    setImmediate((a, b,c) => {
    console.log(a, b, c) // 'name' 123 undefined
    }, 'name', 123, undefined)

46. 标签函数

和普通函数类似,但是调用方式是以 `` 来进行调用的

1
2
3
4
5
6
7
const arr = ['f', 'o', 'r', '(', ';', ';', ')', '{', '}']
function tagFn(...arg) {
// return Function(arr.join(''))
console.log(arg)
return arg.join('')
}
console.log(tagFn`12${arr}34${arr[0]}`);

47. promise终极面试题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Promise.resolve().then(() => {
console.log(0);
return Promise.resolve(4);
}).then((res) => {
console.log(res)
})

Promise.resolve().then(() => {
console.log(1)
}).then(() => {
console.log(2);
}).then(() => {
console.log(3);
}).then(() => {
console.log(5);
}).then(() =>{
console.log(6);
})
问题一:执行顺序能详细说说吗
  • 第一个promise的resolve()执行后,立即调用了.then(),因此调用栈里会出现第一个微任务[p0],这个微任务里输出了0。然后这个then()返回了一个新的promise.resolve()

  • 接着第一个promise最后又调用了一次.then(),此时微任务队列有两个任务[p0, pres]

  • 然后就是第二个promise,它也调用了.then()方法,此时微任务队列有两个任务[p0, pres, p1]

  • 第一个promise和第二个promise的Promise.resolve()会立即执行这是没疑问的

  • 然后第一个promise.then 会被执行,输出第一个promise的0和第二个promise的 1

    • 但第一个promise中 return返回的是一个新的立即完成的promise,这个时候promiseA+规范规定:如果返回的是一个新promise,那么需要再次调用该新promise.then方法,去实现外面的promise,在浏览器中这步操作仍然是微任务的。这步操作完成不会打印任何东西,只会往微任务里添加一个.then()
    • 此时任务队列里只有一个 promise.then()
  • 接着执行第二个promise中的() => console.log(2)

    • 此时任务队列里只有一个 promise.then()() => console.log(2)

核心:当return的是一个新promise时,会自动调用这个新promise的then()方法,去实现外面的promise。这一步任是微任务的,意味着会在任务队列中新增一个任务。因此当 0和1执行完成后,实际上会执行程序员写的then() 并放入微队列中。接着执行2,然后完成前面的then(),但这个then返回的是一个新的promise,内部会进行then()一次,目的是为了实现Promise.resolve()的微任务。于是又被放入了微任务队列后。然后执行3,这时候才真正执行了4,最后输出5和6

问题二:将 return Promise.resolve(4) 换为 return 4 结果会发生变化吗?为什么
  • 会,如果换为return 4,将会作为结果直接返回给下一个then(),因此结果是0142356

48. JS引起内存泄漏的情况

内存泄露是什么
内存泄漏(Memory leak)是在计算机科学中,由于疏忽或错误造成程序未能释放已经不再使用的内存。导致一致占用计算机内存

  • 意外的全局变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 情况1.
    function foo(arg) {
    bar = "this is a hidden global variable";
    }

    // 情况2.
    function foo() {
    this.variable = "potential accidental global";
    }
    // foo 调用自己,this 指向了全局对象(window)
    foo();
  • 定时器造成内存泄露

    1
    2
    3
    4
    5
    6
    7
    8
    var someResource = getData();
    setInterval(function() {
    var node = document.getElementById('Node');
    if(node) {
    // 处理 node 和 someResource
    node.innerHTML = JSON.stringify(someResource));
    }
    }, 1000);
  • 闭包,维持函数内局部变量,使其得不到释放

    1
    2
    3
    4
    5
    6
    7
    function bindEvent() {
    var obj = document.createElement('XXX');
    var unused = function () {
    console.log(obj, '闭包内引用obj obj不会被释放');
    };
    obj = null; // 解决方法
    }
  • 没有清理对DOM元素的引用同样造成内存泄露

    1
    2
    3
    4
    5
    6
    let refA = document.getElementById('refA');
    document.body.removeChild(refA); // dom删除了
    console.log(refA, 'refA'); // 但是还存在引用能console出整个div 没有被回收

    refA = null;
    console.log(refA, 'refA'); // 解除引用

49. 如何执行字符串代码?

有几种方法

  • eval 函数

    1
    const data = eval('for(;;)')
  • Function构造函数

    1
    const data = Function('for(;;)')()
  • Array构造函数

    1
    const data = Array(() => 'for(;;)')[0]()
  • Object构造函数

    1
    const data = Object(() => 'for(;;)')()

二、ES6

1. var、let、const 的区别

  • var 具有变量提升机制(变量悬挂声明)。而let、const没有

  • var可以重复声明同一个变量,后者覆盖前者。而let、const不能

  • var、let 声明的变量可以被修改,而const不行

  • var没有自身作用域。let、const有块级作用域

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 考题1
    function demo(){
    var a = 1;
    if(true){
    var a = 2
    }
    console.log(a) // 2、因为var没有块级作用域、如果换成let则是1
    }

    // 考题二
    const obj = {a:1}
    obj.a = 2
    consolt.log(a) // 2、const在基本数据类型是不可变的,在引用类型不修改内存地址情况下是可以修改obj内部的变量

2. 合并对象

  • 通过 Object.assign 方法(有共同属性时,后者会覆盖前者)

    1
    2
    3
    4
    const a = {a:1,b:4}
    const b = {b:2,c:3}
    let obj1 = Object.assign(a,b)
    console.log( obj1 ) // { a:1, b:2, c:3 }
  • 通过ES6 扩展运算符

    1
    2
    let obj2 = {...a, ...b}
    console.log( obj2 ) // { a:1, b:2, c:3 }
  • 自己 封装函数

    1
    2
    3
    4
    5
    6
    7
    8
    function extend(target, source) {
    for (const k in source) {
    target[k] = source[k] // 把每一项赋值给第一个对象
    }
    return target
    }
    console.log( extend({ a: 1, b: 4 }, { b: 2, c:3 }) );
    // { a:1, b:2, c:3 }

4. 箭头函数与普通函数的区别

  • this 指向问题
    • 普通函数:指向调用者,可修改
    • 箭头函数:指向外层第一个普通函数的this,而且不可修改
  • 箭头函数不能 new(不能当做构造函数)
  • 箭头函数没有 prototype(原型)
  • 箭头函数没有 arguments,如果需要arguments可以在箭头函数外部包一层普通函数
  • 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

5. promise问题

  • 什么是promise

    • promise是ES6中新增的异步编程解决方案, 在代码中的表现是一个对象

    • 开发中为了保存异步代码的执行顺序, 那么就会出现回调函数层层嵌套
      如果回调函数嵌套的层数太多, 就会导致代码的阅读性, 可维护性大大降低
      promise对象可以将异步操作以同步流程来表示, 避免了回调函数层层嵌套问题,避免了回调地狱问题

  • 三种状态,一旦决定就不可改变

    • pending(进行中)

    • reslove(已成功)

    • rejected(已失败)

  • promise 解决了什么问题?

    • 解决了回调地狱的问题,比如在请求里需要等待一个请求里面的参数,再通过这个参数发送请求,就会出现回调套娃的问题。通过promise就可以使用then方法来实现一步一步类似同步的写法,方便维护

      1
      2
      3
      4
      5
      6
      7
      8
      9
      Promise.resolve('foo').then(res => {
      console.log(res);
      }).then(() => {
      console.log(1);
      }).then(() => {
      console.log(2);
      }).then(() => {
      console.log(3);
      }) // foo /1 /2 /3
    • 但一个promise里写了太多内容的话也会不好维护,于是出现了generator

6. generator

  • 用于优化promise写法

  • 它会等待上一个结果返回后再下一个

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // 定义函数,在前边加*号就是generator函数. 使用yield关键字来定义事件
    function* fx() {
    yield 请求1;
    yield 请求2;
    },

    // 使用函数
    let res = fx()
    console.log(res.next().value.then(res=>{console.log(res)})) // 结果1
    console.log(res.next().value.then(res=>{console.log(res)})) // 结果2

    // 也可以和async/await搭配使用
    let a = this.fx();
    console.log(await a.next().value); // 结果1
    console.log(await a.next().value); // 结果2

7. async / await

  • 该关键字会将等到的结果直接拿到。

  • 被async定义的函数会默认返回一个Promise对象resolve的值,因此对async函数可以直接then

  • await 放置在Promise调用之前,await 强制后面的代码等待,直到Promise对象resolve,得到resolve的值作为await表达式的运算结果

  • await 修饰的如果是Promise对象:可以获取Promise中返回的内容(resolve或reject的参数),且取到值后 语句才会往下执行;如果不是Promise对象:把这个非promise的东西当做await表达式的结果。

    1
    2
    3
    4
    async function del() {
    let { data } = await '请求'
    // data中就是返回的数据了
    },

8. find 与 filter的区别

  • filter(过滤)

    • 返回的是数组,不会改变原数组。他返回所有符合条件的值

      1
      2
      3
      4
      5
      let arr = [1,2,3,4,5,6]
      let newArr = arr.filter(item=>{
      return item > 2
      })
      console.log(newArr) // [3,4,5,6]
  • find(寻找)

    • 返回查找符合条件的第一个结果,不会改变原数组。

      1
      2
      3
      4
      5
      let arr = [1,2,3,4,5,6]
      let newArr = arr.find(item=>{
      return item > 2
      })
      console.log(newArr) // 3

9. some 与 every 的区别

  • some

    • 只需要数组中一个值符合条件 就返回true

      1
      2
      3
      4
      5
      let arr = [1,2,3,4,5,6]
      let newArr = arr.some(item=>{
      return item > 2
      })
      console.log(newArr) // true
  • every

    • 需要数组中所有值都符合,才为true

      1
      2
      3
      4
      5
      let arr = [3, 4, 5, 6]
      let newArr = arr.every(item => {
      return item > 2
      })
      console.log(newArr) // true

10. forEach的性能是否比for差

  • 一般来说for会更快

11. forEach中可以用async / await吗

  • 可以,给回调函数加上async,内部的代码是可以使用await的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    arr.forEach(async (item) => {
    console.log(1);
    const data = await new Promise((res, rej) => {
    setTimeout(() => {
    res(2)
    }, 1000);
    })
    console.log(data);
    await queueMicrotask(() => {
    console.log(3);
    })
    })

12. ES6中新增了什么?

  • let、const
  • 箭头函数
  • promise
  • 解构赋值
  • async / await
  • proxy
  • class
  • 新增的API

14. 包装对象

  • 什么是包装对象
    • 指的是与数值、字符串、布尔值分别相对应的Number、String、Boolean三个原生对象。
    • 这三个原生对象可以把原始类型的值变成(包装成)对象。

15. 把对象转为key,value的二维数组

  • 使用:Object.entries(obj)

    1
    Object.entries({0:1,1:2}) // [[0,1],[1,2]] 
  • 也可以遍历循环push

    1
    2
    3
    4
    5
    6
    7
    8
    9
    let o = {
    a: 1,
    b: 2
    }
    let arr = []
    for (const k in o) {
    arr.push([k, o[k]])
    }
    console.log(arr);

16. 解构赋值

  • ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构

  • 如果有默认值的情况下,值是undefind默认值才有效,其余的都会覆盖默认值

  • undefined和null不可解构,会报错

  • 数组的结构赋值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    let [a, b, c] = [1, 2, 3];
    // a=1,b=2,c=3

    let [ , , third] = ["foo", "bar", "baz"];
    third // "baz"

    let [x, y, ...z] = ['a'];
    // x: "a" ,y: undefined ,z: []

    // 默认值
    let [x = 1] = [undefined];
    x // 1

    let [x = 1] = [null];
    x // null
  • 对象的解构赋值

    • 对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者
    • 对象的解构赋值可以取到继承的属性
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    let { foo: baz } = { foo: 'aaa', bar: 'bbb' }
    // baz: "aaa",foo:'报错'
    上面代码中,foo是匹配的模式,baz才是变量。真正被赋值的是变量baz,而不是模式foo

    const obj1 = {};
    const obj2 = { foo: 'bar' };
    Object.setPrototypeOf(obj1, obj2);
    const { foo } = obj1;
    foo // "bar"

    var {x = 3} = {x: undefined};
    x // 3

    var {x = 3} = {x: null};
    x // null

17. Set数据结构

  • 它类似于数组,但是成员的值都是唯一的,没有重复的值。

    • Set本身是一个构造函数,用来生成 Set 数据结构。

    • Set函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。

      1
      2
      3
      4
      5
      6
      7
      8
      const s = new Set();

      [2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));

      for (let i of s) {
      console.log(i);
      }
      // 2 3 5 4
  • Set 实例的属性和方法

    • Set.prototype.constructor:构造函数,默认就是Set函数。
    • Set.prototype.size:返回Set实例的成员总数。
  • Set 操作

    • Set.prototype.add(value):添加某个值,返回 Set 结构本身。
    • Set.prototype.delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
    • Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。
    • Set.prototype.clear():清除所有成员,没有返回值。
  • 遍历Set

  • Set的遍历顺序就是插入顺序

  • 由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致

    • Set.prototype.keys():返回键名的遍历器
    • Set.prototype.values():返回键值的遍历器
    • Set.prototype.entries():返回键值对的遍历器
    • Set.prototype.forEach():使用回调函数遍历每个成员
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    let set = new Set(['red', 'green', 'blue']);

    for (let item of set.keys()) {
    console.log(item);
    }
    // red
    // green
    // blue

    for (let item of set.values()) {
    console.log(item);
    }
    // red
    // green
    // blue

    for (let item of set.entries()) {
    console.log(item);
    }
    // ["red", "red"]
    // ["green", "green"]
    // ["blue", "blue"]

18. WeakSet

  • WeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别。

    首先,WeakSet 的成员只能是对象,而不能是其他类型的值。

  • 其次,WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。

  • 语法

    • WeakSet 是一个构造函数,可以使用new命令,创建 WeakSet 数据结构。

      1
      2
      3
      4
      5
      const ws = new WeakSet();

      const a = [[1, 2], [3, 4]];
      const ws = new WeakSet(a);
      // WeakSet {[1, 2], [3, 4]}
    • 三个方法:

      • WeakSet.prototype.add(value):向 WeakSet 实例添加一个新成员。
      • WeakSet.prototype.delete(value):清除 WeakSet 实例的指定成员。
      • WeakSet.prototype.has(value):返回一个布尔值,表示某个值是否在 WeakSet 实例之中。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      const ws = new WeakSet();
      const obj = {};
      const foo = {};

      ws.add(window);
      ws.add(obj);

      ws.has(window); // true
      ws.has(foo); // false

      ws.delete(window);
      ws.has(window); // false
  • WeakSet 没有size属性,没有办法遍历它的成员。

  • WeakSet 不能遍历,是因为成员都是弱引用,随时可能消失,遍历机制无法保证成员的存在,很可能刚刚遍历结束,成员就取不到了。

    • WeakSet 的一个用处,是储存 DOM 节点,而不用担心这些节点从文档移除时,会引发内存泄漏

19. Map数据结构

  • JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。

  • 为了解决这个问题,ES6提供了Map结构,他类似对象,也是键值对的集合。但键不仅限字符串,各种类型的值(对象),也可以当做键

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    const m = new Map();
    const o = {p: 'Hello World'};

    m.set(o, 'content')
    m.get(o) // "content"

    m.has(o) // true
    m.delete(o) // true
    m.has(o) // false

    // --------------------------

    const map = new Map([
    ['name', '张三'],
    ['title', 'Author']
    ]);

    map.size // 2
    map.has('name') // true
    map.get('name') // "张三"
    map.has('title') // true
    map.get('title') // "Author"
  • 如果对同一个键多次赋值,后面的值将覆盖前面的值

    1
    2
    3
    4
    5
    6
    7
    const map = new Map();

    map
    .set(1, 'aaa')
    .set(1, 'bbb');

    map.get(1) // "bbb"
  • 如果读取一个未知的键,则返回undefined

  • 注意,只有对同一个对象的引用,Map 结构才将其视为同一个键。这一点要非常小心。

    1
    2
    3
    4
    const map = new Map();

    map.set(['a'], 555);
    map.get(['a']) // undefined,不是同一个数组 内存地址不一样
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    let map = new Map();

    map.set(-0, 123);
    map.get(+0) // 123

    map.set(true, 1);
    map.set('true', 2);
    map.get(true) // 1

    map.set(undefined, 3);
    map.set(null, 4);
    map.get(undefined) // 3

    map.set(NaN, 123);
    map.get(NaN) // 123
  • Map具有size属性,代表成员总数

  • 操作

    • Map.prototype.set(key, value)

      • set方法设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键
    • Map.prototype.get(key)

      • get方法读取key对应的键值,如果找不到key,返回undefined
    • Map.prototype.has(key)

      • has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。
    • Map.prototype.delete(key)

      • delete方法删除某个键,返回true。如果删除失败,返回false
    • Map.prototype.clear()

      • clear方法清除所有成员,没有返回值。
  • Map 结构原生提供三个遍历器生成函数和一个遍历方法。

    • Map.prototype.keys():返回键名的遍历器。
    • Map.prototype.values():返回键值的遍历器。
    • Map.prototype.entries():返回所有成员的遍历器。
    • Map.prototype.forEach():遍历 Map 的所有成员。

    需要特别注意的是,Map 的遍历顺序就是插入顺序。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    const map = new Map([
    ['F', 'no'],
    ['T', 'yes'],
    ]);

    for (let key of map.keys()) {
    console.log(key);
    }
    // "F"
    // "T"

    for (let value of map.values()) {
    console.log(value);
    }
    // "no"
    // "yes"

    for (let item of map.entries()) {
    console.log(item[0], item[1]);
    }
    // "F" "no"
    // "T" "yes"

    // 或者
    for (let [key, value] of map.entries()) {
    console.log(key, value);
    }
    // "F" "no"
    // "T" "yes"

    // 等同于使用map.entries()
    for (let [key, value] of map) {
    console.log(key, value);
    }
    // "F" "no"
    // "T" "yes"

20. weakMap

  • WeakMap结构与Map结构类似,也是用于生成键值对的集合。
  • WeakMapMap的区别有两点。
    • WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。
    • WeakMap的键名所指向的对象,不计入垃圾回收机制
  • WeakMap的设计目的在于,有时我们想在某个对象上面存放一些数据,但是这会形成对于这个对象的引用。就不会被垃圾回收
    • WeakMap 就是为了解决这个问题而诞生的,它的键名所引用的对象都是弱引用,即垃圾回收机制不将该引用考虑在内。因此,只要所引用的对象的其他引用都被清除,垃圾回收机制就会释放该对象所占用的内存。也就是说,一旦不再需要,WeakMap 里面的键名对象和所对应的键值对会自动消失,不用手动删除引用
  • weakMap语法
    • WeakMap 与 Map 在 API 上的区别主要是两个,一是没有遍历操作(即没有keys()values()entries()方法),也没有size属性。
    • WeakMap只有四个方法可用:get()set()has()delete()

21. Proxy

  • Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写,意为代理拦截

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var proxy = new Proxy(target, handler);
    // target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。

    let obj = { a: 1, b: 2 };
    var proxy = new Proxy(obj, {
    get: function (target, propKey) {
    return 35;
    },
    });
    proxy.a // 35
  • get:访问时执行

  • set:修改时执行

22. 创建一百个元素为0的数组

  • Array.fill()

    1
    Array(100).fill(0)
  • Array.from()

    1
    Array.from({length: 100}, () => 0);

23. forof 与 forin的区别

  • for…in
    • 循环对象的话出来的是key,数组的话 是数组的下标
    • 还会遍历原型上的值
  • for…of
    • for…of不能循环普通的对象,需要通过和OBject.Keys()搭配使用 大多数用来遍历数组

24. ?? 运算符

  • ?? 类似于 ||

    • 区别:
      • ?? 在判断0时,会返回0
      • ?? 在判断false时,会返回false
    1
    2
    3
    4
    5
    false || [] // []
    0 || [] // []

    false ?? [] // false
    0 ?? [] // 0

24. 创建一个从0-100的数组

1
2
3
let x = 0; 
Array.from({length: 100}, ()=> ++x)
// [0, 1, 2, 3, ..., 100]
1
2
Array.from(Array(100).keys(), n => n + 1);
// [0, 1, 2, 3, ..., 100]

三、HTTP

1. ajax是什么

  • ajax(Asynchronous JavaScript and XML:异步的javascript和xml)

    • 一种不用刷新整个页面便可与服务器通信的办法(通过ajax与服务器进行数据交换,使网页实现局部更新)

2. http响应的结构是怎么样的

  • 状态码:

    • 描述了响应的状态。可以用来检查是否成功的完成了请求。请求失败的情况下,状态码可用来找出失败的原因
  • HTTP头部

    • 包含了更多关于响应的信息。比如:头部可以指定认为响应过期的过期日期,或者是指定用来给用户安全的传输实体内容的编码格式
  • 主体Body

    • 它包含了响应的内容。它可以包含HTML代码,图片,等等。主体是由传输在HTTP消息中紧跟在头部后面的数据字节组成的。

3. http与https区别

  • https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
  • http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议
  • http和https使用的是完全不同的连接方式,用的端口也不一样,http是80,https是443。
  • http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全

4. get和post的区别

  • 一般get主要是从服务器中获取数据,post主要是向服务器发送数据

  • get会把请求内容放在url中,而post放在 Message Body 中传送更安全

    • 一般请求可以用get,而一些敏感信息,例如密码,需要用post
  • get因为请求参数放置在url上,因此长度会比post小。

    • 不同浏览器所支持的长度不一致,比如ie是2083比特,谷歌最大是8182比特字符
  • get执行效率要比post高,因为get是直接发送数据。而post会先发送请求头和服务器验证,再发送真正数据

  • GET在浏览器回退时是无害的,而POST会再次提交请求。

5. http的优点

  • http使用的是可靠的数据传输协议
    • 底层是tcp协议,确保顺序内容的正确,确保没有丢包,因此能够保证传输过程不会损坏
  • 简单快速
    • 客户向服务器发送请求时,只需要输入请求方法与路径,常用的有
      • Get、head、put、delete、post
  • 灵活
    • http允许传输任意类型的数据对象
  • http协议的无状态性
    • 是一种不保证状态,即无状态协议。http协议自身不对请求和响应之间的通讯状态进行保存,不对发送过的请求和响应做持久化处理
  • cookie

    • http是无状态的,是怎么分辨用户的呢?

    • 每次http请求时,客户端都会在请求头中发送相应的cookie信息到服务端,只需要记录下cookie就知道你是谁了

    • 如果客户端禁用cookie怎么办

      • 一般会在url后面附加一个参数来识别用户
  • session

    • 是另一种记录客户端状态的机制
    • 不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上。
    • 客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是Session。客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了,session是一种特殊的cookie。cookie是保存在客户端的,而session是保存在服务端的
    • session原理
      • 当客户端第一次请求服务器的时候,服务器生成一份session保存在服务端,将该数据(session)的id以cookie的形式传递给客户端;以后的每次请求,浏览器都会自动的携带cookie来访问服务器(session数据id)。
  • 为什么要用session

    • 由于cookie 是存在用户端,而且它本身存储的尺寸大小也有限,最关键是用户可以是可见的,并可以随意的修改,很不安全
  • 区别

    • cookie是保存在客户端的
    • cookie有大小限制
    • cookie可以设置过期时间
    • session是保存在服务器端
    • session会比较占用服务器性能,当访问增多时应用cookie
    • session的过期时间依赖于服务器
    • session更加安全

7. 常见状态码

  • 200
    • 200:请求成功
    • 201:请求成功并且创建了资源
  • 300
    • 301:临时重定向
    • 302:永久重定向
    • 304:没有命中强缓存并走协商缓存时就把这些值跟资源文件的信息进行比对,如果资源没更改,返回304,浏览器读取本地缓存。 如果资源有更改,返回200,返回最新的资源。
  • 400
    • 401:请求要求身份验证
    • 403:服务器拒绝请求
    • 404:没有找到资源
  • 500
    • 服务器内部错误
1
2
3
4
5
6
7
8
// 请求码可以由后端指定,一般遵循以下规范
/*
- 1xx 请求处理中
- 2xx 请求成功
- 3xx 请求的重定向
- 4xx 客户端错误
- 5xx 服务器错误
*/

8. 报文(Message)

  • 报文由一行行简单的字符串组成
  • 客户端发给服务器的叫请求报文(request)
  • 服务器返回给客户端的叫响应报文
  • 而HTTP协议就是对这个报文的格式进行规定
  • 报文的组成
请求报文

打开浏览器开发者工具,发送请求后查看 请求标头,整个请求标头 就是一个请求报文

组成部分 作用
请求首行 第一行就是起始行,包含请求类型,url,协议版本。以及响应状态
请求头 以key:val保存了类型
空行(body) 作为分割,将整体分为上下两部分(告知请求头结束)
请求体 只有post请求才有请求体,通过请求体携带数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 请求首行, 包含了请求方式, 请求地址, 协议版本
GET /s?name=zhangsan HTTP/1.1

// 以下都是请求头
/*
Accept: 允许浏览器接受的文件类型
Accept-Encoding: 浏览器允许的压缩编码
Accept-Language: 浏览器允许的语言
User-Agent: 用户代理, 他是一段用来描述浏览器信息的字符串
*/
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cache-Control: no-cache
Connection: keep-alive
Cookie: ""
Host: www.baidu.com
Pragma: no-cache
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36
sec-ch-ua: "Not?A_Brand";v="8", "Chromium";v="108", "Google Chrome";v="108"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
响应报文

格式与请求报文类似

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 响应首行、 协议版本, 响应状态码, 响应的描述
HTTP/1.1 200 OK

// 响应头
/*
- Date 响应时间
- Content-Type 描述响应体的类型
- Content-Length 描述响应体的大小
*/
Date: Sat, 10 Dec 2022 11:35:19 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 1072
Transfer-Encoding: chunked
Connection: keep-alive
Server: nginx
Vary: Accept-Encoding
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-UA-Compatible: chrome=1
Expires: Sun, 1 Jan 2000 01:00:00 GMT
Pragma: must-revalidate, no-cache, private
Cache-Control: no-cache
Set-Cookie: oschina_new_user=false; path=/; expires=Wed, 10 Dec 2042 11:35:18 -0000
Set-Cookie: gitee-session-n=OGIyT0NodGl0SHowdHlmemdGMHpmbGVyVFFMZ3MzRWNYRzdoMHpFY25wVVI2MUtWd3I2M3BnaWRQM1JIZHJjNFlxOWMxN2Zrb0cxd2NPRGtVa282NkJ5M2IrQWI3KzBpY3BGMTBWL1Z2UlZ5czBiZUtMbTZ5OWJaVlJpakJTZWt5TU9XdGFHK0RVT0hZeHlaOExUUXRjc0EraXlEZy8xT0pTYkF0SW9hOTd0MlRMalBvZWVLaW9Hc2E1cVN2RVhyY285ZEp6OUlmQWJ4NHNuOUw5YUhnOFgzeHJYSTU1NUZldURaQkFyR0J4UExMVml5bS9UbitEUUhmSDRCYjduaTUra2dEeE9yaFBuTHBsV1paN1RrSjNIZ0QzRUpYWmpodGdjQXVVdjh0Z2tlelpodDFLdHNpQzNJdS94L2xJd0luU2UyRVFEcC9nWGpGSmtqZDQ2TlBRMmhTQ0tyQ3Y3cE44M3BDMmVJTm5ER3RWdTZuQkxXM0dCdFZ5WWRsaGxYbEVHM2licE0wVlV0TWZ6dlZQZzhCZDZFRHhCOVI4WmdNRy9aaEk3eUFKOWV2b29rY0gvcmVLTGpzNTJGN3RyV09mcHRZL25xdmlEM05BN1Yvc2VpV0RTd2xsU1RhZWxmajVVQ0J0dmJ2cE9YSTNPUjhKbnByc2tGMC9uazl6Rlk5VnJMVU5YQktzTTc0RWpteDhmejlaelhmUCtKSXJSa3M4aUtrZklFaTJxZmU2NjNpQllkMkI5Nk13SkFacHllTms0NFVLUGJDMEJHWDVmcTFlR3IvZ2RDZUVnamc2TU1MbGhpNU80dFA4Zz0tLVhGT21NMkVkaEJsSS9sU0F1Wnp2a1E9PQ%3D%3D--474a140d89de63b99e2d38cee50252b30c9c59d1; domain=.gitee.com; path=/; HttpOnly
X-Request-Id: d6203f25d6be563483f41e6866e7c038
X-Runtime: 0.573366
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self' https://*.gitee.com
Content-Encoding: gzip

9. 请求头常见的内容

请求类型 作用
Accept 浏览器可接受的MIME类型
Accept-Charset 浏览器可接受的字符集
Accept-Encoding 浏览器可接受的数据解码方式
Connection 是否需要持久链接
Content-Length 正文长度
Cookie
Host 主机端口
User-Agent 浏览器类型
响应类型 作用
Allow 服务器支持哪些请求方法
Content-Encoding 文档的编码
Content-Length 内容长度
Content-Type MIME类型
Date 当前GMT时间
Expires 设置文档过期时间,从而不再缓存
Lest-Modified 文档最后改动时间
Refresh 表示多久后刷新文档,单位:秒

10. TCP模型概念是怎么样的?

OSI七层网络模型 TCP/Ip四层模型概念 对应网络协议
应用层(AppLication) 应用层 HTTP、TFTP、FTP、NFS、WAIS、SMTP
表示层(Presentation) TelInet、Rlogin、SNMP、Gopher
会话层(Session) SMTP、DNS
传输层(Transport) 传输层 TCP、UDP
网络层(NetWork) 网络层 IP、ICMP、ARP、RARP、AKP、UUCP
数据链路层(DataLink) 数据链路层 FDDI、Ethernet、Arpanet、PDN、SLIP、PPP
物理层(Physical) IEEE 802.1A、IEEE 802.2到IEEE 802.11

11.一个完整的http请求会经历哪些步骤?

  • 输入url后,将地址发给dns,域名解析(DNS服务),找到服务器ip与端口号
  • 浏览器先查看浏览器缓存-系统缓存-路由器缓存,如果缓存中有,会直接在屏幕中显示页面内容。没有继续走下面
  • 发起TCP三次握手,建立TCP链接
  • 发起Http请求,发送Header、body等信息
  • 服务端响应http,将资源封装成响应包返回,关闭链接。浏览器得到html代码
  • 浏览器解析html代码,遇到图片,js,css等会再次发送请求,拿到数据,最终显示内容
  • 浏览器渲染页面呈现给用户

back

11-1 三次握手是什么?

  • 第一次:客户端发送消息给服务器,服务器接收到了

    • 客户端给服务器端发送一个报文(SYN:申请建立链接)
    • 这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的
  • 第二次:服务器返回一个响应给客户端,说ok。我同意你的链接

    • 服务器收到报文之后,会应答一个报文给客户端(SYN:申请和客户端链接,ACK:同意链接)
    • 这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常
  • 第三次:客户端再发送请求,正文

    • 客户端收到报文后再给服务器发送一个报文(ACK:同意链接)
    • 这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。

因此,需要三次握手才能确认双方的接收与发送能力是否正常

11-2 四次挥手是什么?

https://blog.csdn.net/weixin_44865458/article/details/117234974

断开链接需要经历四次挥手,发起者可能是客户端,也有可能是服务端。以下以客户端发起为例

  • 第一次:客服端发送一个消息给服务器,示意结束

    • 会发送一个FIN报文,指定一个序列号
  • 第二次:服务端返回一个消息给客户端,示意知道了。但不会立即断开,因为数据可能没有接收完毕

    • 会返回一个ACK报文,把客户端的序列号值 +1 作为 ACK 报文的序列号值
  • 第三次:当服务端接受完数据后,会再返回一个信息。示意数据接收完毕,可以断开连接

    • 会再返回一个FIN报文,告诉客户端
  • 第四次:客户端返回一个消息告诉服务器,示意我知道了

    • 客户端收到 FIN 之后,发送一个 ACK 报文作为应答,断开链接

11-3 TCP/IP 协议族

  • TCP/IP 协议族中包含了一组协议

    • 这组协议中规定了互联网中所有的通讯细节
  • 网络通讯的过程由四层组成

    • 应用层:软件所在的层,如浏览器,服务器
    • 传输层:将一整个大数据拆分为一个个小的包。在此阶段还没有进行传输(TCP协议处于该位置)
    • 网络层:网络层负责给每一个包添加信息(如地址等信息)(IP协议处于该位置)
    • 数据链路层(物理层):负责传输数据

客户端与服务器是通过数据链路层链接的

发数据是从上往下执行的,收数据是从下往上执行的(解包的信息、合并包)

12. 什么是套接字(socket)

  • 用于描述ip地址和端口,是一种通讯机制
  • 套接字包含一个节点地址和一个端口号,用来标识这一服务
  • 像websocket
    • WebSocket(网络套接字)是一种用于在单个TCP连接上进行全双工通信的协议。它使用HTTP协议来建立连接,然后使用独立的通信协议传输数据。WebSocket通信协议为客户端和服务器之间的数据传输提供了一种双向通道,使得客户端无需轮询服务器,即可接收实时数据更新。

13. 代理是什么

  • 代理位于客户端和服务器之间,接收所有客户端的HTTP请求,并将这些请求转发给服务器
  • 代理的方式
    • 正向代理:走一个中介,代理请求者的网络内部,访问外部网络
    • 反向代理:请求到服务器,转发到内部其他服务器

14. 浏览器缓存

https://blog.csdn.net/qq_46658751/article/details/123433335

  • 为什么要缓存
    • 缓存可以减少网络 IO 消耗,提高访问速度。浏览器缓存是一种操作简单、效果显著的前端性能优化手段
  • 缓存的机制
    • Memory Cache 内存中的缓存
      • 主要包含的是当前页面中已经抓取到的资源,例如页面上已经下载的样式、脚本、图片等
    • Service Worker Cache 服务缓存
    • Disk Cache 硬盘中的缓存
    • Push Cache 推送缓存
      • 当以上三种缓存都没有命中时,它才会被使用。
  • 缓存它又分为强缓存和协商缓存。优先级较高的是强缓存,在命中强缓存失败的情况下,才会走协商缓存
    • 在 Response Headers 中将过期时间写入 expires 字段,现在一般使用Cache-Control 两者同时出现使用Cache-Control
    • 协商缓存,Last-Modified 是一个时间戳,如果我们启用了协商缓存,它会在首次请求时随着 Response Headers 返回:每次请求去判断这个时间戳是否发生变化。从而去决定你是304读取缓存还是给你返回最新的数据
强缓存
  • 强制缓存就是向浏览器缓存查找请求结果,并根据该结果的缓存规则来决定是否使用该缓存结果的过程。

  • 不会向服务器发送请求,直接从缓存中读取资源,在chrome控制台的Network选项中可以看到该请求返回200的状态码,并且Size显示from disk cache或from memory cache。

  • 强制缓存的情况主要有三种

    • 不存在该缓存结果和缓存标识,强制缓存失效
    • 存在该缓存结果和缓存标识,但是结果已经失效,强制缓存失效,则使用协商缓存
    • 存在该缓存结果和缓存标识,且该结果没有还没有失效,强制缓存生效,直接返回该结果
  • 当浏览器向服务器发送请求的时候,服务器会将缓存规则放入HTTP响应报文的HTTP头中和请求结果一起返回给浏览器,控制强制缓存的字段分别是ExpiresCache-Control,其中Cache-Conctrol的优先级比Expires高

协商缓存
  • 协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,主要有以下两种情况
    • 协商缓存生效,返回304和Not Modified
    • 协商缓存失效,返回200和请求结果

15. 跨域是什么,如何解决跨域

  • 协议域名端口,三者有一不一样,就是跨域
    • 案例:www.baidu.comzhidao.baidu.com 是跨域
  • 如何解决跨域
    • CORS:在服务器设置响应头,允许跨域。'Access-Control-Allow-Origin: '*'
    • 反向代理:使用nginx配置一个反向代理的地址,前端将请求发给这个地址即可
    • JSONP:对于资源,用script标签请求,因为改标签不受同源政策限制
      • jsonp原理
      • 动态创建 script,使用 script.src 加载请求跨过跨域
    • document.domain
      • 该方式只能用于二级域名相同的情况下,比如 a.test.com 和 b.test.com 适用于该方式。
      • 只需要给页面添加 document.domain = ‘test.com’ 表示二级域名都相同就可以实现跨域

基础类问题

项目开发流程

项目调研

首先是公司高层对项目进行分析,讨论为这个项目投入的时间,资金和资源

需求分析

  • 需求文档
  • 原型图设计
  • UI设计

需求审评

然后由项目经理会对这个项目准备一个设计稿或者原型图,召集大家来对这个产品需求进行分析,没问题确认下来后就进入开发阶段

Q:在需求分析的时候,你作为一个前端 你应该去做些什么?

我们应该去考虑页面上某些功能的细节,比如设计稿上有一个input框,作为一个前端我需要考虑的就是input框的一些功能,例如最大能输入多少个字?有没有表单的验证功能?

开发阶段

分析完毕后,内容一旦确认下来,就会对原型图上的功能进行分解,一般情况下会把功能拆成一个个的任务

前端开发页面,后端设计数据库,设计接口。测试去准备一些测试用例。我们各干各的。

但前端对接口是有一些要求的,有时候就要去和后端沟通。

开发完毕后就需要进入测试阶段

Q:你觉得你的开发效率怎么样?

我觉得开发效率还不错,主要能够快速理解需求,和同事沟通,共同完成任务

测试

开发完毕后,就会会有一个提交送测的操作,把项目打包成 dist

我们的项目肯定是多人协同开发,会用git来提交代码,然后通过一些方式告诉项目经理和测试人员。我们是发送邮件来通知的。然后测试就会对我们的项目的功能进行验证。我们是有一个bug的列表网页,如果出现了问题,测试就会把出现的问题和相关人员列出来让我们去修改

而我们首先需要确认这个bug是哪里导致的,可能是后端的问题,还是需求的问题。如果是后端的问题,就要找测试,把bug传给后端进行解决

最后产品通过了测试,没问题后就会进行上线。

上线

  • 上线申请
  • 提交上线分支
  • 最终上线到生产环境

迭代总结

Q:你们的上线流程(上线迭代周期)是怎样的

我们在完成一些页面时,可能就要提交到主干,然后会进行代码审查和测试。这就是一个迭代,我们一般会每2周发布一个版本,发布没问题就会进行回顾和评估,确定下一个版本的工作重点

HTML 基础相关

h5的新特性

html5 备注
只有一种 DOCTYPE ⽂件类型声明(统 一标准)
增加了一些新的标签元素(功能, 语义化) section, video, progress, nav, meter, time, aside, canvas, command, datalist, details, embed, figcaption, figure, footer, header, hgroup…
input 支持了几个新的类型值 date, email, url 等等
新增了一些标签属性 charset(⽤于 meta 标签);async(⽤于 script 标 签)
新增的全域属性 contenteditable, draggable… hidden…
新增API 本地存储, 地理定位, Canvas绘图, 拖拽API, 即时通信 WebSocket

css3新增的特性

说出下方常用的即可, 无需全部背出

1、伪元素

2、弹性布局flex

3、媒体查询

4、圆角

5、渐变

6、阴影

7、平面转换

8、3D转换

9、动画

盒子/图片水平垂直居中

  • 要使一个盒子水平和垂直居中,最简单的方法是使用 CSS 的 flex 布局。

​ 首先,让父元素使用 display: flex 并设置 align-items: center 和 justify-content: center,这样父元素就会在水平和垂直方向上居中。然后,将子元素设置为盒子即可。

下面是一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.parent {
display: flex;
align-items: center;
justify-content: center;
}

.child {
width: 100px;
height: 100px;
}

<div class="parent">
<div class="child">I am centered</div>
</div>

如果你希望盒子在垂直方向上居中,但在水平方向上靠左或靠右,则可以使用 justify-content: flex-start 或 justify-content: flex-end。

如果你想使用其他布局方式来实现居中,也可以使用 position: absolute 和 transform: translate(-50%, -50%)。

1
2
3
4
5
6
7
8
9
10
.parent {
position: relative;
}

.child {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}

还有其他一些方法可以实现居中,但上述方法是最常用的。

css盒模型

CSS盒模型是用于描述HTML元素的布局的一种方法。它用于在浏览器中渲染HTML元素的边框和内容。

每个HTML元素都是一个盒子,其中包含元素的内容,内边距(padding),边框和外边距(margin)。

在CSS中,你可以使用多种属性来控制盒模型的外观和布局,包括:

  • width和height属性用于控制元素的内容区域的大小。

  • padding属性用于控制元素的内边距。

  • border属性用于控制元素的边框。

  • margin属性用于控制元素的外边距。

你还可以使用box-sizing属性来控制元素的盒模型类型。默认情况下,HTML元素使用的是“标准盒模型”,在这种情况下,元素的宽度和高度仅指内容区域的宽度和高度。但是,你也可以将box-sizing属性设置为“边距盒模型”,在这种情况下,元素的宽度和高度将包括内边距和边框。

块级元素和行内元素

在HTML中,元素可以分为两种类型:块级元素和行内元素。

块级元素

  • 块级元素在文档流中占据一整行。

  • 块级元素可以设置宽度和高度。

  • 块级元素可以设置内外边距和边框。

常见的块级元素包括:div,h1,p,form,header,footer,section等。

行内元素

  • 行内元素在文档流中只占据所需的空间。

  • 行内元素不能设置宽度和高度。

  • 行内元素只能设置内边距。

常见的行内元素包括:a,span,button,input,label,select等。

注意:元素的类型并不是固定的,你可以使用CSS的display属性来改变元素的类型。例如,你可以将一个行内元素转换为块级元素,或者将一个块级元素转换为行内元素。

css选择器权重值

在CSS中,选择器的权重值(也称为优先级)是用于确定哪些样式规则应该应用到HTML元素的一种机制。

选择器的权重值由两部分组成:选择器的特殊性和样式定义的位置。

选择器的特殊性是根据选择器中使用的各种类型的选择器来计算的。每种类型的选择器都有一个固定的权重值,如下表所示:

选择器类型 权重值
通配符(*) 0
元素选择器(例如p) 1
类选择器(例如.class) 10
ID选择器(例如#id) 100
属性选择器(例如[type]) 10
伪类选择器(例如:hover) 10
伪元素选择器(例如::before) 10

为了计算选择器的特殊性,需要对选择器中使用的每种类型的选择器的权重值进行加权。

  • 例如,如果选择器为#id .class p,则其特殊性为 (100 * 1) + (10 * 1) + (1 * 1) = 111

样式定义的位置也会影响选择器的权重值。在所有其他因素相同的情况下,样式定义出现的位置越靠后,其权重值就越大。这意味着如果有多个样式规则适用于同一个HTML元素,那么最后出现的样式规则会覆盖先前出现的样式规则。

为了确定哪些样式规则应用到HTML元素,浏览器会比较所有适用于该元素的样式规则的权重值,并应用权重值最大的样式规则。

你可以使用 !important 来强制应用样式规则,即使其权重值低于其他样式规则。例如,如果你想要强制应用某个样式规则,你可以在样式值后面添加 !important,例如:

这将强制将所有段落的文本颜色设置为红色,即使有其他样式规则为段落设置了不同的颜色。注意,使用!important会使样式规则的权重值变得非常高,因此应该谨慎使用。

H5事件

  • onblur失焦事件
  • onfocus聚焦事件
  • onchange改变事件
  • onclick点击事件
  • onerror错误事件
  • oninput输入事件
  • onkeydown键盘按下事件
  • onkeyup键盘抬起事件
  • onmousemove鼠标移动事件
  • onmouseover鼠标进入事件
  • onmouseout鼠标移出事件等

H5中input元素的type属性值

  • color颜色

  • password密码

  • text文本

  • CheckBox复选框

  • radio单选框

  • date日期

  • button按钮

  • submit提交按钮等

事件对象和事件委托

  • 事件对象

    • 一个函数或者方法都会带有一个事件对象参数

    • 事件对象.target是获取最先触发的元素

    • 事件对象有两种公共的方法:

      • .preventDefault() 阻止默认行为

      • .stopPropagation()阻止冒泡

  • 事件委托:

    • 可以把事件处理器添加到一个上级元素上,避免把事件处理器添加到多个子元素上,提高性能

    • 动态添加的元素仍然可以触发该事件

    • 主要依靠的就是事件冒泡,也就是当一个元素接收到事件的时候,会把他接收到的事件传给自己的父级,一直到window

  • 优点:

    • 无须监听每个元素的事件,减少事件处理函数。
    • 元素增减,不会影响监听事件。
    • JavaScript和DOM节点之间的关联减少,避免因循环引用引起的内存泄漏。
  • 场景:

    • 存在大量相同事件,子节点频繁添加或移除。
    • 🔔可以举例React中的合成事件。

sessionStorage和localStorage的区别

sessionStorage localStorage 是两种存储数据的方式,都可以用来保存在浏览器中的数据。但是,它们之间有一些区别

  • 存储范围不同:sessionStorage 只在浏览器的当前会话中有效,当会话结束(浏览器关闭)时,数据就会被清除。而 localStorage 在浏览器中是持久的,除非用户手动清除数据或者浏览器清除缓存,否则数据不会丢失。

  • 存储容量不同:一般来说,sessionStorage localStorage 的存储容量都是有限的。但是,localStorage 的容量通常比 sessionStorage 大一些,具体数值取决于浏览器的具体实现。

  • 使用方法不同:sessionStorage localStorage 的使用方法基本相同,都可以使用 setItem() 和 getItem() 方法来存储和获取数据。但是,在使用时,需要注意区分两者的作用域。

总的来说,sessionStorage localStorage 都是用于存储浏览器中的数据的方式,但是它们在存储范围、容量和使用方法等方面有一些区别。在选择使用哪种存储方式时,应该根据自己的需要进行选择。

总结

  • localStorage: 页面关闭数据不丢失,具有永久性质。除非主动删除数据。

  • sessionStorage:页面关闭,数据会自动清除

rem和em

rem 和 em 都是用于设置字体大小的单位,它们的区别在于计算方式不同。

  • rem(Root em)是相对于根元素的字体大小的单位。也就是说,如果你在根元素(一般是 html 元素)中设置了字体大小,那么所有使用 rem 单位的字体大小都会相对于根元素的字体大小进行计算。

  • em(em)是相对于父元素的字体大小的单位。也就是说,如果你在父元素中设置了字体大小,那么所有使用 em 单位的字体大小都会相对于父元素的字体大小进行计算。

​ 例如,如果你在根元素中设置了字体大小为 16px,并在一个子元素中使用了 rem 单位设置字体大小为 2rem,那么子元素的字体大小就会是 32px(2 * 16px)。如果你在子元素中使用了 em 单位设置字体大小为 2em,那么子元素的字体大小就会是 32px(2 * 16px)。

​ 一般来说,rem 单位更常用于设置整个网站的字体大小,因为它是相对于根元素计算的,可以方便地在整个网站中控制字体大小。而 em 单位更常用于设置单个元素的字体大小

响应式布局

​ 响应式布局是指网站的布局能够根据用户设备的不同尺寸和分辨率进行自适应。这种布局方式可以让网站在不同的设备上都能够很好的呈现,包括电脑、平板电脑、手机等。

下面是一些常用的响应式布局方法:

  • 使用流式布局(Fluid Layout):流式布局是指使用百分比来设置元素的宽度和高度,而不是固定的像素值。这种布局方式可以让元素在不同的设备上自动调整大小,适应屏幕尺寸的变化。
  • 使用媒体查询(Media Queries):媒体查询是一种 CSS 技术,可以根据用户设备的屏幕尺寸和分辨率动态地应用不同的样式。这种方式可以让你为不同的设备设置不同的布局和样式。
  • 使用自适应网格布局(Adaptive Grid Layout):自适应网格布局是指使用网格布局(Grid Layout)技术,在不同的屏幕尺寸下自动调整网格的列数和行数,使得布局看起来更合理。
  • 使用弹性盒布局(Flexbox Layout):弹性盒布局是一种 CSS 技术,可以让元素在一个容器中自动地按照一定的规则进行布局。它可以让元素在不同的屏幕尺寸下自动调整大小,使得布局看起来更合理

有没有用过视频组件

是的,我曾使用过视频组件。视频组件是一种用于在网页或应用中播放视频的组件,通常使用 HTML5 中的 video 元素来实现

在使用视频组件时,通常可以指定视频的源文件地址、视频的宽度和高度、是否自动播放等参数。视频组件还可以支持一些常用的控制功能,如播放、暂停、快进、倒退等。

例如,可以使用以下代码来创建一个视频组件:

1
<video src="video.mp4" width="480" height="360" autoplay></video>

​ 这样,当网页加载时,就会在网页中自动播放 video.mp4 这个视频文件,但是各个浏览器会有所限制。据我所知,谷歌浏览器是禁用自动播放的。QQ浏览器没有禁用。

​ 但还是有些网站也可以实现自动播放,比如B站。我之间了解过,是浏览器中维护了一个名单,这些名单里有常见的域名权重。当权重到达一定时,就不会禁用这些域名的自动播放。

vue中可以使用类似:vue-core-video-player 的组件

​ 也可以回答,业务中没接触过此业务,但看过vue生态中有对应的组件库,如果需要可以很快学会。视频播放是前端的一个细分领域,有兴趣的了解下名称,开阔视野即可

为什么会跨域 ?

本质:跨域是浏览器基于同源策略的一种安全手段。

同源策略:

  • 本质:是一种约定, 浏览器 的一种⽤于隔离潜在恶意⽂件的重要安全保护机制。

  • 含义:即指在同一个域

  • 三个相同点:

    • 协议相同(protocol)

    • 主机相同(host)

    • 端口相同(port)

反之非同源请求,也就是协议、端口、主机其中一项不相同的时候,这时候就会产生跨域

不受同源策略影响的有哪些

除了以下三个资源获取类型的标签,在浏览器中,⼤部分内容都受同源策略限制。