> 文章列表 > 前端面试题整合

前端面试题整合

前端面试题整合

前端面试题

    • 1.对css盒模型的理解
    • 2.css选择器有哪些?优先级?哪些元素可以继承
        • 1、选择器分类:
          • 1、简单选择器(根据名称,id,类来选取元素)
          • 2、组合选择器(根据他们之间的特定关系来选取元素)
          • 3、伪类选择器(根据特定状态选取元素)
          • 4、伪元素选择器(选取元素的一部分并设置其样式)
          • 5、属性选择器(根据属性或属性值来选取元素)
        • 2、优先级
        • 3、那些元素可以继承
    • 3.元素水平垂直居中的方法有哪些?如果元素不定宽高呢?
    • 4.怎么理解回流跟重绘?什么场景下会触发?
    • 5.什么是响应式设计?响应式设计的基本原理是什么?如何做?
            • css随着页面的变化,可以产生不同的结构,这个就是响应式。
    • 6、如果要做优化,css提高性能的方法有哪些?
      • (1)为什么要做优化
      • (2)实现方式
          • 内联首屏关键 css
          • 异步加载css
          • 资源压缩
          • 合理使用选择器
          • 减少使用昂贵的属性
          • 不要使用@import
          • 其他
      • (2)总结
    • 7.对前端工程师这个职位是怎么样理解的?它的前景会怎么样
    • 8.说说JavaScript中的数据类型?存储上的差别?
          • 基本数据类型存储在栈中
          • 复杂数据类型存储在堆中
          • 声明变量时不同的内存地址分配
          • 不同类型数据导致赋值变量时的不同:
    • 9.typeof 与 instanceof 区别
    • 10.说说你对闭包的理解?闭包使用场景
            • 把全局变量变成局部变量
    • 11.bind、call、apply 区别?如何实现一个bind?
        • js继承
    • 12.说说你对事件循环的理解
    • 13.DOM常见的操作有哪些
    • 14.说说你对BOM的理解,常见的BOM对象你了解哪些?
    • 15.Javascript本地存储的方式有哪些?区别及应用场景?
    • 16.什么是防抖和节流?有什么区别?如何实现?
    • 17.如何通过JS判断一个数组
    • 18.说说你对作用域链的理解
    • 19.JavaScript原型,原型链 ? 有什么特点?
    • 20.请解释什么是事件代理
          • 为什么会有`事件代理`
    • 21.谈谈This对象的理解
    • 22.new操作符具体干了什么
    • 23.null,undefined 的区别
    • 24.javascript 代码中的"use strict";是什么意思
    • 25.同步和异步的区别
    • 26.谈一谈箭头函数与普通函数的区别
    • 27.JS 数组和对象的遍历方式,以及几种方式的比较
    • 28.如何解决跨域问题
    • 29.XML和JSON的区别
    • 30.谈谈你对webpack的看法
    • 31.webpack的打包原理
    • 32.如何优化webpack打包速度
    • 33.说说webpack中常见的Loader?解决了什么问题?
    • 34.说说webpack中常见的Plugin?解决了什么问题?
    • 35.说说你对promise的了解
    • 36.async函数是什么,有什么作用
    • 37.有使用过vue吗?说说你对vue的理解
    • 38.你对SPA单页面的理解,它的优缺点分别是什么?如何实现SPA应用呢
    • 39.SPA首屏加载速度慢的怎么解决?
    • 40.VUE路由的原理
    • 41.Vue中组件和插件有什么区别?
    • 42.Vue组件之间的通信方式都有哪些
    • 43.你了解vue的diff算法吗?说说看
    • 44.为什么需要 Virtual Dom
    • 45.Vue3.0的设计目标是什么?做了哪些优化
    • 46.Vue3.0 所采用的 Composition Api 与 Vue2.x 使用的 Options Api 有什么不同?
    • 47.说一下Vue数据响应式的原理
    • 48.说说对 React 的理解?有哪些特性?
    • 49.说说 Real DOM 和 Virtual DOM 的区别?优缺点?
    • 50.说说 React 生命周期有哪些不同阶段?每个阶段对应的方法是?
    • 51.说说 React中的setState执行机制
    • 52.说说对React中类组件和函数组件的理解?有什么区别?
          • **类组件**
    • 53.说说对React Hooks的理解?解决了什么问题?
    • 54.说说你对Redux的理解?其工作原理?
    • 55.说说 React 性能优化的手段有哪些
    • 56.vue、react、angular 区别
    • 57.说说你对 TypeScript 的理解?与 JavaScript 的区别
    • 58.说说你对 TypeScript 中泛型的理解?应用场景?
    • 59.说说你对微信小程序的理解?优缺点?
    • 60.说说你对发布订阅、观察者模式的理解?区别?
    • 61.项目做过哪些性能优化
    • 62.描述浏览器的渲染过程,DOM树和渲染树的区别
    • 63.你认为什么样的前端代码是好的
    • 64.从浏览器地址栏输入url到显示页面的步骤
    • 65.http 请求报文响应报文的格式
    • 66.Token cookie session 区别
    • 67.CORS跨域的原理
    • 68.什么是MVVM
    • 69.说说你对版本管理的理解?常用的版本管理工具有哪些?
    • 70.说说你对Git的理解?
    • 71.说说Git常用的命令有哪些
    • 72.说说 git 发生冲突的场景?如何解决?
  • 自己总结
      • 1、ES5、ES6 和 ES2015 有什么区别?
      • 2、 babel 是什么,有什么作用?
      • 3、let 有什么用,有了 var 为什么还要用 let?
      • 4、举例 ES6 对 String 字符串类型做的常用升级优化
          • 优化部分
          • 升级部分
      • 5、举例 ES6 对 Array 数组类型做的常用升级优化
          • 优化部分
          • 升级部分
      • 6、举例 ES6 对 Number 数字类型做的常用升级优化
          • 优化部分
          • 升级部分
      • 7、举例ES6对Function函数类型做的常用升级优化
          • 优化部分
          • 升级部分

1.对css盒模型的理解

  • 定义:当对一个文档进行布局的时候,浏览器的渲染引擎会根据标准之一的css基础盒模型,将所有元素表示为一个个矩形盒子

  • 组成部分:content(内容)、border(边框)、padding(内边距)、margin(外边距)
    content:实际内容,文本和图像
    border:即边框,颜色,样式,粗细 778
    padding:即内边距,清除内容周围的区域,透明,取值不能为负,受盒子的background属性影响
    margin:即外边距,在元素外额外创建的空白,不能放其他内容

  • 4类型:W3C标准盒子模型IE怪异盒子模型
    1、W3C标准盒子模型是浏览器默认的盒子模型
    盒子总宽度:width + padding + margin + border;
    盒子总高度:height+ padding + margin + border;
    其中width/hegith只包括内容区域,不包含paddingborder
    2、IE怪异盒子模型
    盒子总宽度:width + margin;
    盒子总高度:height + margin;
    其中width/height包含了paddingborder

  • Box-sizing

    css中的box-sizing属性定义了引擎如何计算一个元素的总高度和总宽度
    
1. box-sizing: content-box|border-box|inherit:
2. content-box:默认值,元素的width和height不包含padding和border
3. border-box:元素的width和height包含padding,border与怪异盒子模型一致
4. inherit:指定box-sizing的值,从父级继承

2.css选择器有哪些?优先级?哪些元素可以继承

css选择器:用于查找要设置样式的html元素

1、选择器分类:

1、简单选择器(根据名称,id,类来选取元素)
  • 元素(标签)选择器§
    根据元素名称来选择html元素
  • id选择器(#id)
    根据id来选择html元素,且id须唯一,因此id选择器是用来选择唯一的元素!
    id名前加#号来表示
    注:id不能以数字开头
  • class类选择器(.class)
    根据class来选取html元素
    语法.后面跟类名
  • 通配符选择器(*)
    选择html页面下的所有元素
2、组合选择器(根据他们之间的特定关系来选取元素)
  • 后代选择器(空格)
    匹配指定元素后代的所有元素
    (div p)div下面的所有p标签
  • 子选择器(>)
    子选择器匹配属于指定元素子元素的所有元素
    (div>p)div包裹的第一层p标签
  • 相邻兄弟选择器(+)
    匹配所有作为指定元素的相邻同级的元素
    (div+p)兄弟(同级)元素必须具有相同的父元素,相邻指的是紧随其后
  • 通用兄弟选择器(~)
    匹配指定元素的同级元素的所有元素
    (div ~ p)div同级且在div后面的所有p标签
  • 族群选择器(,)
    (div,p)选择div、p的所有元素
3、伪类选择器(根据特定状态选取元素)

伪类用于定义元素的特殊状态。

:link :选择未被访问的链接:visited:选取已被访问的链接:active:选择活动链接:hover :鼠标指针浮动在上面的元素:focus :选择具有焦点的:first-child:父元素的首个子元素:first-of-type 表示一组同级元素中其类型的第一个元素:last-of-type 表示一组同级元素中其类型的最后一个元素:only-of-type 表示没有同类型兄弟元素的元素:only-child 表示没有任何兄弟的元素:nth-child(n) 根据元素在一组同级中的位置匹配元素:nth-last-of-type(n) 匹配给定类型的元素,基于它们在一组兄弟元素中的位置,从末尾开始计数:last-child 表示一组兄弟元素中的最后一个元素:root 设置HTML文档:empty 指定空的元素:enabled 选择可用元素:disabled 选择被禁用元素:checked 选择选中的元素:not(selector) 选择与 <selector> 不匹配的所有元素
4、伪元素选择器(选取元素的一部分并设置其样式)

伪元素用于设置元素指定部分的样式。
在 CSS3 中,双冒号取代了伪元素的单冒号表示法。这是 W3C 试图区分伪类和伪元素的尝试。
在 CSS2 和 CSS1 中,伪类和伪元素都使用了单冒号语法。

为了向后兼容,CSS2 和 CSS1 伪元素可接受单冒号语法。

:first-letter :用于选取指定选择器的首字母:first-line :选取指定选择器的首行:before : 选择器在被选元素的内容前面插入内容:after : 选择器在被选元素的内容后面插入内容
5、属性选择器(根据属性或属性值来选取元素)
[attribute] 选择带有attribute属性的元素[attribute=value] 选择所有使用attribute=value的元素[attribute~=value] 选择attribute属性包含value的元素[attribute|=value]:选择attribute属性以value开头的元素//css3中新增[attribute*=value]:选择attribute属性值包含value的所有元素[attribute^=value]:选择attribute属性开头为value的所有元素[attribute$=value]:选择attribute属性结尾为value的所有元素

2、优先级

内联 > ID选择器 > 类选择器 > 标签选择器

具体的计算:优先级是由 A 、B、C、D 的值来决定的,
计算规则如下

  • 如果存在内联样式,那么A=1,否则A=0;

  • B的值等于ID选择器的出现次数

  • C的值等于类选择器 和 属性选择器 和伪类选择器出现的总次数

  • D的值等于标签选择器 和 伪元素选择器 出现的总次数

    举例:

    #nav-global > ul > li > a.nav-link求A,B,C,DA = 内联样式,为零B = ID选择器, 为一C = 类选择器+属性选择器+伪类选择器 为(1+0+0)= 1D = 标签选择器 + 伪元素选择器 为(3+0) = 3
    

比较规则如下

  • 从左往右依次比较,较大者优先级更改
  • 如果相等,则继续往右移动一位进行比较
  • 如果4位全部相等,则后面的会覆盖前面的

经过上面的优先级计算规则,我们知道内联样式的优先级最高,如果外部样式需要覆盖内联样式,就需要使用!important

3、那些元素可以继承

在css中继承指的是给父元素设置一些属性,后代元素会自动拥有这些属性
关于继承属性可以分为

  • 字体属性继承

    font:组合字体
    font-family:规定元素的字体系列
    font-weight:设置字体的粗细
    font-size:设置字体的尺寸
    font-style:定义字体的风格
    font-variant:偏大或偏小的字体

  • 文本系列属性

    text-indent:文本缩进
    text-align:文本水平对齐
    line-height:行高
    word-spacing:增加或减少单词间的空白
    letter-spacing:增加或减少字符间的空白
    text-transform:控制文本大小写
    direction:规定文本的书写方向
    color:文本颜色

  • 元素可见性

    visibility

  • 表格布局属性

    caption-side:定位表格标题位置
    border-collapse:合并表格边框
    border-spacing:设置相邻单元格的边框间的距离
    empty-cells:单元格的边框的出现与消失
    table-layout:表格的宽度由什么决定

  • 列表属性

    list-style-type:文字前面的小点点样式
    list-style-position:小点点位置
    list-style:以上的属性可通过这属性集合

  • 引用

    quotes:设置嵌套引用的引号类型

  • 光标属性

    cursor:箭头可以变成需要的形状

继承中比较特殊的几点:

  • a标签的字体属性不能被继承
  • h1-h6标签的字体大小不能被继承

无继承属性

  • display
  • 文本属性: vertical-align、text-decoration
  • 盒子模型的属性:宽度,高度,内外边框,边距
  • 背景属性:背景图片,颜色,位置等
  • 定位属性:浮点,清除浮动,定位position等
  • 生成内容属性:content,counter-reset、counter-increment
  • 轮廓样式属性:outline-style,outline-width、outline-color、outline
  • 页面样式属性:size,page-break-before、page-break-after

3.元素水平垂直居中的方法有哪些?如果元素不定宽高呢?

  • 元素水平垂直居中的方法:

    • 利用定位+margin:auto

      父级设置绝对定位,子元素设置相对定位,4个定位属性设置为0,这时如果子级没有设置宽高,宽高则拉得与父级一样,所有子级须设置宽高,然后,在给子级设置margin:auto,就可以居中了

    • 利用定位+margin:负值

      • 父级设置绝对定位,子元素设置相对定位,子元素的topleft属性,设置为50%
      • 子级的margin设置为负值,移动到中间位置,
        这种方案,不需要父级的宽高,当需要知道子元素的宽高
    • 利用定位+transform

      这种方案和定位+margin:负值的方式一样,只不过不需要设置margin值了,通过设置translate(-50%, -50%)来将子元素定位到中间位置,这种方案不需要知道子元素的宽高

    • table布局

      设置父元素为display:table-cell,子元素设置 display: inline-block。利用给父元素设置verticaltext-align可以让所有的行内块级元素水平垂直居中

    • flex布局

      display: flex时,表示该容器内部的元素将按照flex进行布局
      align-items: center表示这些元素将相对于本容器水平居中
      justify-content: center也是同样的道理垂直居中

    • grid网格布局

      display: grid时,表示该容器内部的元素将按照flex进行布局
      align-items: center表示这些元素将相对于本容器水平居中
      justify-content: center也是同样的道理垂直居中

  • 上述方法中不知道元素宽高大小仍能实现水平垂直居中的方法有:

    利用定位+margin:auto

    利用定位+transform

    flex布局

    grid布局

  • 总结:

    根据元素标签的标签的性质不同可以分为

    内联元素居中布局
    

    块级元素居中布局
    
    • 内联元素居中布局

      水平居中
      行内元素可设置:text-align: center
      flex布局设置父元素:display: flex; justify-content: center
      垂直居中
      单行文本父元素确认高度:height === line-height
      多行文本父元素确认高度:display: table-cell; vertical-align: middle

    • 块级元素居中布局

      水平居中
      定宽: margin: 0 auto
      绝对定位+left:50%+margin:负自身一半
      垂直居中
      position: absolute设置left、top、margin-left、margin-top(定高)
      display: table-cell
      transform: translate(x, y)
      flex(不定高,不定宽)
      grid(不定高,不定宽),兼容性相对比较差

4.怎么理解回流跟重绘?什么场景下会触发?

回流:布局引擎会根据各种样式计算每个盒子在页面上的大小与位置
重绘:当计算好盒模型的位置、大小即其他属性后,浏览器根据每个盒子的大小特性进行绘制

回流主要是用来计算节点的位置和几何信息,所以当页面布局和几何信息发生变化的时候,就会需要回流
形成场景:
1.添加或删除可见的DOM元素
2.元素的位置发生变化
3.元素的尺寸发生变化(外边距,内边距,边框大小,高度和宽度等)
4.内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代
5.页面一开始渲染的时候(不可避免)
6.浏览器窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小)
7.获取一些特定的值

    offsetTop,offsetLeft,offsetWidth,offsetHeight,scollTop,scollLeft,scollWidth,scollHeight,clientTop,clientLeft,clientWidth,clientHeight这些属性有一个共性就是需要通过即时计算得到的,还有getComputedStyle方法

