常见面试题梳理

428 阅读29分钟

css

布局

1. 左右固定宽度100px中间自适应

  • position定位
  • flexbox
  • calc
  • float

2. 左图右文,右边自适应宽度

  • position定位
  • flexbox
    • 左边一个固定宽度 右边flex-grow写了1 但是右边的内容宽度超出了剩余空间,这个时候怎么处理才能让内容宽度不超出呢? Width:0
  • calc
  • float

3. footer固定在底部方法

  • position定位
  • flexbox
  • calc

4. ul内部除最后一个li以外设置右边框效果(引出nth-of-type与nth-child区别)

  • last-child
  • nth-last-of-type
  • not li:not(:last-child)

5. css超出省略怎么写,超出2行省略呢

单行

{
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

多行

{
  display: -webkit-box;
  word-break: break-all;
  overflow: hidden;
  text-overflow: ellipsis;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3
}

flex布局

1. 介绍CSS3的flexbox及常用属性

该布局模型的目的是提供一种更加高效的方式来对容器中的条目进行布局、对齐和分配空间。在传统的布局方式中,block 布局是把块在垂直方向从上到下依次排列的;而 inline 布局则是在水平方向来排列。弹性盒布局并没有这样内在的方向限制,可以由开发人员自由操作。详见 juejin.cn/post/695178…

2. flex: 1是什么的缩写

flex属性是flex-grow flex-shrink flex-basics的简写

  • flex-grow (放大)索取父容器的剩余空间,默认值是 0
  • flex-shrink (缩小)定义了一个子容器的压缩比例。默认值是 1
  • flex-basis 或者 width,那么在分配空间之间,他们会先跟父容器预约这么多的空间,然后剩下的才是归入的剩余空间,然后父容器再把剩余空间分配给设置了 flex-grow 的容器。如果同时设置 flex-basis 和 width,那么 width 属性会被覆盖,也就是说 flex-basis 的优先级比 width 高。有一点需要注意,如果 flex-basis 和 width 其中有一个是 auto,那么另外一个非auto 的属性优先级会更高。 flex: 1完整写法:
   flex-grow: 1;
   flex-shrink: 1;
   flex-basis: 0%;

flex: none完整写法:

   flex-grow: 0;
   flex-shrink: 0;
   flex-basis: auto;

flex: 非负数字:

   flex-grow: 数字;
   flex-shrink: 1;
   flex-basis: 0%

3. flex布局,如何把八个元素分两行摆放

<style>
    .parent {
        width: 100%;
        height: 150px;
        display: flex;
        flex-flow: row wrap;
        align-content: flex-start;
    }
    .child {
        box-sizing: border-box;
        background-color: white;
        flex: 0 0 25%;
        height: 50px;
        border: 1px solid red;
    }
</style>
<div class="parent">
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
</div>

基本概念

1. nth-of-type与nth-child区别

  • nth-child:选择父元素的第n个元素
  • nth-of-type:选择父元素第n个指定元素

2. 行内元素与块级元素的区别

  • 行内元素会在一条直线上排列(默认宽度只与内容 有关),都是同一行的,水平方向排列。
  • 块级元素各占据一行(默认宽度是它本身父容器的 100% (和父元素的宽度一致),与内容无关), 垂直方向排列。块级元素从新行开始,结束接着一个断行。
    • 块级元素可以包含行内元素和块级元素。行内元素不能包含块级元素,只能包含文本或者其它行内元素。
  • 行内元素与块级元素属性的不同,主要是盒模型属 性上:行内元素设置width无效,height无效(可以 设置line-height) , margin上下无效,padding上下 无效

3. css伪类和伪元素区别

1)伪类(pseudo-classes)

  • 其核心就是用来选择DOM树之外的信息,不能够被 普通选择器选择的文档之外的元素,用来添加一些 选择器的特殊效果。
  • 比如:hover ractive :visited :link :visited :first- child :focus :lang等
  • 由于状态的变化是非静态的,所以元素达到一个特 定状态时,它可能得到一个伪类的样式;当状态改 变时,它又会失去这个样式。
  • 由此可以看出,它的功能和class有些类似,但它是 基于文档之外的抽象,所以叫伪类。 2)伪元素(Pseudo-elements)
  • DOM树没有定义的虚拟元素
  • 核心就是需要创建通常不存在于文档中的元素,
  • 比如::before ::after它选择的是元素指定内容,表 示选择元素内容的之前内容或之后内容。
  • 伪元素控制的内容和元素是没有差别的,但是它本 身只是基于元素的抽象,并不存在于文档中,所以 称为伪元素。用于将特殊的效果添加到某些选择器
  • 区别:
  1. 表示方法
  • CSS2中伪类、伪元素都是以单冒号:表示,
  • CSS2.1后规定伪类用单冒号表示,伪元素用 双冒号::表示,
    • 浏览器同样接受CSS2时代已经存在的伪元 素(:before, :after, :first\ufffeline, :first- letter等)的单冒号写法。
  • CSS2之后所有新增的伪元素 (如::selection),应该采用双冒号的写法。
  • CSS3中,伪类与伪元素在语法上也有所区 别,伪元素修改为以::开头。浏览器对以:开 头的伪元素也继续支持,但建议规范书写为:: 开头
  1. 定义不同
  • 伪类即假的类,可以添加类来达到效果
  • 伪元素即假元素,需要通过添加元素才能达 到效果
  1. 总结:
  • 伪类和伪元素都是用来表示文档树以外的“元 素
  • 伪类和伪元素分别用单冒号:和双冒号::来表
  • 伪类和伪元素的区别,关键点在于如果没有 伪元素(或伪类),
  • 是否需要添加元素才能达到效果,如果是则 是伪元素,反之则是伪类。
  1. 相同之处:
  • 伪类和伪元素都不出现在源文件和DOM树中。也就 是说在html源文件中是看不到伪类和伪元素的。不 同之处:
  • 伪类其实就是基于普通DOM元素而产生的不同状 态,他是DOM元素的某一特征。
    • 伪元素能够创建在DOM树中不存在的抽象对象,而 且这些抽象对象是能够访问到的。

