2022年前端面试要点

267 阅读23分钟

一、Vue

1.谈谈对于MVVM的理解

基本上,我们写的产品就是通过接口从数据库中读取数据,然后将数据经过处理展现到用户看到的视图上。当然我们还可以从视图上读取用户的数据,然后又将用户的输入通过接口写入到数据库中。但是如何将数据展示到视图上,然后又如何将用户的数据写入到数据库中,就要采用MVVM了。 Model-View-ViewModel的缩写

  • Model 本地数据和数据库中的数据
  • View 用户看到的视图
  • ViewModel 组件的实例对象。监听模型数据的改变和控制视图行为、处理用户交互,简单理解就是一个同步View和Model的桥梁,连接Model和View
  • 在MVVM框架下,V和M并没有直接联系,而是通过VM进行交互,VM通过双向数据绑定把V和M连接了起来,而V和M之间的同步工作完全是自动的,无需人为干涉。因此开发者只需要关注业务逻辑,不需要手动操作DOM,不需要关注数据状态的同步问题,复杂的数据状态维护完全由MVVM来统一管理。
  • Vm是组件的实例,V是模板,M的话在引入Vuex的情况下完全可以和组件分离。
  • mvvm和其他框架的区别:数据驱动,通过数据来显示视图而不是节点操作。适用于数据操作比较多的场景。

2.谈谈你对Vue生命周期函数的理解

  • 创建、加载、更新、销毁

Vue实例从创建到销毁的过程。总共8个阶段: 创建前/后(beforeCreate|实例刚创建/created|创建完成,属性已经绑定,但还未生成真实dom),

载入前/后(beforeMount|模板编译挂载之前/mounted|组件已挂载),

组件激活/失活(actived/deactivated),

更新前/后(beforeUpdate|销毁开始/updated),

销毁前/后(beforeDestroy/destroyed)。

另外还有keep-alive独有的生命周期,分别是activated和deactivated。用keep-alive包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行。

  • 第一次页面加载会触发哪几个钩子:beforeCreate、created、beforeMount、mounted

  • DOM渲染在哪个周期就已经完成了:mounted

  • 列举三个Vue中常用的生命周期钩子函数:

    • created:实例已经创建完成之后调用,在这一步,经常写一些初始化的方法
    • mounted:dom已渲染完成
    • active的:keep-alive组件激活时调用
  • 作用:给用户在不同阶段添加自己代码的机会

  • created和mounted这两个生命周期中请求数据有什么区别呢?

    • 一般在created中请求数据就可以,如果涉及到需要页面加载完成之后的话就用mounted
    • 在created中,视图中的html并没有渲染出来,所以此时如果去操作html中的dom节点,一定找不到相关的元素。而在mounted中,由于此时html已经渲染出来了,所以可以直接操作dom节点

3.Vue实现数据双向绑定的原理是什么?

数据变化更新视图,视图变化更新数据。双向绑定是通过数据劫持(当给属性赋值的时候,程序可以感知到,就可以控制改变属性值)和观察者模式(当属性发生变化的时候,使用该数据的地方也发生改变)来实现的。

Object.defineProperty():劫持数据的getter和setter。

proxy相对于defineProperty的优势:1.数组变化也能监听到;2.不需要深度遍历监听

4.Vue组件间的参数传递

父子组件传值

父传子:父组件把要传递的数据绑定在属性上发送,子组件通过props接收; 子传父:子组件通过this.$emit(自定义事件名,要发送的数据),父组件设置一个监听事件来接收,然后拿到数据 解决一切通信问题 eventBus,创建一个事件中心,相当于中转站,可以利用它来传递事件和接收事件。项目较小时,推荐用。项目较大时,推荐Vuex

5.谈谈Vue的路由?

前端路由就是更新视图但不请求页面

  • hash模式:地址栏中有#,用window.location.hash读取。特点:hash虽然在URL中,但hash值变化时,不会像服务器请求数据。可以使用hashchange事件来监听hash值的变化,从而进行页面跳转。
  • history模式:地址栏中没有#,HTML5特工了history API 来实现URL的变化,主要有history.pushState(),history.replaseState()。这两个API可以在不刷新的情况下,操作浏览器的历史记录。唯一不同的是,前者是新增一个历史记录,后者是直接替换当前的历史记录。
  • 跳转: 1.this.$router.push() 2.
  • 占位:
  • vue路由传参的两种方式:params和query
    • 动态路由也叫路由传参,就是根据不同的选择在同一个组件渲染不同的内容。用法上:query用path引入,params用name引入。接收参数都是类似的,分别是this.route.query.namethis.route.query.name和this.route.params.name。URL展示上:params类似于post,query类似于get,也就是params传值更安全点。query通过url传参,刷新页面还在,params刷新页面不在了。

6.谈谈你对Vuex的理解

优点:解决了非父子组件的通信,减少了ajax请求次数,有些可以直接从state中获取;缺点:刷新浏览器,vuex会重新变为初始状态。解决办法是vuex-along,配合计算属性和sessionStorage来实现 在vue组件里,通过dispatch来触发actions提交修改数据的操作,然后通过actions的commit触发mutations来修改数据,mutations接收到commit的请求,会自动通过mutate来修改state,最后由store触发每一个调用它的组件的更新

  • 简单来说,vuex就是vue的状态管理工具;通过创建一个集中地数据存储,方便程序中的所有组件进行访问。
  • state(状态中心): 数据源存放地,对应vue对象的data,state里存放的数据是响应式的,数据发生变化,对应这个数据的组件也会发生改变,用this.$store.state.xxx调用
  • mutations(更改状态): 处理数据逻辑的方法全部放在mutations中,当触发事件想改变state数据的时候使用mutations,用this.$store.commit调用。store中修改状态的唯一办法就是提交mutations
  • getters(获取状态): 相当于store的计算属性,主要是对state中数据的过滤以及计算操作,用this.$store.getters.xxx调用。可以在多组件之间复用。
  • actions(异步更改状态):异步操作数据,但是是通过mutation来操作数据,不能直接变更状态。用this.$store.dispatch来触发
  • modules(将state分成多个modules,便于管理): 项目特别复杂的时候,让每一个模块拥有自己的state、mutation、action、getters,更清晰,方便使用
  • 使用场景:组件之间的状态、登录状态、加入购物车、音乐播放
  • vuex怎么请求异步数据?1.首先在state中创建变量;2.然后在action中调用封装好的axios请求,异步接收数据,commit提交给mutations。mutations中改变state中的状态,将从action中获取到的值赋值给state。

7.routeroute和router的区别

  • $route是“路由信息对象”,包含path、params、hash、query、fullPath、matched、name等路由信息参数
  • $router是“路由实例对象”包含了路由的跳转方法,钩子函数等

8. 作用是什么?

  • 包裹动态组件时,会缓存不活动的组件实例,主要用于保留组件状态或避免重新渲染

比如一个列表和一个详情,那么用户会经常执行打开详情=>返回列表=>打开详情...这样的话列表和详情都是一个频率很高的页面,那么就可以对列表组件使用进行缓存,这样用户每次返回列表的时候,能从缓存中快速渲染,而不是重复渲染。

  • 声明周期函数:activated在keep-alive组件激活时调用,deactivated在keep-alive组件停用时调用