触发回流就一定会触发重绘,还有颜色的修改,文本方向的修改,阴影的修改

大多数浏览器都会通过队列化修改并批量执行来优化重排过程,浏览器会将修改操作放入到队列里,直到过了一段时间或者操作达到了一个阈值,才清空队列

  • 如果想设定元素的样式,通过改变元素的 class 类名 (尽可能在 DOM 树的最里层)
    避免设置多项内联样式
  • 应用元素的动画,使用 position 属性的 fixed 值或 absolute 值(如前文示例所提)
  • 避免使用 table 布局,table 中每个元素的大小以及内容的改动,都会导致整个 table 的重新计算
  • 对于那些复杂的动画,对其设置 position: fixed/absolute,尽可能地使元素脱离文档流,从而减少对其他元素的影响
  • 使用css3硬件加速,可以让transform、opacity、filters这些动画不会引起回流重绘
  • 避免使用 CSS 的 JavaScript 表达式
  • 在使用 JavaScript 动态插入多个节点时, 可以使用DocumentFragment. 创建后一次插入. 就能避免多次的渲染性能

5.什么是响应式设计?响应式设计的基本原理是什么?如何做?

css随着页面的变化,可以产生不同的结构,这个就是响应式。

是一种网络页面设计布局,页面的设计与开发应当根据用户行为以及设备环境(系统平台,屏幕尺寸,屏幕定向等)进行相应的响应和调整

通过媒体查询检测不同的设备屏幕尺寸做处理,为了处理移动端,页面头部必须有meta声明viewport

  • 媒体查询
  • 百分比
  • vw/vh
  • rem 【1rem等于html的字体大小】
px、em、rem和%的区别与总结
1.px是固定长度单位,不随其它元素的变化而变化;
2.em和%是相对于父级元素的单位,会随父级元素的属性 (font-size或其它属性)变化而变化:
3.rem是相对于根目录(HTML元素)的,所有它会随HTML元素的属性(font-size) 变化而变化
4.px和%用的比较广泛一些,可以充当更多属性的单位,而em和rem是字体大小的单位,用于充当font-size属性的单位
5.一般来说: 1em = 1rem = 100% = 16 px

响应式设计的思考方向:
* 弹性盒子和媒体查询
* 使用百分比创建流式布局,同时使用媒体查询限制元素的尺寸和内容变更范围
* 使用相对单位是内容自适应调节
* 选择断点,针对不同断点实现不同的布局和内容展示

优点:
面对不同分辨率,灵活性强
能够快捷解决多设备显示适应问题
缺点:
仅适用布局、信息、框架并不复杂的部门类型网站
兼容各种设备工作量大,效率低下
代码累赘,会出现隐藏无用的元素,加载时间加长
其实这是一种折中性质的设计解决方案,多方面因素影响而达不到最佳效果
一定程度上改变了网站原有的布局结构,会出现用户混淆的情况

6、如果要做优化,css提高性能的方法有哪些?

(1)为什么要做优化

css主要是用来做页面渲染和内容展示的,所有css影响着用户对网站的第一体验,所以需要对css进行优化

(2)实现方式

  • 内联首屏关键 css
    通过内联css关键代码能够使浏览器在下载完html后就立可渲染,但,较大的css代码不适合
    
  • 异步加载css
    在css文件请求,下载,解析完成之前,css会阻塞渲染,浏览器讲不会渲染任何已处理的内容,这时候就可以采用异步加载的方式
    
  • 资源压缩
    使用webpack,gulp/grunt、rollup等模块化工具,将css代码进行压缩,使文件变小,大大降低了浏览器的加载时间
    
  • 合理使用选择器
    css的匹配规则是从右往左开始匹配,如果嵌套过多,则查找时间也就有多
    
  • 减少使用昂贵的属性
    如 box-shadow/border-radius/filter/透明度/:nth-child等,会降低浏览器的渲染性能
    
  • 不要使用@import
    css的引入方式有两种:link和@import
    其中@import会影响页面的并行下载,使页面在加载是增加额外的延迟,增加额外的往返耗时,而且多个@import可能导致下载顺序紊乱
    
  • 其他
减少重排以及不必要的重绘
了解那些属性可以继承,减少属性的重复编写
把小的icon转换为base64的编码

(2)总结

css实现性能的方式可以从选择器嵌套、属性特性、减少http这三面考虑,同时还要注意css代码的加载顺序

7.对前端工程师这个职位是怎么样理解的?它的前景会怎么样

前端工程师主要通过html+css+js等代码方式,实现页面的制作及逻辑交互;通过技术来提升用户的体验效果。前端不仅仅局限于网页的制作,还有app,小程序,游戏等开发;在前端的开发中,除了使用最基本的css+html+js外,还可以使用一些新的流行框架或插件,比如 vue,react等;前端不仅可以写出优美的代码,还能高质量的还原效果图;可以说是最贴近用户需求的程序员;一步步让产品优化的更好,给顾客最好的体验效果。
前端工程师的入门门槛比较多,且上限较高,就整个市场来说,数量再多,都不会影响前端开发的需求量,饱和的只是低端饱和,高端始终缺人。