4. link和@import区别

  1. link是XHTML标签,除了加载CSS外,还可 以定义RSS等其他事务;@import属于CSS范畴, 只能加载CSS。
  2. link引用CSS时,在页面载入时同时加载; @import需要页面网页完全载入以后加载。所以会 出现一开始没有css样式,闪烁一下出现样式后的 页面(网速慢的情况下)
  3. link是XHTML标签,无兼容问题;©import 是在CSS2.1提出的,低版本的浏览器不支持。
  4. link支持使用Javascript控制DOM去改变样 式;而@10^011不支持。

5. position有哪些属性?position: sticky

属性
  • static 默认值
  • relative 相对定位不会脱离文档流
  • absolute 相对不是static的最近一级父元素来进行定位,脱离文档流
  • fixed 相对于浏览器的窗口定位,脱离文档流
  • inherit 继承父元素的position属性
  • initial 用于设置css属性为它的默认值
  • sticky 设置了sticky的元素,在屏幕范围时该元素的位置不受定位影响(top、left无效),当该元素的位置要移出偏移范围时,定位变成fixed,根据设置的left、top属性成固定位置,特性如下:
    1. 不脱离文档流
    2. 当元素在容器中被滚动超过指定的偏移量时,元素在容器内固定在指定位置
    3. 元素固定的相对偏移是相对于离他最近的具有滚动框的祖先元素,如果所有祖先元素都不能滚动,则相对于viewport计算偏移量

6. 重绘和回流(首先需要对 重绘和回流 定义有所区分,最后深问具体优化注意点细节,以此了解候选人不仅仅清楚区分定义,更清楚区分的目的是为了日常工作中优化)

定义
  • 重绘
    当改变了某个元素的颜色属性时不会重新触发布局(即不会触发回流),但是会触发样式计算和绘制
  • 回流
    当改变了某个元素的尺寸位置时,会重新进行样式计算布局绘制以及后面的所有流程
具体属性
  • 回流
    width、height、line-height、padding、margin、diaplay、border、top、position、float、font-size、overflow
  • 重绘
    color、background、background-size、visibility、box-shadow

优化

  • className: 不要使用 js 代码对dom 元素设置多条样式,选择用一个 className 代替之
  • transform: 用transform 代替 top,left ,margin-top, margin-left… 这些位移属性
  • 不要在循环内获取dom: 不要在循环内获取dom 的样式例如:offsetWidth, offsetHeight, clientWidth, clientHeight等。浏览器有一个回流的缓冲机制,获取这些属性时会产生回流
  • 文档流:让多次回流的元素脱离文档流比如动画,使用 position 属性的 fixed 值或 absolute 值
  • 一起变化(同时修改所有需要变化的属性)
  • 避免使用table布局
  • 避免使用css表达式