9.nextTick

可以让我们在下次DOM更新循环结束之后执行延迟回调,用于获得更新后的DOM状态

10.路由之间的跳转方式(声明式/编程式)?

声明式(标签跳转):;为路由出口,路由模板显示的位置。

编程式(js方式): this.$router.push('index')

11.Vue的核心是什么?

Vue是一套构建用户界面的渐进式自底向上增量开发的MVVM框架。Vue的核心只关注视图层。

核心思想:1.数据驱动(视图内容随着数据的改变而改变);2.组件化(可以增加代码的复用性,可维护性,可测试性,提高开发效率,方便重复使用,体现了高内聚低耦合)

12.简述vue的单向数据流

父级prop的更新会向下流动到子组件中,每次父组件发生更新,子组件所有的prop都会刷新为最新的值。 数据从父组件传递给子组件,只能单向绑定,子组件内部不能直接修改父组件传递过来的数据。(可以使用data和computed解决)

13.vue常见的修饰符有哪些?

  • 修饰符:
    • .lazy 改变后触发,光标离开input输入框的时候值才会改变
    • .number 将输出字符串转为number类型
    • .trim 自动过滤用户输入的首位空格
  • 事件修饰符:
    • .stop 阻止事件冒泡,相当于原生js中的event.stopPropagation()
    • .prevent 防止执行预设的行为,相当于原生js中的event.preventDefault()
    • .capture 添加事件监听器时使用事件捕获模式,就是谁有该事件修饰符,就先触发谁
    • .self 只会触发自己范围内的事件,不包括子元素
    • .once 只执行一次
  • 键盘修饰符:
    • .enter 回车键
    • .tab 制表键
    • .esc 返回键
    • .space 空格键
    • .up 向上键
    • .down 向下键
    • .left 向作键
    • .right 向右键
  • 系统修饰符:.ctrl .alt .shift .meta

14. v-text、v-html与{{}}的区别

  • 插值语法{{}}将数据解析为纯文本,不能输出html
  • v-html指令:可以渲染输出html
  • v-text指令:将数据解析为纯文本,不能显示输出html。与{{}}的区别是在页面加载时不显示双花括号(在渲染的数据比较多时,可能会把大括号显示出来,俗称屏幕闪动)

15.vue跨域的解决方式

  • 后台更改header
  • 使用jsonp
  • 用http-proxy-middleware(配置代理服务器的中间件)

16.vue数据绑定的几种方式

  • 单向绑定:{{}}与html内字符串绑定
  • v-bind 与html属性绑定
  • 双向绑定 v-model
  • 一次性绑定 v-once 依赖于v-model

17.重点:vue路由钩子函数/路由守卫有哪些

全局守卫:beforeEach(to, from, next)和afterEach(to,from)

路由独享守卫beforeEnter

组件内的守卫:路由进入/更新/离开之前,beforeRouterEnter/update/leave

18.vue中指令有哪些?

  • v-for:循环数组、对象、字符串、数字
  • v-on:绑定事件监听
  • v-bind:动态绑定一个或多个属性
  • v-model:表单空间或者组件上创建双向绑定
  • v-if v-else v-else-if:条件渲染
  • v-show:根据表达式真假,切换元素的diaplay
  • v-html:更新元素的innerHtml
  • v-text:更新元素的textContent
  • v-pre:跳过这个元素和子元素的编译过程
  • v-cloak:保持在元素上直到关联实例结束编译
  • v-once:只渲染一次

19.vue组件中的data为什么是函数

data是一个函数时,每个组件实例都有自己的作用域,每个实例相互独立,不会相互影响。

如果引用类型(对象),当多个组件共用一个数据源时,一处数据改变,所有的组件数据都会改变,所以要利用函数通过return返回对象的拷贝,让每个实例都有自己的作用域,相互不影响。

20.谈谈watch和计算属性,以及两者区别

watch是监控一个值的变化,并调用因为变化需要执行的方法

计算属性computed是用来声明式的描述一个值依赖了其他的值,当它依赖的这个值发生改变时,就更新DOM。每个计算书子那个都会包含一个getter和setter,读取时触发getter,修改时触发setter

computed有缓存功能,当无关数据改变时,不会重新计算,直接使用缓存中的值。而watch监听的是在data中定义的变量,当该变量改变时,会触发watch中的方法。

21.vue循环中的key的作用

key值的存在保证了唯一性,Vue在执行时,会对节点进行检查。如果没有key值,那么vue检查到这里有dom节点,就会对内容清空并赋新值。如果有key值存在,那么会对新老节点进行对比,比较两者key是否相同,进行调换位置或删除操作。

22.vue单页面spa的优缺点

  • 优点:前后端分离,用户体验好,一个字“快”,内容改变不需要重新加载整个页面
  • 缺点:不利于seo,初次加载耗时时间长(浏览器一开始就要加载html、css、js,所有的页面内容都包含在主页面中),页面复杂度提高了,导航不可用

23.请简述插槽

可以放任何内容。在子组件中使用,是为了将父组件中的子组件模板数据正常显示。可以在父组件中使用slot-scope从子组件获取数据。

24.首屏加载比较慢的原因,怎么解决?白屏事件怎么检测,怎么解决白屏问题?

  • 首屏加载慢的原因:第一次加载页面有很多组件数据需要渲染
  • 解决方法:1.路由懒加载 component:() => import("路由地址");2.UI框架按需加载;3.gzip压缩
  • 解决白屏问题
    • 使用v-text渲染数据
    • 使用{{}}语法渲染数据,同时使用v-cloak指令(用来保持在元素上直到关联实例结束时进行编译)

25.vue中this指向问题:

  - 所有被Vue所管理的函数,最好写成普通函数,这些this才指向组件实例对象
  - 所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数、promise的回调函数)最好写成箭头函数,这样this才指向组件实例对象,否则指向window

二、HTML

1. HTTP请求的几种方法及用途

请求:浏览器--请求--服务器; 响应:服务器--响应--浏览器

  • GET方法(缓存、请求长度受限制,会被历史记录保存)

    • 发送一个请求来取得服务器上的某一资源(查)
    • 类似于查找的过程,用户获取数据,可以不用每次都与数据库连接,所以可以使用缓存
    • 请求能携带的参数比较少,大小有限制(1024字节),回来浏览器的URL地址栏显示数据内容,不安全,但高效
  • POST方法(安全、大数据、更多编码类型)

    • 向URL指定的资源提交数据或附加新的数据(增)
    • 一般是修改和删除的工作,所以必须与数据库交互,所以不能使用缓存。
    • 请求能够携带的参数没有限制,大小没有限制,不会在浏览器的URL地址栏显示数据内容,安全,但不搞笑
  • PUT方法

    • 跟POST方法很像,也是向服务器提交数据。不同的是PUT指定了资源在服务器上的位置,而POST没有(改)
  • DELETE方法

    • 删除服务器的某资源(删)
  • HEAD方法

    • 只请求页面的首部
  • OPTIONS方法

    • 用于获取当前URL所支持的方法。如果请求成功,会有一个Allow的头包含类似“GET,POST”这样的信息
  • TRACE方法

    • 用于激发一个远程的,应用层的请求消息回路
  • CONNECT方法

  • 把请求链接转换到透明的TCP/IP通道