8.说说JavaScript中的数据类型?存储上的差别?

  • 基本数据类型存储在栈中
    1. Number:整型(十进制,八进制,二级制)和浮点类型和NAN操作失败
    2. Undefined:当使用 var或 let声明了变量但没有初始化时,就相当于给变量赋予了  undefined值
    3. String:字符串可以使用双引号(")、单引号(')或反引号(`)表示,并且字符串不可变
    4. Null:null表示一个空对象指针,这也是typeof传一个null会返回object,undefined是由null派生的
    5. Boolean:Boolean类型有两个值true和false,通过Boolean可以将其他值变成Boolean
    6. Symbol:Symbol (符号)是原始值,且符号实例是唯一、不可变的。符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险
    
  • 复杂数据类型存储在堆中
    7. Object:创建object常用方式为对象字面量表示法,属性名可以是字符串或数值
    8. Array:js数组是一组有序的数据,但跟其他语言不同的是,数组可以存储任意数据类型,并且数组的大小,也是根据数据的变化而动态变化的
    9. Function:函数实际上是一个对象,每个函数都是Function的实例,而Function也有属性和方法,跟其他引用类型一样
    
  • 声明变量时不同的内存地址分配
    • 简单类型的值存放在栈中,在栈中存放的是对应的值
    • 复杂数据类型存放在堆中,在堆中存放的是执行内存的地址
  • 不同类型数据导致赋值变量时的不同:
    • 简单数据类型赋值,是生成相同的数据,两个对象对应不同的地址
    • 复杂数据类型赋值,是将保存对象的内存地址赋值给另一个变量,也就是两个变量指向堆内存中的同一个对象

9.typeof 与 instanceof 区别

1、typeof 用来检测数据类型的运算符

2、instanceof 检测某一个实例是否属于某个类

3、constructor 构造函数 作用和 instanceof 非常相似

4、Object.prototype.toString.call() 是最准确最常用方法

1、typeof会返回一个变量的基本数据类型,instanceof返回Boolean值
2、instanceof可以准确的判断复杂引用数据类型,但是不能正确判断基础数据类型
3、而typeOf也存在弊端,他虽然可以判断基础数据类型(null除外),但是引用数据类型中,除了function类型以为,其他的也无法判断
4、可以使用Object.prototype.toString

10.说说你对闭包的理解?闭包使用场景

把全局变量变成局部变量
闭包简单来说就是,函数套函数
官方:一个函数和对其周围状态的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包
1、创建私有变量
2、延长变量的生命周期
一般函数内的变量在函数返回之后就被销毁了,但是闭包会保存对创建是所在的词法环境的引用,即便创建时所在的执行上下文被销毁,但创建时所在词法环境依然存在,以达到延长变量的生命周期的目的
其他:计数器,延迟调用,回调等闭包的应用
如果不是某些特定任务需要使用闭包,在其它函数中创建函数是不明智的,因为闭包在处理速度和内存消耗方面对脚本性能具有负面影响

11.bind、call、apply 区别?如何实现一个bind?

apply:接收两个参数,第一个参数是this指向,第二参数是3函数接收的参数,以数组的形式传入,改变this指向之后会立即执行,但只是临时改变this一次,当第一个参数为nullundefind时默认指向window对象

function fn(...args){    console.log(this,args);}let obj = {    myname:"张三"}fn.apply(obj,[1,2]); // this会变成传入的obj,传入的参数必须是一个数组;fn(1,2) // this指向window//当第一个参数是null或undefined时:fn.apply(null,[1,2]); // this指向windowfn.apply(undefined,[1,2]); // this指向window

call:第一个参数this指向,第二个参数列表,除了第二个参数的不同,其他的和apply一样

function fn(...args){    console.log(this,args);}let obj = {    myname:"张三"}fn.call(obj,1,2); // this会变成传入的obj,传入的参数必须是一个数组;fn(1,2) // this指向window
bind`: bind方法和call很相似,第一参数也是this的指向,后面传入的也是一个`参数列表`(但是这个参数列表可以分多次传入)
改变this指向后不会立即执行,而是返回一个`永久改变this指向的函数
function fn(...args){    console.log(this,args);}let obj = {    myname:"张三"}const bindFn = fn.bind(obj); // this也会变成传入的obj,bind不是立即执行需要执行一次bindFn(1,2) // this指向objfn(1,2) // this指向window
  • 三者都可以改变this指向
  • 三者的第一个参数是this指向的对象,如果是null或undefined则指向window对象
  • applycall都是临时改变一次this指向,并在改变之后,立即执行,bind是永久改变this,返回一个函数,需要调用这个
  • apply传参是数组,call传参是参数列表,bind都可以

大致分为三步:
修改this指向
动态传递参数

//方式一,只在bind中传递参数fn.bind(obj,1,2)()//方式二:在bind中传递函数参数,也在返回函数中传递参数fn.bind(obj,1)(2)

兼容new 关键字

Function.prototype.myBind = function (context) {    // 判断调用对象是否为函数    if (typeof this !== "function") {        throw new TypeError("Error");    }    // 获取参数    const args = [...arguments].slice(1),          fn = this;    return function Fn() {        // 根据调用方式,传入不同绑定值        return fn.apply(this instanceof Fn ? new fn(...arguments) : context, args.concat(...arguments));     }}

js继承

  • 原型链继承
  • 借用构造函数继承
  • 组合模式继承
  • 共享原型继承
  • 原型式继承
  • 1111111111寄生式继承
  • 寄生组合式继承
  • ES6 中 class 的继承(新)

12.说说你对事件循环的理解

js是一门单线程语言,意味着在同一时间只能做一件事,但并不意味这单线程就是阻塞,而实现单线程非阻塞的方法就是事件循环

在js中任务可以分为两种:同步任务 和 异步任务
  • 同步任务:立即执行任务,同步任务一般直接进入主线程中
  • 异步任务:异步执行的任务,如ajax网络请求

事件循环:

同步任务进入主线程,即主执行栈,异步任务进入任务队列,主线程内的任务执行完毕为空,会去任务队列读取对应的任务。推入主线程,重复执行上述过程就是事件循环

宏任务和微任务

将任务分为同步任务和异步任务并不准确,因为,异步任务是存放在队列中的,而队列讲究一个先进先出,排在前面的任务,会优先执行,可事实并非如此,原因在与异步任务还可以分为宏任务微任务

微任务

一个需要异步执行的函数,执行时机是在主函数执行结束之后,当前宏任务结束之前
常见的微任务有:
Promise.then 、MutaionObserver、Object.observe(已废弃;Proxy 对象替代)、process.nextTick(Node.js)

宏任务

宏任务的时间粒度比较大,执行的时间间隔是不能精确控制的,对一些高实时性的需求不太符合
常见的宏任务有:
script(可以理解为外层同步代码),setTimeout/setInterval、UI rendering/UI事件、postMessage、MessageChannel

执行机制

执行一个宏任务,如果遇到微任务就将他放到微任务的事件队列中
当前宏任务执行完成后,会查看微任务的事件队列,然后将里面的微任务依次执行完毕

async和await

async是用来声明一个异步方法,而await是用来等待异步方法执行
async返回一个promise对象
await后面跟一个promise对象,返回该对象的结果,如果不是promise对象,就直接返回结果

13.DOM常见的操作有哪些

DOM 文档对象模型是htmlxml文档的编程接口
DOM节点分为三种元素节点属性节点文本节点

常见的DOM操作有:

创建节点、查询节点、更新节点、添加节点、删除节点

创建节点

createElement:创建元素节点const div = documnet.createElement('div')createTextNode:创建文本节点const textEI = document.createTextNode('content')createDocumentFragment:创建文档碎片,用来存储临时节点const fragment = document.createDocumentFragment();createAttribute:创建属性节点,可以是自定义属性const dataAttribute = document.createAttribute('custom');

查询节点

document.getElementById('id属性值');返回拥有指定id的对象的引用document.getElementsByClassName('class属性值');返回拥有指定class的对象集合document.getElementsByTagName('标签名');返回拥有指定标签名的对象集合document.getElementsByName('name属性值'); 返回拥有指定名称的对象结合document/element.querySelector('CSS选择器');  仅返回第一个匹配的元素document/element.querySelectorAll('CSS选择器');   返回所有匹配的元素document.documentElement;  获取页面中的HTML标签document.body; 获取页面中的BODY标签document.all[''];  获取页面中的所有元素节点的对象集合型除此之外,每个DOM元素还有parentNode、childNodes、firstChild、lastChild、nextSibling、previousSibling属性

更新节点

innerHtml : 不但可以修改一个DOM节点的文本内容,还可以直接通过HTML片段修改DOM节点内部的子树innerText,textContent:自动对字符串进行HTML编码,保证无法设置任何HTML标签,两者的区别在于读取属性时,innerText不返回隐藏元素的文本,而textContent返回所有文本style:DOM节点的style属性对应所有的CSS,可以直接获取或设置。遇到-需要转化为驼峰命名

添加节点

appendChild:把一个子节点添加到父节点的最后一个子节点insertBefore:把子节点插入到指定的位置setAttribute:在指定元素中添加一个属性节点,如果元素中已有该属性改变属性值

删除节点

删除一个节点,首先要获得该节点本身以及它的父节点,然后,调用父节点的removeChild把自己删掉,虽然在节点树上不存在,但依然存在在内存中

14.说说你对BOM的理解,常见的BOM对象你了解哪些?

BOM

BOM浏览器对象模型,提供了独立于内容与浏览器窗口进行交互的对象,用来和浏览器作一些交互,比如界面的其前进,后退,刷新等

浏览器的全部内容可以看着DOM,整个浏览器可以看做BOM
区别:
DOM是文档对象模型,BOM是浏览器对象模型
DOM的的顶级对象是documentBOM的顶层对象是window
DOM主要是操作页面元素,BOM主要是和浏览器窗口的交互
DOMW3C标准规范,BOM是浏览器厂商在各自浏览器定义的,交互性较差

BOM的常见对象

windom,location,navigator,screen,history

windom是BOM的核心对象,表示浏览器的一个实例,主要是一些操作窗口的方法

location:请求页面

navigator:用来获取浏览器的属性

screen:保存的是浏览器客户端的信息

history:操作浏览器的历史记录,可以进行页面跳转

15.Javascript本地存储的方式有哪些?区别及应用场景?

js的本地存储方式主要有四种:
cookie,sessionStorage,localStorage,indexedDB

Cookie

类型为「小型文本文件」,指某些网站为了辨别用户身份而储存在用户本地终端上的数据。是为了解决 HTTP无状态导致的问题,一般不超过4kb

localStorage

h5新方法,
特点:
持久化保存
存储的信息在同一域中可共享
当本页操作(新增、修改、删除)了localStorage的时候,本页面不会触发storage事件,但是别的页面会触发storage事件。
大小:5MB(受浏览器厂商影响)
localStorage本质上是对字符串的读取,如果存储内容多的话会消耗内存空间,会导致页面变卡
受同源策略的限制

sessionStorage

sessionStoragelocaStorage本质上一样,只不过是浏览器页面已关闭,sessionStorage就会被删除

cookie、localStorage、sessionStorage的区别

存储大小:cookie数据大小不能超过4k,sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大

有效时间:localStorage存储持久数据,浏览器关闭后数据不丢失除非主动删除数据; sessionStorage数据在当前浏览器窗口关闭后自动删除;cookie设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭

数据与服务器之间的交互方式,cookie的数据会自动的传递到服务器,服务器端也可以写cookie到客户端; sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存

应用场景

标记用户与跟踪用户行为的情况,推荐使用cookie
适合长期保存在本地的数据(令牌),推荐使用localStorage
敏感账号一次性登录,推荐使用sessionStorage
存储大量数据的情况、在线文档(富文本编辑器)保存编辑历史的情况,推荐使用indexedDB

16.什么是防抖和节流?有什么区别?如何实现?

防抖和节流:都是为了优化高频率执行代码的的一种手段

防抖:

n秒后在执行该事件,若在n秒内被重复触发,则重新执行

节流:

n秒内只执行一次,如果在n秒之内重复触发,只有一次生效

实现节流*

//实现节流可以通过时间戳和定时器的写法//使用时间戳的写法,时间会立即执行,停止触发之后没有办法再次执行
function throttled1(fn, delay = 500) {//第一次触发,生成触发的时间    let oldtime = Date.now()    return function (...args) {    //生成第二次触发的时间        let newtime = Date.now()        //判断第二次触发和第一次触发的时间,是否在规定时间内        if (newtime - oldtime >= delay) {            fn.apply(null, args)            oldtime = Date.now()        }    }}//  定时器的写法function throttled2(fn, delay = 500) {//定义一个定时器容器    let timer = null    return function (...args) {    //判断是否有定时器        if (!timer) {            timer = setTimeout(() => {                fn.apply(this, args)                timer = null            }, delay);        }    }}//时间戳加定时器生成一个更精确的节流函数function throttled(fn, delay) {    let timer = null    let starttime = Date.now()    return function () {        let curTime = Date.now() // 当前时间        let remaining = delay - (curTime - starttime)  // 从上一次到现在,还剩下多少多余时间        let context = this        let args = arguments        clearTimeout(timer)        if (remaining <= 0) {            fn.apply(context, args)            starttime = Date.now()        } else {            timer = setTimeout(fn, remaining);        }    }}

防抖的实现

function debounce(func, wait) {    let timeout;    return function () {        let context = this; // 保存this指向        let args = arguments; // 拿到event对象        clearTimeout(timeout)        timeout = setTimeout(function(){            func.apply(context, args)        }, wait);    }}

区别

节流不管事件触发有多频繁,都会保证在规定时间内一定执行一次,而防抖只是在最后一次才会触发

应用场景

防抖
搜索框搜索输入。手机号,邮箱的验证
节流
滚动加载,

17.如何通过JS判断一个数组

1.Array.isArray()2.instanceof//通过查找prototype属性上是否存在3.constructor//构造函数的constructor属性指向构造函数4.Object.prototype.toString.call()

18.说说你对作用域链的理解

作用域,即变量(变量作用域又称上下文)和函数生效(能被访问)的区域或集合

作用域分类:全局作用域,函数作用域,块级作用域

全局作用域

任何不在函数或大括号内声明的变量,都在全局作用域下,全局声明的变量可以在程序的任何位置访问

函数作用域

函数作用域也就局部作用域,如果一个变量声明在函数内部,那么这个变量就只能在函数内部访问到

块级作用域

es6中新增letconst关键字,和var关键字不同,在大括号内使用letconst声明的变量存在块级作用域中,在大括号外不能访问这些变量

词法作用域

词法作用域又称静态作用域,变量被创建是就确定好了,而非执行阶段确定的,js就是词法作用域

作用域链

当在javascript中使用一个变量的时候,首先js引擎会尝试在当前作用域下去寻找该变量,如果没有找到,在到他的上层作用域中去寻找,以此类推直到找到该变量或是已经到了全局作用域下,如果全局作用域下仍然找不到该变量,他就会在全局范围内隐式声明该变量(非严格模式下)或是直接报错

19.JavaScript原型,原型链 ? 有什么特点?

原型

每一个构造函数都有一个prototype属性,这个属性成为显示原型
构造函数实例化后或对象都有一个__proto__属性,称之为对象的隐式原型属性,后续原型链通过__proto__查找属性

原型链

当我们访问对象的某个属性是,会先从当前对象中查找,如果没有找到则继续去对象的proto隐式原型中去查找,如果还没有找到则继续向上级对象的原型中查找,直到找到顶层对象object对象,如果没有找到返回undefined,这种通过对象的__proto__隐式原型查找属性的链条关系就称之为原型链

特点

  • 一切对象都继承自object对象,object对象直接继承根源对象null
  • 一切的函数对象(包括object对象),都是继承自Function对象
  • object对象直接继承自Function对象
  • Function对象的__proto__属性会指向自己的原型对象,最终还是继承自Object对象

20.请解释什么是事件代理

为什么会有事件代理
如果给每一个元素项都绑定一个函数,或者是事件,则对内存消耗就比较大

事件代理:就是把一个元素的响应事件的函数委托给另一个元素
js的事件流会经过三个阶段:捕获阶段->目标阶段->冒泡阶段,而事件委托就是在冒泡阶段完成。
事件委托:会把一个或一组元素的事件委托到它的父级或者更外层元素上,真正绑定事件的是外层元素,而不是目标元素
当事件响应到目标元素上时,会通过事件冒泡机制从而触发它的外层元素的绑定事件上,然后在外层元素上执行函数

21.谈谈This对象的理解

定义

函数的 this 关键字在javaSctipt 中的表现略有不同,此外,在严格模式和非严格模式中也有一些差别,在函数的过程中,this一旦被确定下来,就不能更改了,且this永远指向的是最后调用它的对象

绑定规则

根据使用方式的不同,this的值也有所不同,主要有以下几种情况:
- 默认绑定
- 隐式绑定
- new绑定
- 显式绑定

默认绑定

在非严格模式下,this对象默认指向 window 对象,在严格模式下,this 默认绑定到 undefined

隐式绑定

函数作为方法调用,this指向它的上级对象

new绑定

通过new关键字生成一个实例对象,此时this指向这个实例对象,如果,new的时候返回一个对象,则指向这个返回的对象,如果,返回一个简单数据类型,这还指向这个实例对象

显示修改

apply、call、bind

箭头函数

在箭头函数中,this指向的是window

优先级

new绑定优先级 > 显示绑定优先级 > 隐式绑定优先级 > 默认绑定优先级

22.new操作符具体干了什么

一种创建对象的方法,使用new关键字来创建一个拥有独立内存区域指向原型的指针的对象。当我们使用new的时候,js解析器会分配一块内存空间,用以存放当前的对象的自有属性。之后解析器会给这一对象一个_proto_属性指向的原型对象内容。

构造函数

用new关键字来调用的函数,首字母一般大写,构造函数会有一个属性prototype一个原型对象prototype,prototype的构造函数constructor指向该函数

用new生成的实例有一个proto指向该函数的prototype原型对象

(1) 创建一个新对象;
(2) 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象) ;
(3) 执行构造函数中的代码(为这个新对象添加属性) ;
(4) 返回新对象

23.null,undefined 的区别

  • undefined 表示不存在这个值
  • undefined 是一个缺失值,表示此处应该有值,当没赋值
  • null 表示一个值定义了,但是空值
  • null 是一个空对象指针,值为空值
  • 在验证null时,一定要使用===,==无法区分nullundefined

24.javascript 代码中的"use strict";是什么意思

use strict是一种ES5 添加的严格模式,这种模式使得 Javascript 在更严格的条件下运行,使JS编码更加规范化的模式,消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为

严格模式

  • 变量必须先声明再使用
  • 函数的参数不能有同名属性,否则报错
  • 不能使用with语句
  • 不能对只读属性赋值,否则报错
  • 不能使用前缀 0 表示八进制
  • 不能删除不可删除的属性
  • 不能删除变量delete prop,会报错,只能删除属性 delete global[prop]
  • eval 不会在他的外层作用域引入变量
  • evalarguments不能被重新赋值
  • arguments 不会自动反应函数参数的变化
  • 不能使用 arguments.calleearguments.caller
  • 禁止 this指向全局对象
  • 不能使用fn.callerfn.arguments获取函数调用的堆栈
  • 增加了保留字(比如protectedstaticinterface

25.同步和异步的区别

同步任务 在主线程上排队执行的任务,只有前一个任务执行完毕之后,才会执行下一个任务

异步任务 不进入主线程,而进入 任务队列 的任务,只有等主线程的任务执行完毕,任务队列 开始通知主线程请求执行任务,该任务才会进入主线程执行

常见的js异步操作

定时器,ajax,promise

异步任务又分为宏任务和微任务
1.宏任务
- 定时器
- 事件绑定
- ajax
- 回调函数
- Node中fs可以进行异步的I/O操作
2.微任务
- Promise(async/await) => Promise并不是完全的同步,在promise中是同步任务,执行resolve或者reject回调的时候,此时是异步操作,会先将then/catch等放到微任务队列。当主栈完成后,才会再去调用resolve/reject方法执行
- process.nextTick (node中实现的api,把当前任务放到主栈最后执行,当主栈执行完,先执行nextTick,再到等待队列中找)
- MutationObserver (创建并返回一个新的 MutationObserver 它会在指定的DOM发生变化时被调用。)
执行顺序优先级:SYNC => MICRO => MACRO
所有JS中的异步编程仅仅是根据某些机制来管控任务的执行顺序,不存在同时执行两个任务这一说法

26.谈一谈箭头函数与普通函数的区别

  • 外形不同,箭头函数使用箭头定义,普通函数使用Function定义
  • 箭头函数都是匿名函数,普通函数可以是匿名函数,可以是具名函数
  • 箭头函数不能用于构造函数,不能使用new
  • 箭头函数中的this指向不同,在普通函数中,this总是指向调用他的对象,如果是构造函数,this指向创建的对象的实例,箭头函数本身不创造this,但它在声明的时候可以不会调用它的上下文对象的this供自己使用
  • 结合call和bind方法使用,箭头函数在结合这两个方法使用的时候,只传入一个参数对this没有影响
  • 箭头函数不能绑定arguments,取而代之用rest参数…解决
  • 箭头函数不能Generarot函数,不能使用yeild关键字
  • 箭头函数不具有prototype属性
  • 箭头函数不具有super

27.JS 数组和对象的遍历方式,以及几种方式的比较

js遍历数组的方法

  • for循环是性能最好的一种循环,需要事先知道循环的次数
  • for……of是ES6新增的语法,可以直接遍历值,而不是数组的下标,还可以迭代Array,Map、Set、String
  • for……in遍历的是数组的下标
  • 数组内置方法forEach(),map(),filter(),every,some,reduce()
forEach几乎是最常用的遍历数组的方法了,forEach()被调用时不会直接改变原数组,没有返回值,也无法终止或者跳出
filter顾名思义就是过滤,因此数组的filter用来筛选符合条件的值。filter 不会直接改变原数组,它返回过滤后的新数组。
map和forEach类似,被调用时不修改数组本身,但是会返回一个新数组。
reduce和前面的3个循环不同,它的参数里有一个累加器的概念,并且有没有initialValue执行的次数也会有差别。
some遍历数组找寻符合条件的,找到了返回true,遍历完毕没有找到返回false。
every遍历数组检查符合条件的,有不符合立即返回false,遍历完毕全符合才返回true。

对象的遍历方式

  • for……in:专门用于遍历对象的可枚举属性,包括 prototype 原型链上的属性,因此性能会比较差
  • for……of,遍历的是值
  • Object.keys(),返回一个数组,包含所有的键
  • Object.getOwnpropertyNames():也会返回一个数组,包含所有元素,不管是否可枚举
  • Reflect.ownKeys()返回一个数组,包含对象自定义的属性,不管属性名是 Symbol 还是字符串,也不管是否可枚举

28.如何解决跨域问题

跨域主要是因为同源策略引起的,这是浏览器的一种安全机制,即协议,端口,域名都相同

解决跨域的方法

  • **JSONP:**通过script标签的src属性实现,因此JSONP方案只支持get请求,并且兼容性好,几乎所有浏览器都支持
  • **CORS:**通过服务器设置一系列响应头来实现跨域,而客户端不需要做什么事
  • **代理转发:**在前端服务和后端接口服务之间架设一个中间代理服务,它的地址保持和前端服务一致,
    • 代理服务和前端服务之间由于协议域名端口三者统一不存在跨域问题,可以直接发送请求
    • 代理服务和后端服务之间由于并不经过浏览器没有同源策略的限制,可以直接发送请求
  • **Nginx反向代理:**反向代理和代理很像,都是在客户端与服务端中间架设一个中间代理服务器,不同点在于代理是代表客户端向服务端发送请求,反向代理是代表服务端接收客户端发送的请求

29.XML和JSON的区别

XML是可扩展标记语言,是一种用于标记电子文件使其具有结构性的标记语言

json:是一种轻量级的数据交换格式

都是一种数据格式交换格式

二者的区别

  • XML是重量级的,json是轻量级的
  • xml在传输过程中比较占宽带,JSON占宽度少,易于压缩
  • xml和JSON都是在项目交互下,xml多用于配置文件,JSON用于数据交换
  • JSON可用jackson,gson等方式解析,xml可以dom,sax,demo4j等方式解析
  • JSON支持数组,XML不支持数组

30.谈谈你对webpack的看法

webpack是一种模块打包工具,可以使用webpack管理模块,并分析模块之间的依赖关系,最终编译输出模块为html,js,css以及各种静态文件,让开发过程更加高效,对不不同类型的资源,webpack有不同的模块加载器loader

webpack的基本功能

代码转换,代码语法检测,代码分割,检测代码更新,自动编译,刷新页面,自动发布,文件压缩,模块合并

31.webpack的打包原理

webpack打包原理是根据文件间的依赖关系对其进行静态分析,将这些模块按指定规则生成静态文件,当webpack处理程序时,它会递归的构建一个依赖关系图,其中包含应用程序需要的每个模块,将所有的模块打包成bundle

32.如何优化webpack打包速度

webpack的打包速度影响着我们的开发体验和及时构建,所有我们需要提高webpack的打包速度

方法: ll

  • 减少文件的搜索范围
    在实际开发中,我们会在项目中引入大量的第三方库,以及我们也会进行模块的引用,这样,会导致webpack在寻找文件的过程中占用大量的时间,而我们就需要减少文件的搜索范围,方法:有配置resolve.models,在这里配置模块库的位置(node_models)从而减少搜索范围,这是webpack2以上版本的写法,设置text&include&exclude,webpack的loaders允许每个子项有一下属性test(必须满足的条件),include(导入的文件将有加载程序转换的路径或文件数组),exclude(不能满足的条件)
  • 增强代码压缩工具
    webpack提供的UglifyJS插件,采用单线程压缩,速度很慢,可以使用webpack-parallel-uglify-plugin插件,可以并行运行UglifyJS插件,更加充分合理的使用CPU资源,大大减少构建时间
  • 使用Happypack来加速代码的构建
    将原有的 webpack 对 loader 的执行过程,从单一进程的形式扩展多进程模式
  • 设置 babel 的 cacheDirectory 为true,将文件进行缓存
  • 设置noParse
    如果你确定一个模块中,没有其它新的依赖,就可以配置这项, Webpack 将不再扫描这个文件中的依赖,这对于比较大型类库,将能促进性能表现
  • 拷贝静态文件,将指定的文件拷贝到指定的目录下

33.说说webpack中常见的Loader?解决了什么问题?

loader是用于对模块的源代码进行转换,在import或加载模块时预处理文件,webpack只支持对jsJSON的打包,而html,css,png等文件,则无能为力了,这时就需要配置响应的loader对文件的内容进行解析

常见的loader
* style-loader,将css添加到DOM的内联样式标签style里
* css-loader,将允许将css文件通过require的方式引入,并返回css代码
* less-loader,处理less
* sass-loader,处理sass
* postcss-loader,用postcss来处理css
* file-loader,分发文件到output目录并返回相对路径
* url-loader和file-loader类似,但是当文件小于设定的limit是可以返回Data url
* html-minify-loader,压缩html
* babel-loader,用babel来转换ES6文件到ES

34.说说webpack中常见的Plugin?解决了什么问题?

plugin赋予webpack各种灵活的功能,例如打包优化、资源管理、环境变量注入等,它们会运行在 webpack 的不同阶段(钩子 / 生命周期),贯穿了webpack整个编译周期

常见的Plugin

  • HtmlWebpackPlugin,在打包结束后,⾃动生成⼀个 html ⽂文件,并把打包生成的 js 模块引⼊到该 html 中
  • clean-webpack-plugin,删除(清理)构建目录
  • mini-css-extract-plugin,提取 CSS 到一个单独的文件中
  • DefinePlugin,允许在编译时创建配置的全局对象,是一个 webpack 内置的插件,不需要安装
  • copy-webpack-plugin,复制文件或目录到执行区域
    *

35.说说你对promise的了解

promise是es6新增的异步处理函数,主要是为了解决回调函数产生的地狱回调问题,是一种链式回调问题

Promise简单来说就是一个容器,里面保存这未来才会结束的事件的结果

promise是一个对象,从它里面可以获取异步操作的最终状态(成功或失败)

Promise是一个构造函数,对外提供统一的API,自己身上有all,reject ,resolve等方法,原型上有thencatch等方法

Promise是ES6中的新增的异步处理方法,主要是用于解决ES5中使用回调函数产生的地狱回调的问题
Promise有三种状态,pedding准备中,fullfiilled已完成, rejected失败,状态只能有准备中=>已完成 或 准备中=>失败

特点

Promise对象的状态不受外界状态的影响
一旦状态改变,就不会再变,任何时候都可以得到这个结果

promise的状态

promise有三种状态

  • pending 初始状态
  • fulfilled 成功状态
  • rejected 失败状态

Promise接收一个函数作为参数,该函数有两个参数分别是resolverejected
resolve函数的作用:在异步操作成功时将参数传递出去
reject函数的作用:异步操作失败将参数传递出去

promise的方法
then:异步操作执行成功后执行
catch:异步操作失败后执行
all() 参数是个数组,执行多个Promise对象,必须所有的对象状态执行完后才会变成已完成的状态
race()方法 执行多个Promise对象,只要有一个对象状态是已完成,对象的状态就是已完成。

36.async函数是什么,有什么作用

是es6新增的异步的一种解决方案,可以让异步的操作同步执行,async是Generator函数的语法糖,将Generator函数的*替换成了async,将yield替换成了await
async需要配合await使用,

**使用方式:**在函数前面加上async表示这是一个async函数

await命令:

  1. await有等待的意思,需等待后面的Promise 执行结束才会执行下一步。这里强调一下后面的Promise,是因为假如后面直接跟的是一个计时器,那么计时器虽然是异步操作,但是不会等计时器回调函数执行才执行下一步
  2. await后面可以跟Promise,和其他类型的数据
    (1).当跟的是Promise时,值是执行成功的返回值。
    (2).如果是其他数据时,返回值就是数据本身会返回的值,该啥值是啥值。

async函数返回Promise对象

1、.async函数返回值是一个Promise对象,可以使用.then接受一个回调函数,和Promise的所有操作

2、Promise的状态改变的的几种情况
(1)、当await其中一个Promise执行失败,那么接下来的await不会继续执行,async函数返回Promise的状态变为失败,执行.catch方法
(2)、当执行到return ,下面的await不会执行,return的值就是.then回调函数的参数
(3)、当throw new Error 下面的状态不会执行,async函数返回Promise的状态变为失败,执行.catch方法
(4)、当所有的异步操作都能成功时,那么只有所有异步操作执行完后,async函数返回Promise的状态变为成功,返回值是return的值

37.有使用过vue吗?说说你对vue的理解

Vue.js是一个用于创建用户界面的开源JavaScript框架,也是一个创建单页面应用的Web应用框架。vue是使用mvvm架构的框架

vue的核心特征

数据驱动、组件化、指令系统

vue于传统开发的区别:
vue的所有界面事件,都是只去操作数据的;

vue的所有界面变动,都是根据数据自动绑定出来的

vue和react对比:
相同点:

都有组件化的思想

都支持服务器端渲染

都有虚拟dom

都是数据驱动视图

都有支持native的方案:vue的weex,react的react native

都有自己的构建工具:vue的vue-cli ,react的create react app

区别:

数据流向的不同:react从诞生开始就推崇单向数据流,而vue是双向数据流

数据变化的实现原理不同:react使用的是不可变数据,而vue使用的是可变数据

组件化通信的不同:react中我们使用的是回调函数来进行通信的,而vue中子组件向父组件传递消息有两种方式:事件和回调函数

diff算法不同:react主要使用diff队列保存需要更新哪些dom,得到patch树,再统一操作批量更新dom。vue使用双向指针,边对比,边更新dom

38.你对SPA单页面的理解,它的优缺点分别是什么?如何实现SPA应用呢

spa单页面应用是一种网络应用程序或网站的模型,它通过动态重写当前页面来与用户交互,这种方法避免了页面之间切换打断用户体验,在单页应用中,所有必要的代码(HTML、JavaScript和CSS)都通过单个页面的加载而检索,或者根据需要(通常是为响应用户操作)动态装载适当的资源并添加到页面,页面在任何时间点都不会重新加载,也不会将控制转移到其他页面

单页面应用的优缺点

优点:

具有桌面应用的即时性、网站的可移植性和可访问性
用户体验好、快,内容的改变不需要重新加载整个页面
良好的前后端分离,分工更明确
缺点:

不利于搜索引擎的抓取
首次渲染速度相对较慢

实现一个

原理:
1、监听地址栏中hash变化驱动页面的变化
2、用pushstate记录浏览器的历史,驱动界面发送变化
实现
hash模式
核心通过监听url中hash来进行路由跳转
history模式
history模式核心借用h5 history api,api提供了丰富的 router相关属性
history.pushstate浏览器历史记录添加记录
history.replaceState修改浏览器历史记录中的记录
history.popStatehistory发生变化的时候触发

39.SPA首屏加载速度慢的怎么解决?

首屏时间(First Contentful Paint),指的是浏览器从响应用户输入网址地址,到首屏内容渲染完成的时间,此时整个网页不一定要全部渲染完成,但需要展示当前视窗需要的内容
首屏加载可以说是用户体验中最重要的环节

原因

网络延时问题
资源文件体积是否过大
资源是否重复发送请求去加载了
加载脚本的时候,渲染内容堵塞了

解决方案

减小入口文件积
静态资源本地缓存
UI框架按需加载
图片资源的压缩
组件重复打包
开启GZip压缩
使用SSR

40.VUE路由的原理

vue路由的两种模式:hash模式histroy模式

hash路由

在正常路径后跟一个 # 号,匹配 # 后边的路径为前端路由,通过window.onhashchange方法来操控路由改变的时候切换内容

onhashchange 方法的触发时机

  • 通过改变location.href或location.hash的值;
  • 直接更改浏览器地址,在最后面增加或改变#hash;
  • 通过触发点击带锚点的链接;
  • 浏览器前进后退可能导致hash的变化,前提是两个网页地址中的hash值不同。

histroy路由模式

在window.history这个对象中,包含浏览器的历史,而在HTML5中,新增了 history.pushState和history.replaceState,通过这两个API可以改变url地址且不会发送请求,同时还有window.onpopstate事件,实现原理与hash相似,只不过因为没有了 # 号,所以刷新页面还是会向服务器发送请求,而后端没有对应的处理措施的话,会返回404,所以需要后端配合

window.onpopstate事件的触发时机

仅仅调用pushState方法或replaceState方法 ,并不会触发该事件;
只有用户点击浏览器倒退按钮和前进按钮,或者使用JavaScript调用back、forward、go方法时才会触发。
另外,该事件只针对同一个文档,如果浏览历史的切换,导致加载不同的文档,该事件也不会触发。

41.Vue中组件和插件有什么区别?

组件就是把图形、非图形的各种逻辑均抽象为一个统一的概念(组件)来实现开发的模式,在vue一个.vue文件就是一个组件
插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制

区别

  • 编写形式不同
    组件:编写一个组件,可以有很多方式,我们最常见的就是vue单文件的这种格式,每一个.vue文件我们都可以看成是一个组件
    插件:vue插件的实现应该暴露一个 install 方法。这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象
  • 注册形式不同
    vue组件:注册主要分为全局注册与局部注册
    全局注册通过Vue.component方法,第一个参数为组件的名称,第二个参数为传入的配置项
    局部注册只需在用到的地方通过components属性注册一个组件
    vue插件:插件的注册通过Vue.use()的方式进行注册(安装),第一个参数为插件的名字,第二个参数是可选择的配置项
  • 使用场景
    组件 (Component) 是用来构成你的 App 的业务模块,它的目标是 App.vue
    插件 (Plugin) 是用来增强你的技术栈的功能模块,它的目标是 Vue 本身
    简单来说,插件就是指对Vue的功能的增强或补充

42.Vue组件之间的通信方式都有哪些

父子组件之间的通信
兄弟组件之间的通信
祖孙与后代组件之间的通信
非关系组件间之间的通信

通信方式

  • 通过 props 传递
    适用场景:父组件传递数据给子组件
    子组件设置props属性,定义接收父组件传递过来的参数
    父组件在使用子组件标签中通过字面量来传递值
  • 通过 $emit 触发自定义事件
    适用场景:子组件传递数据给父组件
    子组件通过触发自定义事件,emit第二个参数为传递的数值
    父组件绑定监听器获取到子组件传递过来的参数
  • 使用ref
    父组件在使用子组件的时候设置ref
    父组件通过设置子组件ref来获取数据
  • EventBus
    使用场景:兄弟组件传值
    创建一个中央事件总线EventBus
    兄弟组件通过emit触发自定义事件,emit触发自定义事件,emit触发自定义事件,emit 第二个参数为传递的数值
    另一个兄弟组件通过$on监听自定义事件
  • $parent$root
    通过共同祖辈$parent或者$root搭建通信桥连
  • attrslisteners
    适用场景:祖先传递数据给子孙
    设置批量向下传属性$attrs和 listeners包含了父级作用域中不作为prop被识别(且获取)的特性绑定(class和style除外)。可以通过v−bind="listeners 包含了父级作用域中不作为 prop 被识别 (且获取) 的特性绑定 ( class 和 style 除外)。 可以通过 v-bind="listeners包含了父级作用域中不作为prop被识别(且获取)的特性绑定(classstyle除外)。可以通过vbind="attrs" 传⼊内部组件
  • ProvideInject
    在祖先组件定义provide属性,返回传递的值
    在后代组件通过inject接收组件传递过来的值
  • Vuex
    适用场景: 复杂关系的组件数据传递
    Vuex作用相当于一个用来存储共享变量的容器

43.你了解vue的diff算法吗?说说看

diff 算法是一种通过同层的树节点进行比较的高效算法

特点
比较只会在同层级进行, 不会跨层级比较
在diff比较的过程中,循环从两边向中间比较

原理分析

当数据发生改变时,set方法会调用Dep.notify通知所有订阅者Watcher,订阅者就会调用patch给真实的DOM打补丁,更新相应的视图

vue3中的虚拟DOM

事件缓存:将事件缓存,可以理解为变成静态的了
添加静态标记:vue2是全量Diff,vue3是静态标记+非全量Diff
静态提升:创建静态节点时保存,后续直接复用
使用最长递增子序列优化了对比流程:vue2里在updateChildren()函数里对比变更,在vue3里这一块逻辑主要在patchKeyedChildren()函数里

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6yF90177-1679656110617)(C:\\Users\\lenovo\\AppData\\Roaming\\Typora\\typora-user-images\\image-20221207075707351.png)]

44.为什么需要 Virtual Dom

虚拟DOM只是一层对真实DOM的抽象,以JavaScript 对象 (VNode 节点) 作为基础的树,用对象的属性来描述节点,最终可以通过一系列操作使这棵树映射到真实环境上,在Javascript对象中,虚拟DOM 表现为一个 Object对象。并且最少包含标签名 (tag)、属性 (attrs) 和子元素对象 (children) 三个属性,不同框架对这三个属性的名命可能会有差别
创建虚拟DOM就是为了更好将虚拟的节点渲染到页面视图中,所以虚拟DOM对象的节点与真实DOM的属性一一照应

为什么需要

DOM是很慢的,其元素非常庞大,页面的性能问题,大部分都是由DOM操作引起的
真实的DOM节点,哪怕一个最简单的div也包含着很多属性,所以,操作DOM的代价仍旧是昂贵的,频繁操作还是会出现页面卡顿,影响用户的体验

45.Vue3.0的设计目标是什么?做了哪些优化

设计目标

  • 更小
    Vue3移除一些不常用的 API
  • 更快
    diff算法优化
    静态提升
    事件监听缓存
    SSR优化
  • TypeScript支持
  • API设计一致性
  • 提高自身可维护性
  • 开放更多底层功能
    vue3在兼顾vue2的options API的同时还推出了composition API,大大增加了代码的逻辑组织和代码复用能力

优化

源码
* 源码管理
vue3整个源码是通过 monorepo的方式维护的,根据功能将不同的模块拆分到packages目录下面不同的子目录中
* TypeScript
Vue3是基于typeScript编写的,提供了更好的类型检查,能支持复杂的类型推导
性能
* 体积优化
* 编译优化
* 数据劫持优化
语法 API
* 优化逻辑组织
* 优化逻辑复用

46.Vue3.0 所采用的 Composition Api 与 Vue2.x 使用的 Options Api 有什么不同?

在使用vue2进行开发的时候会遇到,代码的可读性随着组件的变大而便差,每一种代码复用的方式,都存在缺点,TypeScript支持有限,而这些问题vue3却可以迎刃而解

options API

选项API,即以.vue为后缀的文件,通过定义methodscomputedwatchdata等属性和方法,共同处理页面的逻辑,这种选项式的写法,如data中定义状态,methods中定义方法,然而这种情况随着组件复杂,导致对应的属性的列表也会增长,这可能会导致组件难以阅读

Compositions Api

在vue3中Compositions Api中,组件根据逻辑功能来组织的,一个功能所定义的所有Api会放在一起,更加高内聚,低耦合

对比

从逻辑组织和逻辑复用两个方面比较Compositions Api和options Api

逻辑组织
在选项式Api写法中随着组件的内容变得复杂,状态和方法的分离,使得理解和维护组件变得异常困难,并且选项分离也掩盖了组件的内层逻辑

在组合式Api的写法中属性和方法可以写在一起,在进行修改的时候可以非常的明了

逻辑复用
在选项式Api中进行逻辑复用的时候会出现命名冲突和数据来源不清晰的问题

总结

  • 在逻辑组织和逻辑复用方面,组合式Api优于选项式Api
  • 因为组合式Api几乎都是函数,会有更好的逻辑判断
  • 组合式Api对tree-shaking友好,代码也更容易压缩
  • 组合式Api中见不到this的使用,减少了this指向不明的问题
  • 如果是小型组件,可以继续使用选项式Api,也是十分友好

47.说一下Vue数据响应式的原理

vue2的响应式原理和vue3有所区别

vue2响应式原理

vue2中的响应式原理核心是通过obj.defindeProperty中的访问器属性中的getset方法,当访问data中的属性的时候自动调用get方法,当修改data中的属性的时候调用set方法,检测到变化会通知观察者Wacher,观察者Wacher自动触发render,生成新的Dom树,vue判断新旧dom树的区别,最后局部修改到真实DOM树上
vue2中的响应式原理就是vue采用数据劫持结合发布者订阅模式,通过obj,defineProperty()来劫持各个属性的setter,getter,在数据发生变化时发布消息给订阅者,触发响应的监听回调

vue3中的响应式原理

vue3改用proxy代替了obj.defineProerty。因为Proxy可以直接监听对象和数据的变化,并且有多达13中拦截方法,并且作为新标准将受到浏览器厂商中的持续的性能优化,
深层观测
Proxy只会代理对象的第一层,vue3通过判断Reflect.get的返回值是否为object,如果是则在通过reative方法做代理,这样就实现了深层观测
避免多次触发get/set
通过判断key是否为当前被代理对象target自身属性,也可以判断旧值与新值是否相等,只有满足以上两个条件之一时,才有可能执行tergger

48.说说对 React 的理解?有哪些特性?

react

react,用于构建用户界面的JavaScript库,只提供了UI层面的解决方案,遵循组件设计模型,声明式编程范式和函数编程概念,以使前端应用更加高效,使用虚拟dom来有效的操作dom,遵循从高阶组件到低阶组件的单向数据流帮助我们将界面成了各个独立的小块,每一个块就是组件,这些组件之间可以组合、嵌套,构成整体页面react类组件使用一个名为 render() 的方法或者函数组件return,接收输入的数据并返回需要展示的内容

特性

JSX 语法
单向数据绑定
虚拟 DOM
声明式编程
Component

优势

高效灵活
声明式的设计,简单使用
组件式开发,提高代码复用率
单向响应的数据流会比双向绑定的更安全,速度更快

49.说说 Real DOM 和 Virtual DOM 的区别?优缺点?

区别

虚拟 DOM 不会进行排版与重绘操作,而真实 DOM 会频繁重排与重绘
虚拟 DOM 的总损耗是“虚拟 DOM 增删改+真实 DOM 差异增删改+排版与重绘”,真实 DOM 的总损耗是“真实 DOM 完全增删改+排版与重绘”

优缺点

真实DOM的优点
易用
缺点
效率低,解析速度慢,内存占用量过高
性能差:频繁操作真实 DOM,易于导致重绘与回流

虚拟DOM的优点
简单方便:如果使用手动操作真实 DOM 来完成页面,繁琐又容易出错,在大规模应用下维护起来也很困难
性能方面:使用 VirtualDOM,能够有效避免真实DOM树频繁更新,减少多次引起重绘与回流,提高性能
跨平台:React 借助虚拟 DOM,带来了跨平台的能力,一套代码多端运行
缺点
在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化
首次渲染大量 DOM 时,由于多了一层虚拟 DOM 的计算,速度比正常稍慢

50.说说 React 生命周期有哪些不同阶段?每个阶段对应的方法是?

阶段

react生命周期分为旧生命周期新生命周期他们的阶段都分为:
挂载阶段
更新阶段
卸载阶段

旧生命周期

挂载阶段

constructor:实例过程中自动调用的方法

componentWillMount:挂载前

render:渲染

componentDidMount:挂载完成

更新阶段

componentWilReceiveProps:当子组件接收父组件传过来的props,会执行这个函数

shouIdComponentUpdate:当更新state值的时候会执行这个函数

componentWillUpdate:执行render前的函数,里面有两个参数是将要更新的state和props的值

render:渲染

componentDidUpdate:更新完毕

卸载阶段

componentWillUnmount:卸载阶段

新生命周期

挂载阶段

coustructor:实例过程中自动调用的方法

getDerivedStateFromProps:组件创建和更新阶段,不论是props变化还是state变化,也会调用,在每次render方法前调用,第一个参数为即将更新的props,第二个参数为上一个状态的state,可以比较props 和 state来加一些限制条件,防止无用的state更新,该方法需要返回一个新的对象作为新的state或者返回null表示state状态不需要更新

render:渲染

componentDidMount:挂载完成

更新阶段

getDerivedStateFromProps:同上

shouIdcomponentUpdate:到新的props或者state时都会调用,通过返回true或者false告知组件更新与否,一般情况,不建议在该周期方法中进行深层比较,会影响效率
,同时也不能调用setState,否则会导致无限循环调用更新

render:渲染

getSnapshotBeforeUpdate:执行之时DOM元素还没有被更新方法返回的一个Snapshot值,作为componentDidUpdate第三个参数传入,此方法的目的在于获取组件更新前的一些信息

componentDidUpdate:更新完成之后

卸载阶段
componentWillUnMount:卸载

新旧生命周期的区别

在新的生命周期中,react弃用了componentWillMount、componentWillReceiveProps、componentWillUpdate这三个钩子,取而代之的是getDerivedStateFromProps,其实就是把那三个钩子的含义融入到了这一个钩子中,写法如下:
另外还新增了一个钩子,getSnapshotBeforeUpdate,这里可获取到即将要更新的props和state
其他和原来的生命周期一致。

51.说说 React中的setState执行机制

一个组件的显示形态可以由数据状态和外部参数所决定,而数据状态就是state
当需要修改里面的值的状态需要通过调用setState来改变,从而达到更新组件内部数据的作用
setState的第一个参数是一个对象,第二个参数是一个回调函数,可以实时获取更新后的数据
在组件更新中setState有时是同步的有时是异步的
在组件生命周期和React合成事件中,setState是异步的
在setTimeout或者原生DOM事件中,setSate是同步的

52.说说对React中类组件和函数组件的理解?有什么区别?

类组件

通过Es6中的类的编写形式进行编写,该类必须继承React.Component,如果想要访问父组件传递过来的参数,可以通过this.props来访问,在类组件中必须实现render 方法,

函数组件

通过函数的形式编写组件,是React组件最简单的编写形式,函数的第一个参数用于接收父组件传递过来的参数

区别

  • class组件是有状态的组件,可以定义state状态,函数组件无状态
  • class组件有生命周期的,函数组件无生命周期
  • class组件是由this对象,函数组件没有this对象
  • 组件调用: class组件实例化后调用render方法调用,函数组件直接调用的。
  • class组件内部的话,render方法return返回渲染jsx模板,函数组件直接返回即可
  • ref获取子组件的对象,class组件可以直接获取到的,函数组件无法直接获取到。
  • 绑定bind改变this指向,只适用于class组件

53.说说对React Hooks的理解?解决了什么问题?

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性

解决的问题

  • 难以重用和共享组件中的与状态相关的逻辑
  • 逻辑复杂的组件难以开发与维护,当我们的组件需要处理多个互不相关的 local state 时,每个生命周期函数中可能会包含着各种互不相关的逻辑在里面
  • 类组件中的this增加学习成本,类组件在基于现有工具的优化上存在些许问题
  • 由于业务变动,函数组件不得不改为类组件等等

常用的Hook
1.useSate

useState()用于为函数组件引入状态。在useState()中,数组第一项为一个变量,指向状态的当前值。类似this.state,第二项是一个函数,用来更新状态,类似setState

2.useEffect

useEffect()接受两个参数,第一个参数是你要进行的异步操作,第二个参数是一个数组,用来给出Effect的依赖项。只要这个数组发生变化,useEffect()就会执行

3.useRef

相当于class组件中的createRef的作用,ref.current获取绑定的对象

4.useContext

接受context状态树传递的数据内容

5.useReducer

接受reducer函数和状态的初始值作为参数,返回一个数组,其中第一项为当前的状态值,第二项为发送action的dispatch函数

6.userMemo useCallback

useMemo 和 useCallback接收的参数都是一样,第一个参数为回调,第二个参数为要依赖的数据
共同作用:仅仅依赖数据发生变化, 才会调用,也就是起到缓存的作用。useCallback缓存函数,useMemo 缓存返回值。

54.说说你对Redux的理解?其工作原理?

在react中每个组件的state是由自身进行管理,包括组件定义自身的state、组件之间的通信通过props传递、使用Context实现数据共享等,如果让每个组件都存储自身相关的状态,理论上来讲不会影响应用的运行,但在开发及后期我们将比较难以维护,所以我们可以把数据进行集中式的管理,redux就是一个实现上述集中管理的容器的工具,redux并不是只应用在react中,还与其他界面库一起使用,如Vue

Redux三大原则:

  • state数据必须是单一数据源
  • redux中的state数据必须是只读的,只能通过dispatch调用action修改
  • Reducer必须使用纯函数来执行修改

redux执行原理

react组件需要获取或者修改页面的数据,通过dispatch方法调用action进入Reducer函数修改state数据内容,state更新后,通知组件更新页面即可

55.说说 React 性能优化的手段有哪些

React凭借虚拟DOM和diff算法拥有高效的性能,但是某些情况下,性能明显可以进一步提高,其中类组件通过调用setState方法, 就会导致render,父组件一旦发生render渲染,子组件一定也会执行render渲染

性能优化的手段

shouldComponentUpdate,useMoune,useCallback等方法
避免使用内联函数
使用 React Fragments 避免额外标记
使用 Immutable
懒加载组件
事件绑定方式
服务端渲染

56.vue、react、angular 区别

vue是一套用于构建用户界面的渐进式框架
react是一个用于构建用户界面的 JavaScript 库
angular是一个应用设计框架与开发平台,用于创建高效、复杂、精致的单页面应用

相同点

  • 都是基于javascript/typescript的前端开发库,为前端开发提供高效、复用性高的开发方式
  • 都有组件和模板的开发思想
  • 各自的组件都有生命周期,不用的组件可以卸载,不占用资源

区别

  1. 应用类型不同

    Angular支持开发native应用程序SPA单页应用程序混合应用程序web应用程序React支持开发SPA移动应用程序Vue支持开发高级SPA开始支持native应用程序

  2. 模型不同

    angular基于MVC架构;react和vue是基于MVVM

  3. 数据流流向不同

    Angular使用的是双向数据绑定,React用的是单数据流的,而Vue则支持两者。

  4. 对微应用和微服务的支持不同

    Angular使用的是TypeScript,因此它更适合于SPA单页面Web应用,而非微服务。相反,React和Vue的灵活性更适合微应用和微服务的开发。

  5. 对原生应用的支持不同

    React Native为iOS和Android开发原生应用;Angular的NativeScript已被原生应用所采用,特别是Ionic框架已经被广泛地运用在制作混合应用等方面;Vue的Weex平台正在开发之中,尚无下一步使之成为全面跨平台的计划。

  6. 框架和库

    Angular 是一个框架而不是一个库,因为它提供了关于如何构建应用程序的强有力的约束,并且还提供了更多开箱即用的功能。Vue是前端框架,React是js库

  7. 组件之间传值方式不同

    Angular 中直接的父子组件,父组件可以直接访问子组件的 public 属性和方法,也可以借助于@Input 和 @Output 进行通讯。没有直接关系的,借助于 Service 单例进行通讯;React 组件之间通过通过prop或者state来通信,不同组件之间还有Rex状态管理功能;Vue组件之间通信通过props ,以及Vuex状态管理来传值

57.说说你对 TypeScript 的理解?与 JavaScript 的区别

TypeScript

它是 JavaScript 的超集,包含了 JavaScript 的所有元素,可以载入 JavaScript 代码运行,并扩展了 JavaScript 的语法。

区别

1、TypeScript中的数据要求带有明确的类型,JavaScript不要求。
2、TypeScript引入了JavaScript中没有的“类”概念。
3、TypeScript通过类型注解提供编译时的静态类型检查。
4、TypeScript需要编译为js语法

58.说说你对 TypeScript 中泛型的理解?应用场景?

泛型就是给可变不定的类型定义类型,泛型函数其实就是在函数后面加上<T>,T可以用任何类型都代替,当泛型去继承一个类的时候,并不是代表这个类具有了泛型继承的这个类的所有属性和方法。而是在用这个类的时候,接收的参数,只能是当前类及其子类

应用场景

创建泛型函数、泛型接口,泛型类

59.说说你对微信小程序的理解?优缺点?

小程序是一种不需要下载安装即可使用的应用,它实现了应用“触手可及”的梦想,用户扫一扫或者搜一下即可打开应用,也体现了“用完即走”的理念,用户不用关心是否安装太多应用的问题。应用将无处不在,随时可用,但又无需安装卸载

优点

  • 随搜随用,用完即走:使得小程序可以代替许多APP,或是做APP的整体嫁接,或是作为阉割版功能的承载体
  • 流量大,易接受:小程序借助自身平台更加容易引入更多的流量
  • 安全
  • 开发门槛低
  • 降低兼容性限制

缺点

  • 用户留存:及相关数据显示,小程序的平均次日留存在13%左右,但是双周留存骤降到仅有1%
  • 体积限制:微信小程序只有2M的大小,这样导致无法开发大型一些的小程序
  • 受控微信:比起APP,尤其是安卓版的高自由度,小程序要面对很多来自微信的限制,从功能接口,甚至到类别内容,都要接受微信的管控

60.说说你对发布订阅、观察者模式的理解?区别?

观察者模式

当对象之间存在一对多的依赖关系时,其中一个对象的状态发生改变,所有依赖它的对象都会收到通知,这就是观察者模式。
在观察者模式中,只有两种主体:目标对象 (Object) 和 观察者 (Observer)。
观察者 Observer需要实现update方法,供目标对象调用。update方法中可以执行自定义的业务逻辑 ———— 弟子们需要定义接收任务通知后的方法,例如去抢任务或任务不适合,继续等待下一个任务

发布订阅

基于一个事件(主题)通道,希望接收通知的对象 Subscriber 通过自定义事件订阅主题,被激活事件的对象 Publisher 通过发布主题事件的方式通知各个订阅该主题的 Subscriber 对象。

区别

  • 在观察者模式中,观察者是知道Subject的,Subject一直保持对观察者进行记录。然而,在发布订阅模式中,发布者和订阅者不知道对方的存在。它们只有通过消息代理进行通信。
  • 在发布订阅模式中,组件是松散耦合的,正好和观察者模式相反。
  • 观察者模式大多数时候是同步的,比如当事件触发,Subject就会去调用观察者的方法。而发布-订阅模式大多数时候是异步的(使用消息队列)

61.项目做过哪些性能优化

1、加载优化

1.减少http请求
2.缓存资源
3.无阻塞
4.首屏加载
5.预加载
6.压缩代码
7.压缩图片
8.减少cookie
9.避免重定向
10.异步加载第三方资源

2、执行优化

1.css样式放头部
2.script放尾部且异步
3.避免img、iframe等src空值问题
4.尽量使用雪碧图
5.图片转BASE64编码

3、渲染优化

1.设置viewport
2.减少dom节点
3.优化动画
4.优化gup加速
5.优化高频事件

4、样式优化

1.避免在html里书写样式
2.避免css表达式
3.移除css空规则
4.正确使用display
5.不滥用float等

5、脚本优化

1.尽量不要引起重绘和回流
2.缓存dom选择和计算
3.缓存集合length的值
4.尽量使用事件委托
5.尽量使用id选择器
6.touch事件优化

62.描述浏览器的渲染过程,DOM树和渲染树的区别

浏览器渲染过程

处理 HTML 并构建 DOM 树。
处理 CSS构建 CSSOM 树。
将 DOM 与 CSSOM 合并成一个渲染树。
根据渲染树来布局,计算每个节点的位置。
调用 GPU 绘制,合成图层,显示在屏幕上

DOM树和渲染树

DOM树与HTML标签一一对应,包括head和隐藏元素
渲染树不包括head和隐藏元素,大段文本的每一个行都是独立节点,每一个节点都有对应的css属性

63.你认为什么样的前端代码是好的

高复用低耦合,这样文件小,好维护,而且好扩展。
具有可用性、健壮性、可靠性、宽容性等特点
遵循设计模式的六大原则
设计模式六大原则:1、单一原则;2、里氏替换原则;3、依赖倒置原则;4、接口隔离原则;5、迪米特原则;6、开闭原则。

64.从浏览器地址栏输入url到显示页面的步骤

1、浏览器先查看浏览器缓存-系统缓存-路由器缓存,如果缓存中有,会直接在屏幕中显示页面内容。若没有,则往下执行。

2、在发送http请求前,需要域名解析(DNS解析),解析获取相应的IP地址。

3、浏览器向服务器发起tcp连接,与服务器建立tcp三次握手。

4、握手成功后,浏览器向服务器发送http请求,请求数据包。

5、服务器处理收到的请求,将数据返回至浏览器

6、浏览器收到HTTP响应。根据情况选择关闭TCP连接或者保留重用

7、如果得到的资源(静态)可以缓存,进行缓存

8、读取页面内容,浏览器渲染,解析html源码

9、生成Dom树、解析css样式、js交互

10、ajax(Asynchronous Javascript And XML)异步处理 可以在不重新加载整个网页的情况下,对网页的某部分进行更新

65.http 请求报文响应报文的格式

HTTP 请求报文

由请求行、请求头部、空行 和 请求包体 4 个部分组成:
(1)请求行:请求行由方法字段、URL 字段 和HTTP 协议版本字段 3 个部分组成,他们之间使用空格隔开。常用的 HTTP 请求方法有 GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE、CONNECT;
(2)请求头部:请求头部由关键字/值对组成,每行一对,关键字和值用英文冒号“:”分隔。请求头部通知服务器有关于客户端请求的信息;
(3)空行:最后一个请求头之后是一个空行,发送回车符和换行符,通知服务器以下不再有请求头;
(4)请求包体:请求包体不在 GET 方法中使用,而是在POST 方法中使用。POST 方法适用于需要客户填写表单的场合。与请求包体相关的最常使用的是包体类型 Content-Type 和包体长度 Content-Length。

HTTP 响应报文

由状态行、响应头部、空行 和 响应包体 4 个部分组成:
(1)状态行:状态行由 HTTP 协议版本字段、状态码和状态码的描述文本 3 个部分组成,他们之间使用空格隔开;
(2)响应头部:响应头可能包括:Location、Serve、Vary、Connection等;
(3)空行:最后一个响应头部之后是一个空行,发送回车符和换行符,通知服务器以下不再有响应头部;
(4)响应包体:服务器返回给客户端的文本信息

66.Token cookie session 区别

Cookie是浏览器用来保存用户信息的文件
Session是一次会话,会话是指我们访问网站的一个周期。
Token是服务器返回的一个临时签名数据, 可以使用这个签名数据表明用户身份.

区别

  • cookie
    • cookie由服务器生成,保存在客户端浏览器。
    • 容易被劫持,不安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗。
    • cookie可以被用户禁止
    • 容量小, 单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。
  • session
    • session是由应用服务器维持的一个服务器端的存储空间,没有对存储的数据量的限制,可以保存更为复杂的数据类型.
    • session 默认被存在在服务器的一个文件里,但是实际中可以放在文件、数据库、或内存中都可以。
    • 当用户量增多时,会对服务器造成较大压力。
    • Session的实现方式大多数情况用Cookie保存的,但是也可以使用URL地址重写。
    • 比较安全,用户验证这种场合一般会用 session, 比如金融银行类的产品,
  • token
    • 无状态、可扩展
    • 支持移动设备
    • 跨服务器调用
    • 安全

67.CORS跨域的原理

CORS即跨域资源共享是一种机制,是W3C标准,他允许浏览器向胯源服务器,发出XMLHttpRequestFetch请求。并且整个CORS通信过程都是浏览器自动完成的,不需要用户参加,而实现这种跨域资源共享的前提是,浏览器必须支持这种功能,并且浏览器必须同意这种跨域请求

过程

  • 浏览器先根据同源策略对前端页面和后台交换地址做匹配,若同源则直接发生数据请求;若不同源,则发送跨域请求。
  • 当我们发起跨域请求的时候,如果是非简单请求,浏览器会帮我们自动触发预检请求,也就是OPTIONS请求,用于确认目标资源是否支持跨域,如果是简单请求,则不会触发预检请求,直接发送正常请求
  • 服务器收到浏览器跨域请求后,根据自身配置返回响应头,若未配置过任何跨域,则响应头里不包含Access-Control-Allow-origin字段,若配置过域名,则返回Access-Control-Allow-Origin + 对应配置规则里的域名的方式
  • 浏览器根据接收到的响应头里的Access-Control-Allow-origin字段做匹配,若没该字段,说明不允许跨域,从而抛出一个错误;若有该字段,则对字段内容和当前域名做对比,如果同源,则说明可以跨域,浏览器接收该响应;若不同源;则说明该域名不可跨域,浏览器不接收响应,并抛出一个错误

CORS验证机制

CORS的验证机制分为两种模式:简单请求和预检请求
简单请求
简单请求模式下浏览器直接发送请求,并在请求头中携带 Origin。 服务器端接到请求后,会根据自己的跨域规则,通过响应头Access-Control-Allow-Origin 来返回验证结果。

  • 只能使用GET、HEAD、POST方法。使用POST方法向服务器发送数据时,Content-Type 只能使用 application/x-www-form-urlencoded 、multipart/form-data 或 text/plain 编码格式。
  • 请求时不能使用自定义的 HTTP Headers

预检请求
需要预检的请求必须首先使用 OPTIONS 方法发起一个预检请求到服务器,服务器基于预检请求的信息来判断是否接受接下来的实际请求。

为什么简单请求不需要预检

因为简单请求虽然是一种定义,不过它定义是有一定理由的,浏览器可能觉得这类请求预检的安全性没有那么大必要性,不预检带来性能方面的收益更大

复杂请求预检检测什么东西

预检请求的使用,可以避免跨域请求对服务器的用户的数据产生未预期的影响。列如,某个请求只支持header,cc,你发送了一个dd的headers,那么options可以有效拦截,不会发出实体的请求,避免了一些安全问题

68.什么是MVVM

MVVM是Model-View-ViewModel缩写,也就是把MVC中的中得Controller演变成view Model

  • Model是数据模型,定义数据和逻辑,仅关注数据本身,不关注任何行为
  • View视图层,当view Model对model进行更新的时候,会通过数据绑定更新到view
  • viewModel业务逻辑层,View需要什么数据,ViewModel要提供这个数据,View有某些操作,ViewModel就要响应这些操作,所有可以说他是model for view

总结

MVVM模式简化了界面与业务的依赖,解决了数据频繁更新。MVVM 在使用当中,利用双向绑定技术,使得 Model 变化时,ViewModel 会自动更新,而 ViewModel 变化时,View 也会自动变化。

69.说说你对版本管理的理解?常用的版本管理工具有哪些?

版本管理-

版本管理是维护工程蓝图的标准做法,能追踪工程蓝图从诞生一直到定案的过程,此外,版本控制也是一种软件工程技巧,借此能在软件开发的过程中,确保不同人员所编辑的同一程序文件都能得到同步
优点

  • 记录文件所有历史变化,这是版本控制系统的基本能力
  • 随时恢复到任意时间点,历史记录功能使我们不怕改错代码了
  • 支持多功能并行开发,通常版本控制系统都支持分支,保证了并行开发的可行
  • 多人协作并行开发,对于多人协作项目,支持多人协作开发的版本管理将事半功倍

常用的版本管理工具有哪些

本地版本控制系统
集中式版本控制系统
代表工具:SW,CVS
分布式版本控制系统
代表工具:GitHG

70.说说你对Git的理解?

是一个分布式版本控制软件,最初目的是为更好地管理Linux内核开发而设计
项目目录会隐藏一个.git子目录,其作用是用来跟踪管理版本库的
文件状态对应的,不同状态的文件在Git中处于不同的工作区域,主要分成了四部分:
工作区:相当于本地写代码的区域,
暂存区:暂存区是一个文件,保存了下次将提交的文件列表信息,一般在 Git 仓库目录中
本地仓库:提交更新,找到暂存区域的文件,将快照永久性存储到 Git 本地仓库
远程仓库:远程的仓库,如 github

71.说说Git常用的命令有哪些

git clone url:下载一个项目和它的整个代码历史
git init 初始化仓库,默认为 master 分支
git add . 提交全部文件修改到暂存区
git diff 查看当前代码 add后,会 add 哪些内容
git status 查看当前分支状态
git pull <远程仓库名> <远程分支名> 拉取远程仓库的分支与本地当前分支合并
git pull <远程仓库名> <远程分支名>:<本地分支名> 拉取远程仓库的分支与本地某个分支合并
git commit -m “<注释>” 提交代码到本地仓库,并写提交注释
git commit -v 提交时显示所有diff信息
git checkout <分支名> 切换到本地某个分支
git branch -D <分支名> 删除本地某个分支
git branch 查看本地所有分支
git branch -r 查看远程所有分支
git branch -a 查看本地和远程所有分支
git merge <分支名> 合并分支
git push [remote] [branch] 上传本地指定分支到远程仓库
git push [remote] --force 强行推送当前分支到远程仓库,即使有冲突
git checkout [file] 恢复暂存区的指定文件到工作区
git checkout . 恢复暂存区的所有文件到工作区

72.说说 git 发生冲突的场景?如何解决?

一般情况下,出现分支冲突的场景有如下:
多个分支代码合并到一个分支时
多个分支向同一个远端分支推送

具体情况就是,多个分支修改了同一个文件(任何地方)或者多个分支修改了同一个文件的名称
如果两个分支中分别修改了不同文件中的部分,是不会产生冲突,直接合并即可
应用在命令中,就是push、pull、stash、rebase等命令下都有可能产生冲突情况,从本质上来讲,都是merge和patch(应用补丁)时产生冲突

当Git无法自动合并分支时,就必须首先解决冲突,解决冲突后,再提交,合并完成
解决冲突就是把Git合并失败的文件手动编辑为我们希望的内容,再提交快快快,

自己总结

1、ES5、ES6 和 ES2015 有什么区别?

ES2015 特指在2015年发布的新一代JS语言标准,ES6 泛指下一代JS语言标准,包含 ES2015、ES2016、ES2017、ES2018 等。现阶段在绝大部分场景下,ES2015 默认等同 ES6,ES5 泛指上一代语言标准。ES2015 可以理解为 ES5 和 ES6 的时间分界线。

2、 babel 是什么,有什么作用?

  • Babel 是一个 JS 编译器,自带一组 ES6 语法转化器,用于转化 JS 代码。
    这些转化器让开发者提前使用最新的 JS语法 (ES6/ES7),而不用等浏览器全部兼容;
  • Babel 默认只转换新的 JS 句法(syntax),而不转换新的 API;

3、let 有什么用,有了 var 为什么还要用 let?

在 ES6 之前,声明变量只能用 var,var 方式声明变量其实是很不合理的,准确的说:是因为 ES5 里面没有块级作用域是很不合理的。没有块级作用域回来带很多难以理解的问题,比如: for循环var变量泄露、变量覆盖等问题。let 声明的变量拥有自己的块级作用域,且修复了 var 声明变量带来的变量提升问题。

4、举例 ES6 对 String 字符串类型做的常用升级优化

优化部分
ES6 新增了字符串模板,在拼接大段字符串时,用反引号`` 取代以往的字符串相加的形式,
使得字符串拼接看起来更加直观,更加优雅
升级部分
ES6 在String原型上新增了 includes() 方法,用于取代传统的只能用 indexOf() 查找包含字符的方法 (indexOf 返回 -1 表示没查到,
不如 includes 方法返回 false 更明确,语义更清晰), 此外还新增了 startsWith(), endsWith(), padStart(), padEnd(),
repeat() 等方法,可方便的用于查找,补全字符串。

5、举例 ES6 对 Array 数组类型做的常用升级优化

优化部分
数组解构赋值:
ES6 可以直接以 let [a,b,c] = [1,2,3] 形式进行变量赋值,在声明较多变量时,不用再写很多 let(var),且映射关系清晰,
且支持赋默认值。
扩展运算:
ES6 新增的扩展运算符 (...),可以轻松的实现数组和松散序列的相互转化,可以取代 arguments 对象和 apply 方法,
轻松获取未知参数个数情况下的参数集合。(尤其是在ES5中,arguments 并不是一个真正的数组,而是一个类数组的对象,
但是扩展运算符的逆运算却可以返回一个真正的数组)。
扩展运算符还可以轻松方便的实现数组的复制和解构赋值(let a = [2,3,4]; let b = [...a])
升级部分
ES6 在Array原型上新增了 find() 方法,用于取代传统的只能用 indexOf 查找包含数组项目的方法,
且修复了 indexOf 查找不到 NaN 的bug ([NaN].indexOf(NaN) === -1).
此外还新增了 copyWithin(), includes(), fill(), flat() 等方法,可方便的用于字符串的查找,补全、转换等。

6、举例 ES6 对 Number 数字类型做的常用升级优化

优化部分
ES6 在 Number 原型上新增了 isFinite(), isNaN()方法,用来取代传统的全局 isFinite(), isNaN() 方法检测数值是否有限、
是否是NaN。ES5的 isFinite(), isNaN() 方法都会先将非数值类型的参数转化为 Number 类型再做判断,这其实是不合理的,
最造成 isNaN('NaN') === true 的奇怪行为。'NaN'是一个字符串,但是 isNaN 却说这就是 NaN。
而 Number.isFinite() 和 Number.isNaN() 则不会有此类问题 (Number.isNaN('NaN') === false)。(isFinite()同上)
升级部分
ES6 在 Math 对象上新增了 Math.cbrt(),trunc(),hypot() 等较多的科学计数法运算方法,可以更加全面的进行立方根、
求和立方根等科学计算。

7、举例ES6对Function函数类型做的常用升级优化

优化部分
箭头函数(核心)。箭头函数是ES6核心的升级项之一,箭头函数里没有自己的 this , 这改变了以往JS函数中最让人难以理解的this运行机制。
  • 箭头函数内的 this 指向的是函数定义时所在的对象,而不是函数执行时所在的对象。
    ES5函数里的 this 总是指向函数执行时所在的对象,这使得在很多情况下 this 的指向变得很难理解,尤其是非严格模式情况下,this 有时候会指向全局对象,这甚至也可以归结为语言层面的 bug 之一。ES6的箭头函数优化了这一点,它的内部没有自己的this,这也就导致了 this 总是指向上一层的 this,如果上一层还是箭头函数,则继续向上指,直到指向到有自己 this 的函数为止,并作为自己的 this。

  • 箭头函数不能用作构造函数,因为它没有自己的this,无法实例化;

  • 因为箭头函数没有自己的 this ,所以箭头函数内也不存在 arguments 对象(可用扩展运算符代替);

  • 函数默认赋值。ES6之前,函数的形参是无法给默认值得,只能在函数内部通过变通方法实现。

    ES6以更简洁更明确的方式进行函数默认赋值

升级部分
ES6新增了双冒号运算符,用来取代以往的 bind,call 和 apply。(浏览器暂不支持,Babel已经支持转码)
foo::bar;
// 等同于
bar.bind(foo);foo::bar(...arguments);
// 等同于
bar.apply(foo, arguments);

ndexOf() 查找包含字符的方法 (indexOf 返回 -1 表示没查到,
不如 includes 方法返回 false 更明确,语义更清晰), 此外还新增了 startsWith(), endsWith(), padStart(), padEnd(),
repeat() 等方法,可方便的用于查找,补全字符串。


### 5、举例 ES6 对 Array 数组类型做的常用升级优化##### 优化部分

数组解构赋值:
ES6 可以直接以 let [a,b,c] = [1,2,3] 形式进行变量赋值,在声明较多变量时,不用再写很多 let(var),且映射关系清晰,
且支持赋默认值。
扩展运算:
ES6 新增的扩展运算符 (…),可以轻松的实现数组和松散序列的相互转化,可以取代 arguments 对象和 apply 方法,
轻松获取未知参数个数情况下的参数集合。(尤其是在ES5中,arguments 并不是一个真正的数组,而是一个类数组的对象,
但是扩展运算符的逆运算却可以返回一个真正的数组)。
扩展运算符还可以轻松方便的实现数组的复制和解构赋值(let a = [2,3,4]; let b = […a])


##### 升级部分

ES6 在Array原型上新增了 find() 方法,用于取代传统的只能用 indexOf 查找包含数组项目的方法,
且修复了 indexOf 查找不到 NaN 的bug ([NaN].indexOf(NaN) === -1).
此外还新增了 copyWithin(), includes(), fill(), flat() 等方法,可方便的用于字符串的查找,补全、转换等。


### 6、举例 ES6 对 Number 数字类型做的常用升级优化##### 优化部分

ES6 在 Number 原型上新增了 isFinite(), isNaN()方法,用来取代传统的全局 isFinite(), isNaN() 方法检测数值是否有限、
是否是NaN。ES5的 isFinite(), isNaN() 方法都会先将非数值类型的参数转化为 Number 类型再做判断,这其实是不合理的,
最造成 isNaN(‘NaN’) === true 的奇怪行为。'NaN’是一个字符串,但是 isNaN 却说这就是 NaN。
而 Number.isFinite() 和 Number.isNaN() 则不会有此类问题 (Number.isNaN(‘NaN’) === false)。(isFinite()同上)


##### 升级部分

ES6 在 Math 对象上新增了 Math.cbrt(),trunc(),hypot() 等较多的科学计数法运算方法,可以更加全面的进行立方根、
求和立方根等科学计算。


### 7、举例ES6对Function函数类型做的常用升级优化##### 优化部分

箭头函数(核心)。箭头函数是ES6核心的升级项之一,箭头函数里没有自己的 this , 这改变了以往JS函数中最让人难以理解的this运行机制。


- 箭头函数内的 this 指向的是函数定义时所在的对象,而不是函数执行时所在的对象。ES5函数里的 this 总是指向函数执行时所在的对象,这使得在很多情况下 this 的指向变得很难理解,尤其是非严格模式情况下,this 有时候会指向全局对象,这甚至也可以归结为语言层面的 bug 之一。ES6的箭头函数优化了这一点,它的内部没有自己的this,这也就导致了 this 总是指向上一层的 this,如果上一层还是箭头函数,则继续向上指,直到指向到有自己 this 的函数为止,并作为自己的 this。- 箭头函数不能用作构造函数,因为它没有自己的this,无法实例化;- 因为箭头函数没有自己的 this ,所以箭头函数内也不存在 arguments 对象(可用扩展运算符代替);- 函数默认赋值。ES6之前,函数的形参是无法给默认值得,只能在函数内部通过变通方法实现。ES6以更简洁更明确的方式进行函数默认赋值##### 升级部分

ES6新增了双冒号运算符,用来取代以往的 bind,call 和 apply。(浏览器暂不支持,Babel已经支持转码)


```js
foo::bar;
// 等同于
bar.bind(foo);foo::bar(...arguments);
// 等同于
bar.apply(foo, arguments);