7. transform位移跟position:absolute top:50的区别

  1. 位置:
    • 使用 top left 定位是直接改变元素真实位置的,简单来说你 top: 5px 那就真的是离父容器上端 5px 或者偏离顶部定位 5px(这里我们不讨论 position 各种定位的破事)
    • 使用 transform: translateY(-5px) 只是改变了视觉位置,元素本身位置还是在 0px,只是视觉上向上偏移了 5px。这一点对于 css 布局是非常重要的,因为大多数情况下你不希望一个元素在动画的时候(比如飞离 fade off)会导致父元素大小改变然后导致 siblings 元素位置变动从而导致集体 shaking,所以很多时候我们用 transform。
  2. 语法:
    • 做效果的时候 transform 相对来说是比较方便的,因为 transform 的视角是元素本身,所以比较直观。比如你希望一个元素向左飞 50px 那就是 transform: translateX(-50px),
    • 而用 left 父子元素都是 position: absolute,那可能用 left 就要写成从 left: 100px 到 left: 30px,这就很不直观。
  3. 效率:
    • 由于 transform 不改动 css 布局,因为渲染行为大多数情况下在元素本身,所以效率比 top left 要高。另外在早期的一些版本,用 transform: translateZ(0px) 强制开启硬件加速好像只能应用在 transform 上

8. 以下div是什么颜色

<style>
  div {
    color: red;
  }
  #title {
    color: yellow;
  }
  div.title {
    color: bule;
  }
</style>
<div class="title" id="title">abc</div>

9. CSS优化、提高性能的方法有哪些?

  • 避免过度约束
  • 避免后代选择符
  • 避免链式选择符
  • 使用紧凑的语法
  • 避免不必要的命名空间
  • 避免不必要的重复
  • 最好使用表示语义的名字。一个好的类名应该是描述他是什么而不是像什么
  • 避免!important,可以选择其他选择器
  • 尽可能的精简规则,你可以合并不同类里的重复规则

10. ssr和csr各自的区别,优势

  1. ssr:服务端渲染,页面上的内容是服务端生成的 浏览器直接显示服务端返回的html就可以了
  2. csr:客户端渲染,页面上的内容是我们加载的js文件渲染出来的 js文件运行在浏览器上面 服务端只返回一个html模板
  3. 常见的服务器端渲染框架:基于react的next.js和基于vue的nuxt.js
  4. 二者的区别(csr的优缺点)
    • csr(客户端渲染)的ttfp时间(首屏时间)长
    • csr对于seo不友好,因为百度爬虫(大部分的爬虫)都只识别html里面的内容,而不会去识别js的内容
    • csr中前端负责页面的渲染,后端负责数据的处理,实现了前后端分离,各司其职
    • ssr会影响服务端的性能

11. spa架构的优势和劣势

  1. SPA是什么?
  • 全称是单页面应用。
  • 一个SPA就是一个WEB应用,它所需的资源(HTML CSS JS等),在一次请求中就加载完成,也就是不需刷新地动态加载。
  • 用术语“单页”就是因为页面在初始化加载后就永远不会重新加载刷新。

  1. 优点
  • 减轻服务器端的压力。
  • 提高了页面的渲染效果。
  • 由于移动设备的流行,可以开发提供JSON格式数据的网络服务,然后可以提供不同的客户端使用。
  • SPA的使用,我们可以使用一个HTTP API,一个HTTP API相比在服务端渲染一个HTML页面有诸多好处,这样就可以很方便的进行单元测试等操作,还可以被其他很多客户端程序所用。
  1. 缺点
  • 首屏加载时间会很长。
  • SEO不友好。

12. 首屏渲染优化

  • 优化首屏加载,减少白屏时间,提升加载性能:
  • 加速或减少HTTP请求损耗:使用CDN加载公用库,使用强缓存和协商缓存,使用域名收敛,小图片使用Base64代替,使用Get请求代替Post请求,设置 Access-Control-Max-Age 减少预检请求,页面内跳转其他域名或请求其他域名的资源时使用浏览器prefetch预解析等;
  • 延迟加载:非重要的库、非首屏图片延迟加载,SPA的组件懒加载等;
  • 减少请求内容的体积:开启服务器Gzip压缩,JS、CSS文件压缩合并,减少cookies大小,SSR直接输出渲染后的HTML等;
  • 浏览器渲染原理:优化关键渲染路径,尽可能减少阻塞渲染的JS、CSS;
  • 优化用户等待体验:白屏使用加载进度条、loading图、骨架屏代替等;

js