2. 重点:从浏览器地址栏输入url到显示页面的步骤

  • tomcat默认端口8080;mysql默认端口3306;http默认端口80;https默认端口443;

  • 默认主机名localhost(相当于127.0.0.1)

  • a.当发送一个URL请求时,浏览器会开启一个线程来处理这个请求,同时在远程的DNS服务器上启动一个DNS查询。这能使浏览器获得一个请求对应的IP地址;

  • b.浏览器与远程Web服务器通过TCP三次握手协商来建立一个TCP/IP连接。该握手包括一个同步报文,一个同步-应答报文和一个应答报文,这三个报文在浏览器和服务器之间传递。该握手首先由客户端尝试建立起通信,然后服务器响应并接收客户端的请求,最后由客户端发起该请求已经被接收的报文。

  • c.一旦TCP/IP连接建立,浏览器会通过该连接向远程服务器发送HTTP的GET请求。远程服务器找到资源并使用HTTP响应返回该资源。

  • d.此时,Web服务器提供资源服务,客户端开始下载资源。

  • 简化:

  • 浏览器根据请求的URL交给DNS域名解析,找到真实IP,向服务器发起请求;

  • 服务器交给后台处理完成后返回数据,浏览器接收文件(HTML、CSS、JS、图像等);

  • 浏览器对加载到的资源(HTML、CSS、JS等)进行语法解析,建立相应的内部数据结构(如HTML的DOM);

  • 载入解析到的资源文件,渲染页面,完成。

  • 通俗:

    • 输入一个域名,回车;
    • 检查本机的C:\Windows\System32\drivers\etc\hosts(hosts配置的文件目录)配置文件下有没有这个域名映射
      • 如果有:直接返回对应的ip地址,这个地址中有问们需要访问的web程序,可以直接访问。 比如 127.0.0.1 www.kincar.com
      • 如果没有:去DNS服务器(全世界的域名都在这里管理)找,找到的话就返回,找不到就不返回。

3.如何进行网站性能优化?

  • content方面(降低请求量)
    • 减少HTTP请求:合并文件、合并图片、CSS精灵图、inline Image
    • 减少DNS查询:DNS缓存、将资源分布到恰当数量的主机名
    • 减少DOM元素数量
    • 禁止使用iframe(阻塞父文档onload事件)
  • server方面(加快请求速度)
    • 使用CDN托管
    • 预解析DNS
    • 对组件使用Gzip压缩
  • 缓存方面
    • 减小cookie大小
    • 离线缓存 manifest
    • 离线数据缓存 localStorage
  • css方面
    • 将样式表放到页面顶部
    • 不使用css表达式
    • 使用不使用@import
  • javascript方面
    • 将脚本放到页面底部
    • 将js和css从外部引入
    • 压缩js和css
    • 删除不需要的脚本
    • 减少DOM访问
  • 图片方面
    • 优化图片:根据实际颜色选择色深、压缩
    • 优化css精灵图(通过backgound-position和元素尺寸调解),小图标使用base64编码
    • 不要在HTML中拉伸图片
    • 图片懒加载(在页面上的未可视区域可以添加一个滚动事件,判断图片位置与浏览器顶端的距离与页面的距离,如果前者小于后者,优先加载)
      • 预加载(提前加载):在页面加载完成之前,提前将所需资源下载,之后使用的时候从缓存中调用;懒加载(迟缓甚至不加载):延迟加载,按照一定的条件或者需求等到满足条件的时候再加载对应的资源。

4. HTTP响应状态码及其含义

  • 1xx:信息状态码(接收,继续处理)

    • 100 Continue 继续,一般在发送post请求时,已发送了http header之后服务端将返回此信息,表示确认,之后发送具体参数信息
  • 2xx:成功状态码

    • 200 OK 请求响应成功,并返回数据
    • 201 Created 已创建。请求成功并且服务器创建了新的资源
    • 202 Accepted 已接收。服务器已经接收请求,但尚未处理
  • 3xx:重定向(你重新到我给你的新位置去)

    • 301 Moved Permanently 永久移动,重定向。请求的网页已永久移动到新位置
    • 302 Found 临时移动,可使用原有URL。临时性重定向
    • 303 See Other 临时性重定向,且总是使用Get请求新的URL
    • 304 Not Modified 资源未修改,可使用缓存。自从上次请求后,请求的网页未修改过
  • 4xx:服务端错误(找不到资源)

    • 400 Bad Request 请求语法错误。服务器无法理解请求的格式,客户端不应当尝试再次使用相同的内容发起请求
    • 401 Unauthorized 要求身份认证。请求未授权
    • 403 Forbidden 决绝请求。禁止访问
    • 404 Not Found 资源不存在。找不到如何与URL相匹配的资源
  • 5xx:服务器错误

    • 500 Internal Server Error 最常见的服务器端错误,服务器代码错误
    • 502 网关错误
    • 503 Service Unavailable 服务器端暂时无法处理请求(可能是过载或维护)

5. html5有哪些新特性、移除了哪些元素?

  • html5主要是关于图像、位置、存储、多任务功能的增加
    • 绘画 canvas
    • 用于媒介回放的video和audio元素
    • 本地离线存储localStorage长期存储数据,浏览器关闭后数据不会丢失;而会话存储sessionStorage的数据在浏览器关闭后自动删除
    • 语义化更好的内容元素,如article、footer、header、nav、section等
    • 表单控件,calendar、date、time、email、url、search等
    • 新技术webworke、websocket、geolocation
  • 移除的元素:
    • 纯表现的元素:basefont、big、center、s、strike、tt、u
    • 对可用性产生负面影响的元素:frame、frameset、noframes

6. 请描述一下cookie,sessionStorage和localStorage的区别?

  • cookie 是网站为了标识用户身份而储存在用户本地终端(Client Side)上的数据(通常经过加密)。cookie数据始终在同源的http请求中携带(即使不需要),并会在浏览器和服务器间来回传递。

  • sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存

  • 存储大小:

    • cookie数据大小不能超过4k
    • sessionStorage和localStorage可以达到5M或更大
  • 存储时间:

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

7. 行内元素有哪些?块级元素有哪些?空(void)元素有哪些?行内元素和块级元素有什么区别?

  • 行内元素:a b span img input select strong
  • 块级元素:div ul ol li dl dt dd h1 h2 h3... p
  • 空元素: br hr img input link meta
  • 行内元素不可以设置宽高,不独占一行
  • 块级元素可以设置宽高,独占一行

8. HTML全局属性有哪些?

  • class:为元素设置类识别
  • data-* :微元素增加自定义属性
  • draggable:设置元素是否可拖拽
  • id:元素id,文档内唯一
  • lang:元素内容的语言
  • style:行内css样式
  • title:元素相关的建议信息

9. viewport

    <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale="1.0",user-scalable="true">
    // width 设置viewport宽度,为一个正整数,或字符串"device-width"
    // device-width:设备宽度
    // height 设置viewport高度,一般设置了宽度,会自动解析出高度,可以不用设置
    // minimum-scale 允许用户最小缩放比例,为数字,可带小数
    // maximum-scale 允许用户最大缩放比例,为数字,可带小数
    // user-scalable 是否允许手动缩放

三、CSS

1. 如何创建BFC(块级格式化上下文)?BFC有什么用?

  • 容器里面的子元素不会在布局上影响到外面的元素(只要脱离文档流,肯定就会产生BFC)
  • 产生BFC的方式:
    • 浮动元素(float值不为none)
    • 绝对定位元素(position值不为relative和static)
    • display取值为inlin-block、tabel-cell、table-caption、flex、inline-flex之一的元素
    • overflow值不为visible的元素
  • 作用:
    • 可以包含浮动元素(清除内部浮动,原理是两个div都位于同一个BFC区域之中)
    • 阻止元素被浮动元素覆盖
    • 阻止父子元素的margin重叠

2. 重点:清除浮动的几种方式,各自的优缺点

高度塌陷:当所有的子元素浮动的时候,且父元素没有设置高度,这时父元素就会产生高度塌陷。

  • 清除浮动方式1:给父级div定义height

    • 优点:快速简单,代码少;缺点:无法进行响应式布局
  • 清除浮动方式2:给父级div定义overflow:hidden; zoom:1(针对ie6的兼容)

    • 优点:简单快速、代码少,兼容性较高;缺点:超出部分被隐藏,布局时要注意
  • 清除浮动方式3:在浮动元素后面加一个空divclear:both; height:0; overflow:hidden;

    • 优点:简单快速、代码少,兼容性较高;缺点:增加空标签,不利于页面优化
  • 清除浮动方式4:父级定义overflow:auto;

    • 优点:简单,代码少,兼容性好;缺点:内部宽高超过父级div时,会出现滚动条
  • 清除浮动方式5:万能清除法。给塌陷的父级div添加伪对象after和zoom

    • 优点:写法固定,兼容性高;缺点:代码多
.father .clearfix:after {
    content: ".";
    display: block;
    height: 0;
    visibility: hidden;
    overflow:hidden;
    clear: both;
}
.clearfix {
    *zoom: 1;
}
  • 结尾处加br标签 clear:both

3. css3有哪些新特性?

  • 新增各种css选择器
  • 圆角border-radius
  • 多列布局
  • 阴影和反射
  • 文字特效text-shadow
  • 线性渐变
  • 旋转transform

4. 介绍下盒模型

  • 盒模型:内容(content)、填充(padding)、边界(margin)、边框(border)

标准盒模型 box-sizing:content-box; 默认值,总宽度=margin + border + padding + width(width不包括padding和border)

怪异盒模型 box-sizing:border-box; 盒子宽度包含padding和border,总宽度= margin + width (width包括padding和border)

5. CSS优先级算法如何计算?

  • 就近原则,同权重情况下样式定义最近者为准
  • 后写的优先级高(层叠性)
  • 优先级为:!important>行内样式>#id>.class>tag>*>继承>默认;

6. 重点:position的值的定位原点是什么?

  • relative:相对定位,不脱离文档流,相对于自身定位;
  • absolute:绝对定位,脱离文档流,相对于父级定位;
  • fixed: 固定定位,脱离文档流,相对于浏览器窗口定位;
  • static:默认值,没有定位
  • inherit: 规定从父元素继承position属性的值

7. 伪类和伪元素区别

  • 伪类表示状态::
  • 伪元素是真的元素 :

8. 什么是外边距重叠?

  • 就是margin-collapse
  • 在CSS中,相邻的两个div(可能是兄弟或父子)的外边距可以结合成一个单独的外边距。
  • 连个相邻的外边距都为正数时,取两者之间的较大值;都为负数时,取两者绝救治的较大值;一正一负时,取两者的和

9. CSS中可以让文字在垂直和水平方向上重叠的两个属性是什么?

  • 垂直 line-height
  • 水平 letter-spacing

10.子元素如何在父元素中居中?

  • 水平居中:
    • 子元素宽度固定,子元素设置margin:auto,并且子元素不能设置浮动,否则居中失效;
    • 子元素宽度固定,父元素设置text-align:center,子元素设置display:inline-block,并且子元素不能设置浮动,否则居中失效;
  • 垂直居中:
    • 设置子元素和父元素的行高line-height相等
    • 子元素设置为行内块inline-block,在加vertical-align:middle;
    • 已知父元素高度,子元素相对定位,通过transform:translateY(-50%);
    • 给父元素设置display:table,子元素设置display:table-cell; vertical-align:middle
    • 弹性盒,父元素display:flex; 子元素设置align-self:center
  • 水平垂直居中:
    • 子元素相对于父元素绝对定位,子元素top、left设置50%,子元素margin-top和margin-left减去各自宽高的一半;
    • 子元素相对于父元素绝对定位,子元素上下左右全为0,然后子元素设置margin:auto;
    • 子元素相对定位,子元素top、left值为50%,transform:translate(-50%, -50%);
    • 子元素相对于父元素绝对定位,子元素top、left值为50%,transform:translate(-50%, -50%);
    • 父元素设置弹性盒子,display:flex; justify-content:center; align-item: center;

11.水平居中的方法

  • 元素为行内元素,设置父元素 text-align:center
  • 元素为块级元素,宽度固定,设置左右margin:0 auto
  • 元素为绝对定位,设置父元素postion:relative,元素设置left:0; right:0; margin:auto;
  • 使用flex-box布局,指定jusctify-content: center
  • display: table-cell
  • absolute + transform

12. 垂直居中的方法

  • display: table-cell; vertical-align: center
  • flex布局,align-item: center
  • 绝对定位设置bottom: 0; top: 0; 并设置margin: auto;
  • 绝对定位固定高度时设置 top: 50%;margin-top 值为高度一半的负值
  • 文本垂直居中设置 line-height为height值
  • absolute + transform

13.css选择器有哪些?哪些属性可以继承,优先级如何计算?CSS3新增的伪类有哪些?

  • CSS2选择器:元素选择器, id选择器, 群组选择器,类选择器, * 通配符选择器, 后代选择器, 伪类选择器a:link/visited/hover/active
  • CSS3选择器:
    • 空格 > +相邻兄弟选择器 ~通用选择器(查找后面所有)
    • 结构伪类选择器:查找第几个 nth-child(n);
  • CSS继承性主要是文本方面: 所有元素可继承: visibility和cursor

块级元素可继承:text-indent和text-align

列表元素可继承:list-style

内联元素可继承:letter-spacing, wordspacing, line-height, color, font相关属性, text-decoration, text-transform, direction

14.margin和padding在什么场合下使用?

margin外边距,自身边框到另一个边框之间的距离;

padding内边距,自身边框到自身内容之间的距离;

当需要在border外侧添加空白时用margin,当需要在border内测添加空白时用padding

15.弹性盒子布局常用属性简述?

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

flex-direction:弹性容器中子元素排列方式(主轴排列方式)

flex-wrap:设置弹性盒子的子元素超出父容器时是否换行

flex-flow:是flex-direction 和 flex-wrap简写形式

align-item:设置弹性盒子元素在侧轴上的对齐方式

justigy-content:设置弹性盒子在主轴上的对齐方式

16. px、rem、em的区别

px绝对长度单位,相对于屏幕分辨率来说

em相对长度单位,继承入级元素的字体大小(参照物是父元素的font-size)

rem相对于html根元素的font-size(缺点:屏幕越小的移动设备,如果用了rem文字肯定越小,导致看文章看不清)

四、JS部分

1.闭包