跨域

  1. 跨域方法
  • jsonp
  • 正向代理:cors: access-control-allow-origin 前端直接请求域名
  • http-proxy、http-proxy-middleware
  • proxy
  • postMessage
  • nginx add_header Access-Control-Allow-Origin "*"
  1. 跨域情况下,有时候发送post请求会看到一条options的请求,为什么?
  • 在跨域请求中,分为简单请求(get和部分post,post时content-type属于application/x-www-form-urlencoded,multipart/form-data,text/plain中的一种)和复杂请求。而复杂请求发出之前,就会出现一次options请求。前台跨域post请求,由于CORS(cross origin resource share)规范的存在,浏览器会首先发送一次options嗅探,同时header带上origin,判断是否有跨域请求权限,服务器响应access control allow origin的值,供浏览器与origin匹配,如果匹配则正式发送post请求。什么是options请求呢?它是一种探测性的请求,通过这个方法,客户端可以在采取具体资源请求之前,决定对该资源采取何种必要措施,或者了解服务器的性能。

强缓存和协商缓存

浏览器缓存包含两种类型,即强缓存(也叫本地缓存)和协商缓存,浏览器在第一次请求发生后,再次请求时:

  • 浏览器在请求某一资源时,会先获取该资源缓存的header信息,判断是否命中强缓存(cache-control和expires信息),若命中直接从缓存中获取资源信息,包括缓存header信息;本次请求根本就不会与服务器进行通信;在firebug下可以查看某个具有强缓存资源返回的信息,例如本地firebug查看的一个强缓存js文件
  • 如果没有命中强缓存,浏览器会发送请求到服务器,请求会携带第一次请求返回的有关缓存的header字段信息(Last-Modified/If-Modified-Since和Etag/If-None-Match),由服务器根据请求中的相关header信息来比对结果是否协商缓存命中;若命中,则服务器返回新的响应header信息更新缓存中的对应header信息,但是并不返回资源内容,它会告知浏览器可以直接从缓存获取;否则返回最新的资源内容

说一下webpack整个生命周期

  • 初始化参数
    • 从配置文件和shell语句中读取与合并参数,得出最终的参数
  • 开始编译
    • 用上一步得到的参数初始化Compiler对象,加载所有配置的插件,执行对应的run方法开始执行编译
  • 确定入口
    • 根据配置中的entry找出所有的入口文件
  • 编译模块
    • 从入口文件出发,调用所有配置的Loader对模块进行编译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理
  • 完成模块编译
    • 在经过第4步使用Loader编译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系
  • 输出资源
    • 根据入口和模块之间的依赖关系,组装成一个个包含多个模块的chunk,再把每个chunk转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
  • 输出完成
    • 再确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统 在以上过程中,Webpack会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用webpack提供的Api改变webpack的执行结果

说一下webpack种css-loader和style-loader的区别,file-loader和url-loader的区别

  • css-loader和style-loader
    • css-loader 处理css文件
    • style-loader 把js中import导入的样式文件代码,打包到js文件中,运行js文件时,将样式自动插入到 style标签中
  • file-loader和url-loader
    • file-loader 返回的是图片的url
    • url-loader 可以通过limit属性对图片分情况处理,当图片小于limit(单位:byte)大小时转base64,大于limit时调用filie-loader对图片进行处理。
    • url-loader封装了file-loader,但是url-loader不依赖于file-loader

webpack做过哪些优化,开发效率、打包策略等方面

  • 使用高版本的webpack(使用webpack4)
  • 多线程/多实例构建:thread-loader
  • 缩小打包作用域
    • exclude/include(确认loader规则范围)
  • 充分利用缓存提升二次构建速度
    • babel-loader 开启缓存
    • terser-webpack-plugin 开启缓存
    • 使用 cache-loader 或者 hard-source-webpack-plugin -DLL
    • 使用DLLPlugin进行分包,使用DllReferencePlugin(索引链接) 对 manifest.json 引用,让一些基本不会改动的代码先打包成静态资源,避免反复编译浪费时间。
    • HashedModuleIdsPlugin 可以解决模块数字id问题