函数嵌套函数,内部函数就是闭包

正常情况下,函数执行完毕,内部变量会销毁(释放内存空间)。而闭包在内部函数执行完成,外部函数变量不会销毁。

function outerFun() {
            let a = 10;

            function innerFun() {
                console.log(a);
            }
            return innerFun;
        }
        outerFun()() //10

应用:封装一段代码,实现模块化

  let module = (function () {
            let a = 10;
            let b = 20;

            function add() {
                return a + b
            }

            function sub() {
                return a - b
            }
            return {
                add,
                sub
            }
        })()
        console.log(module.add(), module.sub()); //30 -10

2.防抖与节流(防止函数多次调用。防抖:多次执行变为最后一次执行;节流:多次执行变为每隔一段时间执行)

防抖:用户触发事件过于频繁,只要最后一次事件的操作 作用:防止重复调用,降低性能

场景:用户输入,只需要在输入完成后做一次输入校验即可。

原理:需要一个定时器辅助实现,延迟执行需要执行的代码,如果方法多次触发,把上次记录的延迟执行代码用cleartimeout清除掉,重新开始,如果计时完毕,没有方法来访问触发,则执行代码。

<input type="text">
    <script>
    // 通过加定时器的方式,在一定的时间间隔500ms内,将多次触发变成一次触发。
        let inp = document.querySelector('input') 
        let t = null
        inp.oninput = function () {
            if (t !== null) {
                clearTimeout(t)
            }
            t = setTimeout(() => {
                console.log(this.value);
            }, 500)
        }
    </script>
 
        inp.oninput = debounce(function () {
            console.log(this.value)
        }, 500)
// 使用闭包封装防抖函数
        function debounce(fn, delay) {
            let t = null
            return function () {
                if (t !== null) {
                    clearTimeout(t)
                }
                t = setTimeout(() => {
                    fn.call(this) //改变fn中this的指向,使其指向input,而不是window
                }, delay)
            }
        }

节流:控制执行次数

作用:控制高频事件执行次数。每隔一段时间后执行一次,也就是降低频率,将高频操作优化为低频操作。

使用场景:滚动条事件或resize事件,通常每隔100~500ms执行一次即可。

原理:声明一个变量当标志位,记录当前代码是否在执行,如果正在执行,取消这次方法执行,直接return,如果空闲,正常触发方法执行。

        window.onscroll = throttle(function () {
            console.log('hello word')
        }, 500)
        
        // 利用闭包封装节流函数
        function throttle(fn, delay) {
            let flag = true
            return function () {
                if (flag) {
                    setTimeout(() => {
                        fn.call(This)
                        flag = true
                    }, delay)
                }
                flag = false
            }
        }

3.回调地狱

es5使用回调函数拿取异步数据,会造成回调地狱

 function getTea(fn) {
            setTimeout(() => {
                fn("喝奶茶")
            }, 800)
        }

        function getHotPot(fn) {
            setTimeout(() => {
                fn("吃火锅")
            }, 1000)
        }

        function getSing(fn) {
            setTimeout(() => {
                fn("去唱歌")
            }, 500)
        }

        function getGame(fn) {
            setTimeout(() => {
                fn("玩游戏")
            }, 700)
        }
        // 回调地狱
        getGame(function (data) {
            console.log(data);
            getSing(function (data) {
                console.log(data);
                getHotPot(function (data) {
                    console.log(data);
                    getTea(function (data) {
                        console.log(data);
                    })
                })
            })
        })
        // 玩游戏,去唱歌,吃火锅,喝奶茶

es6利用promise对象拿取异步数据,解决回调地狱

// 利用promise解决回调地狱
        function getTea() {
            return new Promise(function (resolve) {
                setTimeout(() => {
                    resolve("喝奶茶")
                }, 2000)
            })
        }

        function getHotPot() {
            return new Promise(function (resolve) {
                setTimeout(() => {
                    resolve("吃火锅")
                }, 1000)
            })
        }

        function getSing() {
            return new Promise(function (resolve) {
                setTimeout(() => {
                    resolve("去唱歌")
                }, 500)
            })
        }

        function getGame() {
            return new Promise(function (resolve) {
                setTimeout(() => {
                    resolve("玩游戏")
                }, 300)
            })
        }
          
        getHotPot().then(function (data) {
            console.log(data);
            return getTea()
        }).then(function (data) {
            console.log(data);
            return getSing()
        }).then(function (data) {
            console.log(data);
            return getGame()
        }).then(function (data) {
            console.log(data);
        })
        // 吃火锅,喝奶茶,玩游戏,去唱歌,

利用async/await函数直接拿取异步数据,解决回调地狱

function getTea() {
            return new Promise(function (resolve) {
                setTimeout(() => {
                    resolve("喝奶茶")
                }, 2000)
            })
        }

        function getHotPot() {
            return new Promise(function (resolve) {
                setTimeout(() => {
                    resolve("吃火锅")
                }, 1000)
            })
        }

        function getSing() {
            return new Promise(function (resolve) {
                setTimeout(() => {
                    resolve("去唱歌")
                }, 500)
            })
        }

        function getGame() {
            return new Promise(function (resolve) {
                setTimeout(() => {
                    resolve("玩游戏")
                }, 300)
            })
        }


        // async/await函数
        async function getData() {
            // 直接获取resolve传递出来的异步数据
            let hotPot = await getHotPot()
            console.log(hotPot);
            let tea = await getTea()
            console.log(tea);
            let sing = await getSing()
            console.log(sing);
            let game = await getGame()
            console.log(game);
        }
        getData()
        // 吃火锅,喝奶茶,玩游戏,去唱歌,

4. 原型

什么是原型?

原型prototype:一个简单的对象,用于实现对象的属性继承。可以简单的理解为对象的爹。每个obj都包含一个__proto__的属性指向它爹(给对象的原型),可以obj.__proto__进行访问。 构造函数:可以通过new来新建一个对象的函数 实例:通过构造函数和new 创建出来的对象,便是实例。实例通过__proto__指向原型,通过constructor指向构造函数

  • 实例.proto === 原型
  • 原型.constructor === 构造函数
  • 构造函数.prototype === 原型
  // 什么是原型?
        // 每一个对象都有它的原型对象prototype,它可以使用自已原型对象上的所有的属性和方法
        // 获取原型的方法1: 通过对象的__proto__获取
        // let cat = {
        //     name: "汤姆"
        // }
        // cat.__proto__.eat = function () {
        //     console.log("吃鱼");
        // }
        // cat.eat() // 吃鱼

        // 获取原型的方法2: 通过构造函数的prototype属性获取
        // 构造函数中的this指向谁?指向new创建的对象
        function Cat(name, age) {
            this.name = name,
                this.age = age
        }
        let cat = new Cat("汤姆猫", 3)
        Cat.prototype.eat = function () {
            console.log("爱吃鱼");
        }
        cat.eat()  //爱吃鱼
  • 构造函数和普通函数的区别:
    • 构造函数也是一个普通函数,创建方式和普通函数一样,但是构造函数习惯上首字母大写;
    • 调用方式不一样,普通函数可以直接调用,构造函数要用关键字new来调用;
    • 调用时,构造函数内部会创建一个新对象,就是实例,普通函数不会创建新对象;
    • 构造函数内部的this指向实例,普通函数内部的this指向调用函数的对象(如果没有对象调用,默认为window)
    • 构造函数默认的返回值是创建的对象(也就是实例),普通函数的返回值由return语句决定
    • 构造函数的函数名与类名相同 原型对象有什么用?
 //    原型对象有什么用? 扩展对象
        let date = new Date()
        Date.prototype.formate = function () {
            let year = this.getFullYear()
            let month = this.getMonth() + 1
            let date = this.getDate()
            return `${year}${month}${date}日`
        }
        // 目标:输出xx年xx月xx日
        console.log(date.formate());