有哪些常见的Loader?他们是解决什么问题的?

  • babel-loader:把es6转成es5;

  • css-loader:加载css,支持模块化,压缩,文件导入等特性;

  • style-loader:把css代码注入到js中,通过dom操作去加载css;

  • eslint-loader:通过Eslint检查js代码;

  • image-loader:加载并且压缩图片晚间;

  • file-loader:文件输出到一个文件夹中,在代码中通过相对url去引用输出的文件;

  • url-loader:和file-loader类似,区别是用户可以设置一个阈值,大于阈值会交给 file-loader 处理,小于阈值时返回文件 base64 形式编码 (处理图片和字体)。

  • source-map-loader:加载额外的source map文件,方便调试。

  • raw-loader:加载文件原始内容(utf-8)

  • svg-inline-loader:将压缩后的 SVG 内容注入代码中

  • json-loader 加载 JSON 文件(默认包含)

  • handlebars-loader: 将 Handlebars 模版编译成函数并返回

  • ts-loader: 将 TypeScript 转换成 JavaScript

  • awesome-typescript-loader:将 TypeScript 转换成 JavaScript,性能优于 ts-loader

  • sass-loader:将SCSS/SASS代码转换成CSS

  • postcss-loader:扩展 CSS 语法,使用下一代 CSS,可以配合 autoprefixer 插件自动补齐 CSS3 前缀

  • tslint-loader:通过 TSLint检查 TypeScript 代码

  • mocha-loader:加载 Mocha 测试用例的代码

  • coverjs-loader:计算测试的覆盖率

  • vue-loader:加载 Vue.js 单文件组件

  • i18n-loader: 国际化

  • cache-loader: 可以在一些性能开销较大的 Loader 之前添加,目的是将结果缓存到磁盘里

有哪些常见的Plugin

  • define-plugin:定义环境变量 (Webpack4 之后指定 mode 会自动配置)

  • ignore-plugin:忽略部分文件

  • html-webpack-plugin:简化 HTML 文件创建 (依赖于 html-loader)

  • web-webpack-plugin:可方便地为单页应用输出 HTML,比 html-webpack-plugin 好用

  • uglifyjs-webpack-plugin:不支持 ES6 压缩 (Webpack4 以前)

  • terser-webpack-plugin: 支持压缩 ES6 (Webpack4)

  • webpack-parallel-uglify-plugin: 多进程执行代码压缩,提升构建速度

  • mini-css-extract-plugin: 分离样式文件,CSS 提取为独立文件,支持按需加载 (替代extract-text-webpack-plugin)

  • serviceworker-webpack-plugin:为网页应用增加离线缓存功能

  • clean-webpack-plugin: 目录清理

  • ModuleConcatenationPlugin: 开启 Scope Hoisting

  • speed-measure-webpack-plugin: 可以看到每个 Loader 和 Plugin 执行耗时 (整个打包耗时、每个 Plugin 和 Loader 耗时)

  • webpack-bundle-analyzer: 可视化 Webpack 输出文件的体积 (业务组件、依赖第三方模块)

Loader和Plugin的区别?

Loader 本质就是一个函数,在该函数中对接收到的内容进行转换,返回转换后的结果。 因为 Webpack 只认识 JavaScript,所以 Loader 就成了翻译官,对其他类型的资源进行转译的预处理工作。

Plugin 就是插件,基于事件流框架 Tapable,插件可以扩展 Webpack 的功能,在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。

Loader 在 module.rules 中配置,作为模块的解析规则,类型为数组。每一项都是一个 Object,内部包含了 test(类型文件)、loader、options (参数)等属性。 Plugin 在 plugins 中单独配置,类型为数组,每一项是一个 Plugin 的实例,参数都通过构造函数传入。

cdn

  1. 是什么
  • 内容分发网络

  • 地理位置分散的服务器组成的网络,服务器存储网站缓存作为服务器与客户实体

  • 后续不需要任何请求可以传递到原始服务器,确保减轻重要服务器压力,全球用户相同速度访问网页

  1. 优点
  • 解决网络拥挤
  • 将网站内容缓存到边缘服务器上,优先且就进提供查找的内容,通过这种方式提升网站速度
  • 改善网络性能确保了更好用户体验
  • 改善 SEO 获得更好排名
  1. 缺点
  • 这个IP下出现了上百个网站,SEO 权重分散
  • 代价高昂
  • 只能对静态内容加速

V8垃圾回收机制

垃圾回收: 将内存中不再使用的数据进行清理,释放出内存空间。V8 将内存分成 新生代空间 和 老生代空间。

  • 新生代空间: 用于存活较短的对象
    • 又分成两个空间: from 空间 与 to 空间
    • Scavenge GC算法: 当 from 空间被占满时,启动 GC 算法
      • 存活的对象从 from space 转移到 to space
      • 清空 from space
      • from space 与 to space 互换
      • 完成一次新生代GC
  • 老生代空间: 用于存活时间较长的对象
    • 从 新生代空间 转移到 老生代空间 的条件
      • 经历过一次以上 Scavenge GC 的对象
      • 当 to space 体积超过25%
    • 标记清除算法: 标记存活的对象,未被标记的则被释放
      • 增量标记: 小模块标记,在代码执行间隙执,GC 会影响性能
      • 并发标记(最新技术): 不阻塞 js 执行
    • 压缩算法: 将内存中清除后导致的碎片化对象往内存堆的一端移动,解决 内存的碎片化