ES6的类

        // ES6类的基本语法
        // 获取原型的方法3: 通过类的prototype属性获取
        class Cat {
            constructor(name, age) {
                this.name = name
                this.age = age
            }
        }
        Cat.prototype.eat = function () {
            console.log("爱吃鲱鱼");
        }
        let cat = new Cat("Tom", 1)
        cat.eat() //爱吃鲱鱼

ES6的继承

  class User {
            constructor(username, password) {
                this.username = username
                this.password = password
            }
            login() {
                console.log("登录");
            }
        }
        //继承
        class Admin extends User {
            deletePerson() {
                console.log("删除一个人");
            }
        }
        let admin = new Admin()
        admin.login() //登录
        admin.deletePerson() //删除一个人   

ES5的继承

  • 原型链的核心就是依赖对象的__proto__的指向,当自身不存在某属性时,就一层层地扒出创建对象的构造函数,直到Object为止
 function User(username, password) {
            this.username = username
            this.password = password
            this.login = function () {
                console.log("登录");
            }
        }
        // 原型链 prototype---prototype---...---Object
        // 最顶层,所有对象的原型方法都可以在Object上面拿到
        Object.prototype.top = function () {
            console.log("Object原型上的top方法");
        }

        function Admin() {
            this.deletePerson = function () {
                console.log("删除一个人");
            }
        }
        // 继承
        Admin.prototype = new User()
        let admin = new Admin()
        admin.login() //登录
        admin.deletePerson() //删除一个人
        admin.top() //Object原型上的top方法
        
        function Parent(){
            this.name = 'wang'
        }
        function Child(){
            this.age = 28
        }
        Child.prototype = new Parent() //通过原型,继承了Parent
        var demo = new Child()
        console.log(demo.name) //得到被继承的属性

5.作用域链是什么?原型链又是什么?

作用域链的变量只能向上访问,访问到window对象即终止,向下访问变量是不被允许的。

每个函数都有prototype属性,该属性指向原型。每个对象都有__proto__属性,指向了创建该对象的构造函数的原型。对象可以通过__proto__来寻找不属于该对象的属性,__proto__将对象连接起来组成了原型链

6.事件委托(事件代理)是什么?

是js中绑定事件的常用技巧。把原来需要绑定的事件委托给父元素,让父元素进行事件监听。原理是DOM元素的事件冒泡。

好处是可以提高性能,大量节省内存占用,减少事件注册。

7. 谈谈this对象的理解?

this总是指向函数的直接调用者; 如果有new关键字,this指向new出来的那个对象; 在事件中,this指向触发这个事件的对象;

8.Ajax原理是什么?

  • 简单来说是在用户和服务器之间加了一个中间层(Ajax引擎),通过XMLHttpRequest对象(核心机制)向服务器发异步请求,从服务器获得数据,然后用js来操作DOM更新页面。使用户操作与服务器响应异步化。
//1.创建连接
var xhr = null
xhr = new XMLHttpRequest()
//2.连接服务器
xhr.open('get', url, true)
//3.发送请求
xhr.send(null)
//4.接收请求
xhr.onreadystatechange = function(){
    if(xhr.readyState == 4){
        if(xhr.status == 200){
            success(xhr.responseText)
        } else {
            fail && fail(xhr.status)
        }
    }
}

优点:异步模式,提升了用户体验;优化了浏览器与服务器之间的传输,减少了不必要的数据返回,减少了带宽占用;在客户端运行,承担了一部分服务器承担的工作,减少了大用户量下的服务器负载;可以实现动态不刷新(局部刷新)

9.如何解决跨域问题?

为什么会出现跨域问题?在前后端分离的模式下,前后端的域名是不一致的,这就造成了跨域。

  • 浏览器同源策略(一种安全策略,没有同源策略,浏览器很容易受到XSS、CSFR等攻击): “协议+域名+端口”三者必须相同,有一个不同就是跨域,Ajax请求就会失败。即便两个不同的域名指向同一个IP地址,也非同源。

为什么要同源?举例说明:比如一个黑客,他利用Iframe把真正的银行登录页面嵌入到他的页面上,当你使用真实的用户名、密码登录时,他的页面就可以通过JS读取到你的表单中input中的内容,这样用户名、密码就轻松到手了。

  • 通过jsonp解决跨域(利用
var demo = document.createElement('demo')
demo.type = 'text/javascript'
//传参并指向回调执行函数为onBack
demo.src= 'http://www.xxx:8080/login?user=admin&callback=onBack'
document.head.appendChild(demo)

//回调执行函数
function onBack(res){
    alert(JSON.stringify(res))
}

<script src="http://xxx/api?param1=1&param2=b&callback=jsonp"></script>
<script>
    function jsonp(data){
        console.log(data)
    }
</script>

function jsonp(url, jsonpCallback, success){
    const script = document.createElement('script')
    script.src = url
    script.async = true
    script.type = 'text/javascript'
    window[jsonpCallback] = function(data){
        success && success(data)
    }
    document.body.appendChild(script)
}
  • nginx代理跨域
  • nodejs中间件代理跨域
  • CORS 需要浏览器和后端同时支持。实现CORS通信的关键是后端。服务器设置Access-Control-Allow-Origin:*,就可以开启CORS。
  • 后端在头部信息里面设置安全域名

10.模块化开发怎么做?

立即执行函数,不暴露私有成员

var module = (function(){
    var count = 0
    var m1 = function(){
        //...
    }
    var m2 = function(){
        //...
    }
    return{
        m1: m1,
        m2: m2
    }
})()

11.谈谈你对webpack的看法

是一个模块打包工具,可以使用webpack管理你的模块依赖,并编译输出模块们所需要的静态文件。能够很好地管理、打包web开发中所用到的HTML、JS、CSS以及各种静态文件(图片、字体等),让开发更高效。对于不同类型的资源,webpack有对应的模块加载器。webpack模块打包器会分析模块间的依赖关系,最后生成了优化且合并后的静态资源。

12.常见的web安全及防护原理

  • sql注入原理
    • 就是通过SQL命令插入到web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。 永远不要信任用户的输入,要对用户的输入进行校验,可以通过正则表达式或长度限制;永远不要使用动态拼装SQL,可以使用参数化的SQL或直接使用存储过程进行数据查询存取;永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接;不要把机密信息铭文存放,请加密或hash掉密码和敏感信息。
  • XSS原理及防范(注入恶意代码)
    • XSS攻击指攻击者往web页面里插入恶意html标签或者js代码。 比如:攻击者在论坛中放一个看似安全的链接,骗取用户点击后,窃取cookie中的用户私密信息;或者攻击者在论坛中加一个恶意表单,当用户提交表单时,却把信息传送到攻击者的服务器中,而不是用户原本以为的信任站点。
    • 防范方法 首先代码里对用户输入的地方和变量都需要检查长度和对<>;,等字符做过滤;其次任何内容写到页面之前都需要加encode,避免不小心把html tag弄出来;cookie设置httpOnly;转义页面上的输入内容和输出内容
  • XSS与CSRF(跨站请求伪造)有什么区别?
    • XSS是获取信息,不需要提前知道其他用户页面的代码和数据包;CSRF是代替用户完成指定的动作,需要知道其他用户页面的代码和数据包。要完成一个CSRF攻击,受害者必须依次完成两个步骤:1.登录受信任网站A,并在本地生成cookie;2.在不登出A的情况下,访问危险网站B。 CSRF防御策略:在客户端页面增加伪随机数;通过验证码的方法;不被第三方网站访问到用户的cookie;设置白名单,不被第三方网站请求;请求校验

13. JS中有哪些方法定义对象

  • 对象字面量: var obj = {}
  • 构造函数: var obj = new Object()
  • Object.create(): var obj = Object.create(Object.protorype)

14.谈谈Promise

承诺的意思,承诺过一段时间会给你一个结果。是一种异步解决编程的方案。相对es5的回调函数和事件更合理,更强大。

  • 构造一个Promise对象,最基本的用法如下:
var demo = new Promise(function(resolve,reject){
    if(...){
        resolve(result)  //successed
    }else{
        reject(Error(errorMessage)) //failed
    }
})
//promise实例拥有then方法
demo.then(onfulfilled,onRejected)

解决了回调地狱的问题(代码难以维护,常常第一个函数的输出是第二个函数的输入)。支持多并发的请求,获取并发请求中的数据。

15.介绍JS的基本数据类型

number,string,undefine,null,boolean

16.JSON的了解

  • JSON是一种轻量级的数据交换格式
  • 是基于JS的一个子集,数据格式简单,易于读写,占用带宽小
  • JSON字符串转为JSON对象
    • var obj = evel('(' + str + ')')
    • var obj = str.parseJSON()
    • var obj = JSON.parse(str)
  • JSON对象转换为JSON字符串
    • var last = obj.toJSONString()
    • var last = JSON.stringify(obj)

17.同步和异步的区别

  • 同步(阻塞):浏览器访问服务器请求,用户看得到页面刷新,重新发请求,等请求完,页面刷新,新内容出现,用户看到新内容,进行下一步操作。比如,咱俩一起上班,到吃饭时间了,我去喊你一起吃饭,你很忙,我就等着你忙完再一起去吃饭。
  • 异步(非阻塞):浏览器访问服务器请求,用户正常操作,浏览器后端进行请求。等请求完,页面不刷新,新内容也会出现,用户看到新内容。咱俩一起上班,到吃饭时间了,我去喊你一起吃饭,你很忙,我就先自己去吃了,你忙完了再去吃饭。

18.谈谈ES6

  • 模板字符串(为js提供了简单的字符串插值功能)
  • 箭头函数
  • for-of(用来遍历数据)
  • 异步解决方案:将promise对象纳入规范,提供了原生的Promise对象
  • let和const命令,let一般用来声明变量,const一般用来声明无法修改的常量(块级作用域、不存在变量提升、暂时性死去、不允许重复声明)
  • 引入module模块的概念(解决命名冲突;提升复用性,提高代码可维护性)
  • 解构赋值
//导出模块API
// a.js
export function a(){}
export function b(){}
// b.js
export derault function(){}

//引入模块API
import {a, b} from './a.js'
import xxx from './b.js'
  • arguments 对象可被不定参数和默认参数完美代替

19.什么是面向对象编程及面向过程编程,他们的异同和优缺点

  • 面向过程就是分析出解决问题所需的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了
  • 面向对象是把构成问题事物分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描述某个事物在整个解决问题的步骤中的行为。面向对象是以功能来划分问题,而不是步骤。

20. 面向对象编程思想

  • 将功能通过对象来实现,将功能封装进对象之中,让对象去实现具体的细节
  • 特点:封装(隐藏对象的属性和实现细节,对外提供公共访问方式)、继承(提高代码复用性,继承是多态的前提)、多态(是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象)
  • 优点
    • 易维护
      • 可读性高,由于继承的存在,及时改变需求,name维护也只是在局部模块,所以维护起来是非常方便和节约成本的
    • 易扩展
    • 可发工作的重用性、继承性高,降低重复工作量
    • 缩短了开发周期

21.map与forEach的区别

  • forEach是最基本的方法,就是遍历与循环,默认3个参数:遍历数组的内容value、数组索引index、和当前遍历数组Array。无法遍历对象。无法使用break,coninue跳出循环,并且使用return跳出本次循环。
  • map与forEach基本一直,但不同的是,会返回一个新的数组,所以在callback需要return值,如果没有,会返回undefined

22. 谈一谈箭头函数

  • 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象
  • 不可以当做构造函数,也就是说,不可以使用new命令,否则会报错
  • 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替
  • 不可以使用yield命令,因此不能用作Generator函数

23.重点:谈一谈this指向

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

要明白this的指向,就要搞清楚函数的运行环境。说人话就是,谁调用了函数。例如obj.fn(),便是obj调用了函数,即函数中的this === obj;fn(),这里可以看成window.fn(),因此 this === window。 this,函数执行上下文,可以通过apply、call、bind改变this指向。

全局的this -> 指向window

对象中的this -> 指向其本身

事件中的this -> 指向事件对象

当然es6的箭头函数其实是没有this的,只取决于它外面的第一个不是箭头函数的函数的this。

24.异步编程的实现方式

  • 回调函数
    • 优点:简单、易于理解
    • 缺点:不利于维护,代码耦合度高
  • 事件监听(采用时间驱动模式,取决于某个事件是否发生)
    • 优点:容易理解,可以绑定多个事件,每个事件可以指定多个回调函数
    • 缺点:事件驱动型,流程不够清晰
  • 发布/订阅(观察者模式)-->Vue,通过Object.defineProperty()来劫持各个属性的stter、getter,在数据变动时发布消息给订阅者,触发相应的监听回调
    • 类似于事件监听,但是可以通过“消息中心”,了解现在有多少发布者,订阅者
  • Promise对象
    • 优点:可以利用then方法,进行链式写法;可以书写错误时的回调函数;
    • 缺点:编写和理解,相对较难
  • Genorator函数
    • 优点:函数体内外的数据交换、错误处理机制
    • 缺点: 流程管理不方便
  • async函数
    • 函数前必须加一个async,异步操作方法前加一个await关键字,意思就是等一下,执行完了再继续走。注意:await只能在async函数中运行,否则会报错。
    • 优点:内置执行器、更好的语义、更广的适用性、返回的是Promise、结构清晰
    • 缺点:错误处理机制。promise如果返回一个错误的结果,如果没有做异常处理,就会报错,所以用try...catch捕获一下异常就可以了。

25.对原生js了解程度

  • 数据类型、运算、对象、Function、继承、闭包、作用域、原型链、事件、RegExp(正则)、JSON、Ajax、DOM、BOM、内存泄漏、跨域、异步加载、模板引擎、前端MVC、路由、模块化、Canvas、ECMAScript

26.怎么添加、移除、移动、复制、创建和查找节点

  • 创建新节点
    • createDocumentFragment() //创建一个DOM片段
    • createElement() //创建一个具体的元素
    • createTextNode() //创建一个文本节点
  • 添加、移除、替换、插入
    • appendChild() //添加
    • removeChild() //移除
    • replaceChild() //替换
    • insertBefore() //插入
  • 查找
    • getElementsByTagName() //通过标签名称
    • getElementsByName() //通过元素Name属性的值
    • getElementById() //通过元素Id,唯一性

27.重点:掌握两种以上的数组去重方法

  • 方法一:ES6中的set去重 ES6中Set不接受重复数据,成员值都是唯一的,可以用来去重(简单);set与扩展运算符结合,简化代码
function unique(arr){
    return Array.from(new Set(arr))
}

等同于[...new Set(arr)]

function unique(arr){
    return [...new Set(arr)]
}
简化:
let unique = (arr) => [...new Set(arr)]
  • 方法二:双层for循环去重
    • 遍历原始数组,将原始数组中的每一个元素与新数组中的每个元素进行对比,如果不重复则添加到新数组中,最后返回新数组
function unique(arr){
    const newArr = []
    for(let i=0; len=arr.length; i<len; i++){
        for(let j=i+1; j<len; j++){
            if(arr[i] == arr[j]){
                j = ++i
            }
        }
        newArr.push(this.[i])
    }
    return arr
}
  • 方法三:Array.filter()与indexOf()
    • 如果索引不是第一个索引,说明是重复值(indexOf:元素在数组中第一次出现的位置)
        function unique(arr) {
            return arr.filter((item, index) => {
                return arr.indexOf(item) === index
            })
        }
  • 方法四: Array.prototype.sort()
    • 先对原数组进行排序,然后再进行元素比较

28.深浅拷贝

let a = {age:1}
let b = a
a.age = 2
console.log(b.age) // 2

浅拷贝1
let a = {age:1}
let b = Object.assign({}, a)
a.age = 2 
console.log(b.age) //1
浅拷贝2
let a = {age:1}
let b = {...a}
a.age = 2
console.log(b.age) //1
  • 浅拷贝(以赋值的形式拷贝引用对象。只能解决第一层的问题)
    • Object.assign
    • 扩展运算符...
  • 深拷贝(完全拷贝一个新对象)
    • JSON.parse(JSON.stringify(object))
  • 局限性:不能拷贝函数、undefined、symbol。如果遇到这些问题,可以考虑使用lodash中的深拷贝函数
//浅拷贝
let a = {
    age: 1,
    jobs: {
        first: 'FE'
    }
}
let b = {...a}
a.jobs.first = 'native'
console.log(b.jobs.first) //native

// 深拷贝
let a = {
    age: 1,
    jobs: {
        first: 'FE'
    }
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) //FE

29.谈谈axios

axios({
    method: 'post',
    url: '/user/12345',
    data: {
        item1: 'aaa',
        item2: 'bbb'
    }
})
.then(function(response){
    console.log(response)
})
.catch(function(error){
    console.log(error)
})

优缺点:

  • 从浏览器中创建XMLHttpRequest
  • 从node.js发出http请求
  • 支持 Promise API
  • 拦截请求和响应
  • 转换请求和响应数据
  • 取消请求
  • 自动转换JSON数据
  • 客户端支持防止CSRF/XSRF

30.什么样的前端代码是好的?

  • 高复用低耦合,这样文件小、好维护,而且好扩展。
  • 具有可用性、健壮性、可靠性、宽容性
  • 遵循设计模式的六大原则

31.谈谈cookie、localStorage、sessionStorage、indexDB

  • cookie(通常用于保存登录信息,比如你登录某个网站时经常可以看到“记住密码”):一般由服务器生成可以设置过期时间;如果在浏览器端生成cookie,默认是关闭浏览器后失效;数据存储大小4K;与服务器端通信(每次都会携带在header中,对于请求性能有影响,也存在安全隐患);
  • localStorage:(常用于保存userData)长久储存,除非被清理,否则一直存在;数据存储大小5M;与服务端通信(不参与);
  • sessionStorage:刷新页面数据依然存在,但关闭页面数据就会被清空;数据存储大小5M;与服务端通信(不参与);
  • indexDB:除非被清理,否则一直存在;数据存储大小无限;与服务端通信(不参与)

32.谈谈影响浏览器性能问题的重绘和回流?

当元素的样式发生变化时,浏览器需要触发更新,重新绘制元素。这个过程中,有两种类型的操作,即重绘与回流。

  • 重绘:当元素样式的改变不影响布局时,浏览器将使用重绘对元素进行更新,此时由于只需要UI层面的重新像素绘制,因此损耗较少。比如改变color
  • 回流:当元素的尺寸、结构或触发某些属性时,浏览器会重新渲染页面,成为回流。此时,浏览器需要重新经过计算,计算后还需要重新页面布局,因此是较重的操作,会触发回流的操作。
  • 回流必定会触发重绘,重绘不一定触发回流。重绘的开销较小,回流的代价较高。
  • 以下几个动作可能会导致性能问题:页面初次渲染;改变浏览器窗口window大小;元素尺寸、位置、内容发生改变;元素字体大小变化;添加或删除可见的DOM元素及样式;激活CSS伪类(如:hover),查询某些属性或调用某些方法(clientWidth、clientHeight、clientTop、clientLeft、scrollTo...)
  • 减少重绘和回流:
    • css
      • 避免使用table布局
      • 将动画效果应用到postion属性为absolute或fixed的元素上
    • js
      • 避免频繁操作样式,可汇总后统一一次修改
      • 尽量使用class进行样式修改
      • 减少dom的增删改查,可使用字符串或documentFragment一次性插入
      • 极限优化时,修改样式可将其display:none后修改
      • 避免多次触发上面提到的那些会触发回流的方法,可以的话尽量用变量存储

33.数组(array)

  • map:遍历数组,返回回调返回组组成的新数组
  • forEach:无法break,可以用try/catch中throw new Error 来停止
  • filter:过滤数组,返回一个满足要求的数组
  • some:有一项返回true,则整体为true
  • every:有一项返回false,则整体为false
  • join:通过指定连接符生成字符串
  • push/pop:末尾追加或删除,改变原数组,返回操作项
  • unshift/shift:头部追加或删除,改变原数组
  • sort(fn)/reverse:排序与反转,改变原数组
  • concat:连接数组,不影响原数组,浅拷贝
  • slice(start,end):返回截断后的新数组,不改变原数组
  • splice(start,number,value...):返回删除元素组成的数组,value为插入值,改变原数组
  • indexOf/lastIndexOf(value,fromIndex):查找数组项,返回对应的下标
  • reduce/reduceRight(fn(prev,cur), defaultPrev):两两执行,prev为上次化简函数的return值,cur为当前值(从第二项开始)
  • str.split()将字符串转化为数组

34.http协议和https协议的区别

  • http是超文本传输协议,信息是明文传输;而https是具有安全性的ssl解密传输协议。

  • http和https连接方式完全不同,端口也不同,http是80,https是443。

  • http的连接方式很简单,是无状态的,而https协议是由ssl+http协议构成的,可进行加密传输,身份认证的网络协议,比http协议安全。