new一个构造函数,内部做了什么事情;如果构造函数里面主动return一个值会怎么样?

  1. 在内存中创建一个新的空对象
  2. 让this指向这个新的对象
  3. 执行构造函数里面的代码,给这个新的对象添加属性和方法
  4. 返回这个新的对象(所以构造函数里面不需要return) return 基本类型 会返回一个空对象 return 对象类型 则会返回这对象

继承方式

  1. 类继承
  2. 构造函数继承
    • call方法可以改变函数的执行上下文,因此在子类中调用父类的构造函数,就相当于以子类的this调用了父类的构造函数,但是最终会返回到子类创建的对象中,实现了子类调用父类构造函数的操作
  3. 组合继承
    • 既不会实例属性影响类属性的引用特性,同时在子类构造函数中执行父类的构造函数能都传递参数
  4. 原型继承
    • proptotype
  5. 寄生组合继承
    • 直接将父类的原型复制到子类,同时将父类的原型中的构造函数改成子类的构造函数,变为一个子类的对象实现继承,一般和其他的构造函数式继承混合使用

for..in和Object.keys的区别

for..in循环时,返回的是所有能够通过对象访问的、可枚举的属性,既包括存在于实例中的属性,也包括存在于原型中的实例。 Object.keys()用于获取对象自身所有的可枚举的属性值,但不包括原型中的属性,然后返回一个由属性名组成的数组 for..in会枚举对象原型链上的可枚举属性,Object.keys不会

简述eventLoop

执行结果
setTimeout(function() { 
     console.log(1) 
}, 0); 

new Promise(function(resolve, reject) { 
     console.log(2) 
     for (var i = 0; i < 10000; i++) { 
          if(i === 10) {console.log(10)} 
          i == 9999 && resolve(); 
     } 
     console.log(3) 
}).then(function() { 
     console.log(4) 
})  
console.log(5)      // 2 10  3  5 4 1

简述节流防抖原理、区别及应用场景

  1. 防抖:
  • 原理: 在事件被触发n秒后再执行回调,如果n秒内又被触发,则重新计时
  • 场景:
    • 搜索框联想:防止联想发送请求,只发送最后一次输入
  1. 节流:
  • 原理: 规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。
  • 场景:
    • 拖拽场景: 固定时间内只执行一次,防止超高频次触发位置变动

数组常用方法

forEach map filter some every reduce find some every reduce

代码执行结果

数据类型
if([] == false){console.log(1)};
if({} == false) {console.log(2)};
if([]){console.log(3)};
if([1] == [1]){console.log(4)}; // 1 3
变量名提升
alert(a);
a();
var a = 3;
function a() {
  alert(10);
}
alert(a);
a = 6;
a(); // 'function a(){ alert(10) }'' 10 3 typeError: a is not a function
箭头函数this
var name = 'window'; 
var A = {
   name: 'A',
   sayHello: () => {
      console.log(this.name)
   }
}
A.sayHello();
A.sayHello.call({name: 'B'}); // window // window
作用域
for (var i=1; i<=5; i++) { 
    setTimeout(function timer() { 
       console.log(i); 
    }, i*1000); 
}   //5个6

for (var i=1; i<=5; i++) { 
    (function(j) { 
        setTimeout(function timer() { 
            console.log(j); 
        }, j*1000); 
    })(i); 
}
for (let i=1; i<=5; i++) { 
    setTimeout(function timer() { 
        console.log(i);
    }, i*1000); 
}

判断数据类型

Object.prototype.toString.call(a).slice(8, -1);

函数执行过程

  1. 确定执行环境
  • 当JavaScript执行全局代码的时候,会创建全局执行执行上下文(整个页面的生存周期内,全局执行上下文只有一份);在调用一个函数的时候会创建该函数的执行上下文(执行环境)。将多个执行上下文管理起来的就是调用栈(调用栈就是用来管理函数调用关系的一种数据结构)
  1. 创建阶段
  • 函数执行前会完成一些初始化操作,这就是创建阶段,该阶段主要进行生成变量对象、建立作用域链、确定this指向,为后续执行阶段做准备。
  1. 执行阶段
  • 一切准备工作做好之后即进行代码执行阶段,该阶段将完成变量赋值、函数引用及执行其它代码。
  1. 垃圾回收
  • 当函数执行完毕之后,会从调用栈中淡出、销毁、等待浏览器的垃圾回收。

什么是事件委托,事件委托的原理,作用,缺点

  • 简单来说,事件就是onclick,onmousemove等等,委托就是把自己的事拜托给别人做,让别人来完成.
  • 原理:利用事件冒泡,只指定一个事件处理程序来管理某一类型的所有事件。
  • 作用:减少DOM操作,节省内存空间,提高性能。
  • 缺点
  1. 事件委托基于冒泡,不冒泡的事件不支持
  2. 层级过多,冒泡过程中可能被中间层阻止
  3. 如果把所有事件都用事件委托,可能会出现事件误判,即不该触发事件的被绑定了

前端错误监控怎么做

  • 前端错误的分类: 即时运行错误(代码错误)和资源加载错误。
    • 即时运行错误的捕获方式:
  1. try…catch

  2. window.onerror

    • 资源加载错误的捕获方式:
  3. object.onerror() (无法冒泡)

  4. performance.getEntries()

  5. Error事件捕获

    • 延伸:跨域的js运行错误跨域捕获吗? 可以。
  6. 在script标签增加crossorigin属性

  7. 设置js资源响应头Access-Control-Allow-Origin:*

    • 上报错误的基本原理:
  8. 利用ajax

  9. 利用Image对象(生产环境使用)

组件

vue

1. vue响应式原理

核心是 Object.defineProperty 默认初始化 Vue ,给 data 中的属性用 Object.defineProperty  重新定义,为属性添加 get  set  方法。当属性在模版中获取的时候,会调用 get ,然后进行依赖收集。在属性发生变化的时候进行会执行 set , 通知相关依赖进行派发更新。

2. 数组是如何检测变化的

  • 通过修改数组原型链, 重写数组方法这种劫持的方式来检测变化。
  • 修改数组属性的__proto__ 指向我们自己设置的方法,目前我们指拦截了 push pop unshift shift sort splice resolve 七种方法
  • 循环数组中每一项,是对象类型才会被观测

3. 为什么组件中的 data 必须是一个函数,然后 return 一个对象,而 new Vue 实例里,data 可以直接是一个对象

因为组件是用来复用的,用的都是同一个构造函数,JS 里对象是引用关系,这样作用域没有隔离,而 根 new Vue 的实例,是不会被复用的,因此不存在引用对象的问题

4. vue.nextTick 实现原理

nextTick 它是一个异步方法。 它里面主要包含了四个方法,Promise、MutationObserve、setImmediate、setTimeout、主要是运用 EventLoop JS 运行机制。同步任务执行完之后,会执行微任务,接着宏任务。所以 Promise 优先考虑。

5. Vue组件间通讯的方式

props、$emit、vuex

6. vuex原理

Vuex 实现了一个单向数据流,在全局拥有一个 State 存放数据,当组件要更改 State 中的数据时,必须通过 Mutation 进行,Mutation 同时提供了订阅者模式供外部插件调用获取 State 数据的更新。而当所有异步操作(常见于调用后端接口异步获取更新数据)或批量的同步操作需要走 Action,但 Action 也是无法直接修改 State 的,还是需要通过 Mutation 来修改 State 的数据。最后,根据 State 的变化,渲染到视图上。

7. 虚拟dom为什么快

  • 真实 dom 中的属性非常多,再加上频繁操作 dom, 每次更改都需要重新替换,在渲染。
  • 虚拟 dom 他用js 对象描述一段 dom 结构,每次dom 变化会比对 新旧结点,并运用了DOM diff 算法,
  • 相同的子结点不会重新渲染,存在的相同结点会进行替换操作,不存在的结点才会新增

8. DOM DIFF

  • 先同级比较,在比较子节点
  • 先判断一方有儿子,一方没儿子,增删
  • 比较都有儿子
  • 递归比较儿子

react

手写代码

1.单链表反转

var list = {
  val: 1,
  next: {
    val: 2,
    next: {
      val: 3,
      next: {
        val: 4,
        next: null
      }
    }
  }
}

递归

function invertList (head) {
  if (!head || !head.next) return head
  const currenttList = invertList(head.next)
  head.next.next = head
  head.next = null
  return currenttList
}
const result = invertList(list)
console.log(result)

非递归

function invertList (head) {
  if (!head || !head.next) return head
  let currentList = null
  while (head != null ) {
    let temp = head.next
    head.next = currentList
    currentList = head
    head = temp
  }
  return currentList
}
const result = invertList(list)
console.log(result)

2.给定一个数组,数组内各元素通过parentId和id确定父子关系,解析成树形结构

[{id, children: []}] [{id: 0}, {id: 1, parentId: 0}, {id:2, parentId: 0}, {id: 3, parentId: 0}, {id: 11,parentId: 1}, {id: 111,parentId: 11}...]

3. 手写一个深拷贝

function deepClone(origin) {
  if (getType(origin) === 'Array') {
    return origin.map(item => deepClone(item));
  }

  if (getType(origin) === 'Object') {
    const obj = {};
    Object.keys(origin).forEach(key => (obj[key] = deepClone(origin[key])));
    return obj;
  }

  return origin;
}

4. 输入一个字符串 输出所有子串 abcd 输出 a b c d ab abc abcd bc bcd c cd

    getStr(str) {
      for (let i = 0; i <= str.length; i++) {
        for (let j = 0; j <= str.length - i; j++) {
          if (str.substr(i, j)) {
            console.log(str.substr(i, j));
          }
        }
      }
    }

5. 多维数组转一维或根据参数展开n层

const arr = [1, [2, 22], [3, [33, [333]]],[4, [44, [444, [4444]]]], [5, [55, [555, [5555, [55555]]]]]]

const flatteOne = (arr) => {
    let res = []
    arr.forEach(item => {
        res = Array.isArray(item) ? [...res,...item] : [...res, item]
    })
    return res;
}

const flatte = (arr, num = 0) => {
    if(!num) {
        return arr;
    }
    let newArr = arr
    for(let i = 1; i <= num; i++) {
        newArr = flatteOne(newArr)
    }
    return newArr
}

6. 实现格式化输出 eg 88888888888 88,888,888,888

7. 找出两个数组的交集元素 [1,2,3,4,5,6] [1,4,5,6,9,10] 输出[1,4,5,6]

8. 实现一个JSON.stringify

if (!window.JSON) {
  window.JSON = {
    parse: function (sJSON) {
      return eval("(" + sJSON + ")");
    },
    stringify: (function () {
      var toString = Object.prototype.toString;
      var isArray =
        Array.isArray ||
        function (a) {
          return toString.call(a) === "[object Array]";
        };
      var escMap = {
        '"': '\\"',
        "\\": "\\\\",
        "\b": "\\b",
        "\f": "\\f",
        "\n": "\\n",
        "\r": "\\r",
        "\t": "\\t",
      };
      var escFunc = function (m) {
        return (
          escMap[m] ||
          "\\u" + (m.charCodeAt(0) + 0x10000).toString(16).substr(1)
        );
      };
      var escRE = /[\\"\u0000-\u001F\u2028\u2029]/g;
      return function stringify(value) {
        if (value == null) {
          return "null";
        } else if (typeof value === "number") {
          return isFinite(value) ? value.toString() : "null";
        } else if (typeof value === "boolean") {
          return value.toString();
        } else if (typeof value === "object") {
          if (typeof value.toJSON === "function") {
            return stringify(value.toJSON());
          } else if (isArray(value)) {
            var res = "[";
            for (var i = 0; i < value.length; i++)
              res += (i ? ", " : "") + stringify(value[i]);
            return res + "]";
          } else if (toString.call(value) === "[object Object]") {
            var tmp = [];
            for (var k in value) {
              if (value.hasOwnProperty(k))
                tmp.push(stringify(k) + ": " + stringify(value[k]));
            }
            return "{" + tmp.join(", ") + "}";
          }
        }
        return '"' + value.toString().replace(escRE, escFunc) + '"';
      };
    })(),
  };
}

给一个字符串'asdasdfaffjasaso',返回第一个不重复的字母

方法一

var str = 'asdasdfaffjasaso';
var strs = [];
var len = str.length;
var num = 0;
for (var i = 0; i < len; i++) {
    strs.push(str.slice(i, i + 1));
}
for (var j = 0; j < len; j++) {
    for (var k = 0; k < len; k++) {
        if (j != k) {
            if (strs[j] != strs[k]) {
                num++;
            }
        }
    }
    if (num == len - 1) {
        console.log(strs[j]);
        break;
    }
    num = 0;
}

方法二

var firstUniqChar = function(s) {
        let stack = {}
        for(let i=0;i<s.length;i++){
            if(!stack[s[i]]){
                stack[s[i]] = 1
            }else{
                stack[s[i]]++
            }
        }
        let ret = -1;
        for(let j=0;j<s.length;j++){
            if(stack[s[j]]==1){
                ret = j
                return ret
            }
        }
        return ret
};