vue面试必备,笔记(祝您面试成功,记得收藏)

一.vue2 部分面试题

1.生命周期函数

1.1 什么是生命周期函数

所谓的 vue ⽣命周期就是 vue 实例从创建到销毁的整个过程我们称之为 vue 的 ⽣命周期,通过 vue 的⽣命周期我们可以在不同的阶段进⾏不同的逻辑操作. vue ⽣命周期常⽤的钩⼦函数⼀共有 8 个,分别是创 建前后、挂载前后、更新前后以及销毁前后. 分别对应的钩⼦函数为 beforeCreate 创建前、 created 创建后、beforeMount 挂载 前、mounted 挂载后、beforeUpdate 更新前、updated 更新后、beforeDestory 销毁前、 destoryed 销毁后,

除了以上八个 还有三个 activated 激活 deactivated 停用 以上两个跟 keep-alive 有关的 组件的激活和组件的停用 errorCapture 子组件加载错误时触发

1.2 用的最多的是 created

用它来发送 http 请求 还可以获取本地存储里的数据

1.3 created 和 mounted 的区别

created 执行的时机更早 mounted 可以获取到 dom 元素

1.4 如果想在 created 中获取到 dom 元素

使用 nextTick

1.5. 父子组件执行的顺序

父组件 beforeCreate 父组件 created 父组件 beforeMount 子组件 beforeCreate 子组件 created 子组件 beforeMount 子组件 mounted 父组件 mounted

2.nextTick

在 dom 更新之后执行的异步回调 在 vue 中 dom 更新是异步 如果在 dom 还没更新的时候我们想要拿到 dom 节点来用的时候 就要把 代码放在 nextTick 中

3.vuex

3.1 什么是 vuex

vue 的状态管理工具 管理公共数据 放在 vuex 里的数据 能够被所有的组件共享

3.2 vuex 的五大核心 以及 如何调用

state 状态 this.$store.state 调用的时候会通过计算属性computed 把state return出去再用 mutations 方法 this.$store.commit actions 异步操作 this.$store.dispatch getters 相当于是计算属性 this.$store.getters modules 模块化

3.3 vuex 的辅助函数 是 vuex 的语法糖

mapState 展开在计算属性里 mapGetters 展开在计算属性里 mapMutation 展开在方法里 mapAction 展开在方法里

用的时候都先 import 引入

import { mapState, mapMutations } from "vuex";
​
computed: {
    // name() {
    //   return this.$store.state.name;
    // },
​
    ...mapState(["name", "age"]),
  },
  methods: {
    // change() {
    //   this.$store.commit("change")
    // },
    ...mapMutations(["change"]),
    //调用mutation修改name
  },

3.4 modules 怎么使用

当 vuex 里数据很多的时候我们可以分模块管理数据 建立不同的 js 文件 作为我们的模块文件 每一个模块里都有 state 等其他的四大核心 把这个 js 文件引入到 store/index.js 中 在 modules 中注册

使用模块里的数据的时候 this.$store.state.xx(模块名字).arr(state 数据名字)

如果使用模块里的 mutations 方法 首先在模块里跟 state 同级的地方开启一个命名空间 namespaced:true 使用的时候 this.$store.commit("a(模块名字)/add(模块里方法的名字)")

4.路由的两种模式

⾯试官您好,接下来我给您介绍⼀下 vue 的路由模式,vue 的路由模式⼀共有两种,分别是哈希和 history. 他们的区别是 hash 模式不 会包含在 http 请求当中,并且 hash 不会重新加载⻚⾯,⽽使⽤ history 模式的话,如果前端的 url 和后端发起请求的 url 不⼀致的话,会 报 404 错误,所以使⽤ history 模块的话我们需要和后端进⾏配合. history 的原理就是利⽤ html5 新增的两个特性⽅法,分别是 pushState 和 replaceState 来完成的. 以上就是我对 vue 路由模式的理解

5.路由的导航守卫

三种 7 个 全局 beforeEach 前置守卫 beforeResolve 路由解析守卫 afterEach 后置守卫

组件级守卫 beforeRouteEnter 路由进入之前 beforeRouteUpdate 路由更新之前 beforeRouteLeave 路由离开之前

单个路由独享守卫 beforEnter 路由进入之前

5.1 beforEach 和 beforeResolve 的区别

都是在路由跳转之前执行 但是 beforeEach 是在解析路由规则之前 执行 beforeResolve 是解析之后 路由跳转之前执行 beforeEach 比 beforeResolve 执行的时机更早

5.2 路由守卫的执行顺序

全局前置 独享守卫 组件守卫 解析守卫 后置守卫

5.3 要进行是否登陆的守卫

如果守卫所有页面 会出现一个死循环的问题 再加一层判断 如果是登陆页面 就 next 不是才跳转 next 组件级守卫 有一个问题就是 拿不到 this 解决方法是 next 接收一个回调函数 这个回调函数的第一个参数 vm 就是组件实例就相当于是 this

6.v-if 与 v-show 的区别

⾯试官您好,接下来我给您介绍⼀下 v-if 和 v-show 的区别? ⾸先 v-if 和 v-show 都是控制元素的显示与隐藏, 不过 v-if 控制元素的显示和隐藏的时候会删除对⽤的 dom 元素,当每⼀个显示的时候,都会重新创建 dom 和渲染. ⽽ v-show 则是通过 css 的 display:none 和 display:block 来控制元素的显示与隐藏. v-if ⽐较耗费性能,所以我们涉及到频繁的显示隐藏操作我们建议使⽤ v-show,如果不 是频繁操作的话,我们可以 v-if 在项⽬中我会经常使⽤ v-if 和 v-show,⽐如我们在搜索功能的时候,他有⼀个历史记录,这个时候我们根据是否有搜索的结果来判 断历史记录的显示与隐藏,这块我就可以使⽤ v-if ,当然⽤ v-show 也可以. 以上就是我对 v-if 和 v-show 的理解

7.v-for 与 v-if 的优先级那个⾼?如果同时使⽤ v-for 和 v-if 怎么解决?

v-for 的优先级⾼. 因为 v-for 的时候我们才开始渲染 dom 元素,这个 v-if 还⽆法进⾏判断. v-for 和 v-if 不能同时使⽤,我们可以通过标签,⽐如 div 或者 template 标签来进⾏包裹,把 v-if 写到包裹的标签上⾯(写到 v-for 外⾯)

在 vue3 中 v-if 的优先级高

8. methods、computed 和 watch 的区别?

⾸先呢,methods 是⽤来定义⽅法的区域,methods 定义的⽅法需要调⽤才能触发. 不具备缓存⾏ ⽽ computed 是计算属性,他依赖于属性值的变化,当属性发⽣改变的时候,计算属性⾥⾯定义的⽅法就会触发,computed 具有缓 存性,依赖属性值的变化⽽变化. ⽽ watch 主要是⽤于监听,不具备被缓存性.依赖于数据变化⽽触发. 在项⽬中,⽐如我们获取 state 的状态的时候我们会把它放到 computed ⾥⾯,或者在写购物⻋数量计算的时候也会使⽤计算属性. ⽽ watch 也在项⽬经常使⽤,⽐如我们封装编辑 和 新增弹窗组件的时候会通过 watch 来进⾏ id 判断我们要显否要清空表单的数 据. 以上就是我对 computed 和 watch 的理解. watch 有三个属性 deep 深度监听 immediate 进入页面立刻监听 handler 执行函数

我们平常用的就是对 handler 的简写

9. vuex 的运行机制

先在页面中通过 this.$store.dispatch 调用 action 再在 actions 中通过 commit 触发 mutations 方法

10.组件通信

父传子 在父组件中定义自定义属性传递变量 在子组件中通过 props 接收 在接收的时候 可以定义为数组 也可以定义为对象,定义为对象的话就能设置 数据类型和默认值 子传父 在父组件中通过自定义事件向子组件传传递事件 在子组件中 通过 this.$emit 调用父组件传来的方法 $emit 的第一个参数就是调用的事件的名字 第二个参数就是传的值

兄弟组件 在 main.js 中建立中央事件总线并挂载在 原型上 传数据的时候 this.$bus.$emit 传 接数据的时候是在 钩子函数 created 中 this.$bus.$on 接收 第一个参数是事件名称 第二个参数是一个回调函数 包含了 要接受的数据

vuex

本地存储

v-model 就相当于是 v-on:input 和 v-bind:value 的合写语法糖 本质也是组件传值

11. MVVM

MVVM 是把 MVC 的 Controller 和 MVP 的 Presenter 改成了 ViewModel 。 View 的变化会⾃动更新到 ViewModel , ViewModel 的变化也会⾃动同步到 View 上显示。这种⾃动 同步是因为 ViewModel 中的属性实现了观察者模式 ,当属性变更时都能触发对应的操作

vue 是一个 mvvm 模式的框架 m model 数据模型 v view 视图 vm ViewModel 调度者(控制器)

常见的 mvvm 的框架有哪些 vue 小程序 mvc anguler

12.vue 双向数据绑定原理?

vue.js 则是采⽤ 数据劫持 结合 发布者-订阅者 模式的⽅式, 通过 Object.defineProperty() 来劫持各个属性的 setter , getter , 在数据变动时发布消息给订阅者,触发相应的监听回调。 这个时候就可以实现数据的双向绑定

13. vue 常⽤的指令有哪些?

v-html 富文本 v-text 文本内容 v-model 双向数据绑定 v-on 绑定事件 v-bind 动态绑定 v-if v-show v-for

14.常用的修饰符

.trim 去除首位空格 .number 把字符串变成 nunmber 类型 .stop 阻止冒泡 .self 自身触发 .prevent 阻止默认事件 .native 事件穿透 .capture 变成捕获

15. vue 如何封装可复⽤的组件?以及请举例说明你封装过的组件?

  1. 抽离出相同的业务以及页面结构

  2. 在 components 文件夹下封装

  3. 引入注册作为标签使用

  4. 有些东西 需要固定写在子组件下 还有一些东西需要 通过组件通信和 slot 插槽进行灵活定义 这就是组件的封闭性和开放性

  5. 举一个封装子组件的例子

16.key 值的作用

避免 dom 元素重复渲染. 我⻔⼀般在设置 key 的时候⾸先尽量会设置为 id 加了 key 值之后 当渲染的列表发生改变的时候 新的虚拟 dom 和旧的进行对比的时候 会根据 key 值进行对比 如果没有 key 值 就按照 顺序进行对比 所以加了 key 值能够提高循环效率

17.keep-alive

keep 是一个内置组件 正常我们切换 vue 页面 页面会被销毁 如果加上 keep-alive 页面不会被销毁 会被缓存下来 一般是加在坑 router-view 的外面

用 keep-alive 可以缓存滚动条的距离,在路由离开之前 beforeRouteLeave 里面记录当前页面的 scrollTop 在 activated 中 把刚才记录的滚动条的位置赋值给当前页面的滚动条 还能记录 填入的表单的内容

可以使用 include 规定缓存谁 exclude 规定不缓存谁 可以直接使用组件的 name 值 也可以使用正则表达式来规定缓存规则 meta 信息 这三种方式来规定缓存谁不缓存谁

18.组件传值的八种方式

  1. 父传子

  • 首先在使用子组件的标签上 通过自定义属性传递变量 在子组件中 通过 props 接受 在接收的时候有两种接收方式 数组形式 和 对象形式 对象形式可以规定传来的变量的数据类型(type)默认值(default)以及是否必填(required)

  1. 子传父

首先在使用子组件的标签上定义一个自定义事件 在子组件里通过 this.$emit 去调用这个自定义事件 $emit 方法的第一个参数是自定义事件的名字 第二个参数是就是子组件要传递给父组件的变量 最后在父组件接收使用就可以了

  1. 兄弟组件

  • 利用中央事件总线 eventbus 在 main.js 中 把一个空的 vue 实例挂载在 vue 的原型上起名叫$bus 传数据的时候用this.$bus.$emit传 在要接受数据的子组件 在 created钩子函数中 用$on 方法接收

  1. 利用 vuex 进行组件通信 把公共的数据存在 vuex 里就可以实现组件之间都能使用这个数据了

  2. 其实 v-model 也能实现组件通信 因为 v-model 就是 :value 和 @input 事件的合写 如果在一个子组件上使用 v-model 也能实现父子组件之间的通信

  3. $attr+$listener 如果父组件 A 下面有子组件 B 子组件 B 下面又有子组件 C 如果 a 组件的变量和方法想要传给组件 C 的时候 就用到这个方法 适用于多级组件传值 在 B 组件中给 C 组件绑定 v-bind="$attrs" v-on="$listeners" 然后在 C 组件中就可以直接使用 a 传来的属性和方法了 (简单来说:$attrs与$listeners 是两个对象,$attrs 里存放的是父组件中绑定的非 Props 属性,$listeners 里存放的是父组件中绑定的非原生事件。)

  4. provide 和 inject 父组件通过通过 provide 提供变量 子组件中通过 inject 注入变量,不论嵌套了几层子组件 都能通过 inject 来调用 provide 的数据

这种写法传的数据是不响应的 6 和 7 都是用来祖孙传值的

  1. $parent和$children 在子组件内可以直接通过$parent对父组件进行操作,在父组件内可以直接通过$children 对子组件进行操作 在父组件调用子组件时候要加下标也就是$children 是一个数组 因为可以有很多个子组件

  2. 也能用本地存储 来 完成组件通信

19.mixin 混入

把公共的选项提出来 选项就是 data methods computed 钩子函数 等选项对象 混入分为全局的混入和局部的混入 全局的混入就是在 mainjs 中引入 Vue.mixin 注入 局部就是在页面内引入 mixins:[]混入 混入的选项和页面的选项冲突的时候 页面的起作用 如果是钩子函数 会进行合并也就是页面的和混入的都会执行

20.虚拟 dom

虚拟 dom 是利用 js 描述元素与元素的关系,用 js 对象来表示真实的 DOM 树结构,创建一个虚拟 DOM 对象 由于在浏览器中操作 DOM 是很昂贵的。频繁的操作 DOM,会产⽣⼀定的性能问题. 在组件渲染的时候会调用 render 函数,这个函数会生成一个虚拟 dom,再根据这个虚拟 dom 生成真实的 dom,然后这个真实的 dom 会挂载到我们的页面中。 如果只是渲染一个页面后期不改动的话 那么虚拟 dom 其实成本更高 因为 都要渲染成真实的 dom 如果组件内有响应的数据,数据发生改变的时候 render 函数会生成一个新的虚拟 dom 新的虚拟 dom 树和旧的虚拟 dom 树进行对比,找到要要修改的虚拟 dom 的部分,去修改相对应部分的真实 dom

21.diff 算法

diff 算法就是对虚拟 dom 进行对比,并返回一个 patch 对象,这个对象的作用是存储两个节点不同的地方,最后用 patch 里记录的信息去局部更新真实的 dom

diff 算法的步骤 1.js 对象表示真实的 dom 结构,就是我们说的生成一个虚拟 dom,再用虚拟 dom 构建一个真的 dom 树,放到页面中。 2.状态改变的时候生成一个新的虚拟 dom 跟旧的进行对比,这个对比的过程就是 diff 算法,通过 patch 对象记录差异 3.把记录的差异用在第一个虚拟 dom 构建的真实的 dom 上,视图就更新了

Vue 的 diff 算法是平级⽐较,不考虑跨级⽐较的情况。内部采⽤深度递归的⽅式+双指针⽅式⽐较

22.过滤器

所谓的 vue 过滤器就是将数据进⾏⼆次处理,得到我们想要的结果数据 vue 的过滤器分为两种,第⼀种是全局过滤器,通过 vue.filter 来进⾏定义,第⼆种是局部过滤器,需要定义在组件内部 filters 项⽬中我们通过过滤器将后台返回的状态 0 和 1 转化为⽀付或者未⽀付 还可以用来过滤时间格式(将后台给我们的时间转化为时间对象然后获取年月日时分秒 然后用字符串拼接在一起)

23.自定义指令

除了 vue 自带的内置指令 v-for v-model 这些外 我们还可以自己定义一个自定义指令来对 dom 进行操作 指令在用的时候用 v-xx 使用,可以在组件内定义局部 directives 还可以在 main.js 中定义全局的 Vue.directive

自定义指令有五个钩子函数 bind 指令刚刚绑定上 inserted 指令所绑定的 dom 节点插入到页面中 update 指令更新 componentUpdated 子组件更新 unbind 解绑

这些钩子函数有两个参数 el 代表绑定的元素 binding 包含了跟指令相关的一些信息 我们在用自定义指令的时候一般会对这些钩子函数进行简写

24.说⼀下如何对 axios 进⾏⼆次封装?以及 api 如何封装?(30%)

  1. 在 src ⽂件夹内创建 utils ⽂件夹

  2. 在 utils ⽂件夹内创建 request.js ⽂件

  3. 在 request.js 内引⼊ axios

  4. 使⽤ axios.create ⽅法创建 axios 的实例,在 axios.create ⽅法⾥⾯可以配置请求的公共地址和超时时间以及其他的⼀些配置

  5. 在创建请求拦截器和响应拦截器

  6. 在请求拦截器⾥⾯可以获取 vuex 的 token,并通过 config.header.token = vuex 的 token,将 token 发送给后台

  7. 在请求拦截器⾥⾯我们配置 loading 加载

  8. 在响应拦截器⾥⾯我们可以结束 loading 加载以及 token 的过期处理,以及错误响应信息的处理

  9. 最后通过 export default 导出 axios 的实例对象

  10. 在 src ⽂件内创建 api ⽂件夹

  11. 在 api ⽂件夹内创建对应模块的 js ⽂件

  12. 在对应的⽂件⾥⾯引⼊ request.js ⽂件

  13. 封装 api ⽅法

  14. 最后通过 export default 导出封装的 api ⽅法

25 说⼀下 axios 的拦截器的作⽤?应⽤场景都有哪些?(80%)

⾸先呢,axios 拦截器是 axios 给我们提供的两个⽅法,通过这两个⽅法我们可以对请求发送之前以及响应之后进⾏逻辑的再次处理(拦截). 有请求拦截和响应拦截 这两个拦截器不需要⼿动触发,只要发送 http 请求的时候就会⾃动触发. 我在项⽬中经常通过拦截器发送 token, 对 token 进⾏过期处理,加全局 loading 以及处理错误编码字典

26 跨域

跨域是浏览器的跨域 在请求不满足同源策略的接口的时候 会报跨域的错误 同源策略 就是 协议 域名 端口号 都一样 不满足同源策略的就会产生跨域的问题

跨域怎么解决 jsonp 跨域 服务器代理跨域

proxy 是 vue 中解决跨域的方案 proxy 跨域的本质也是服务器代理

27. 说⼀下你对 slot 插槽的理解?(70%) 过⼀下

⾸先呢,所谓的插槽就是⼀个占位符,将⾃定义组件的内容展示出来.我们知道⾃定义的组件⾥⾯如果写内容的话,⻚⾯是不会显示出来的,如果我们想让⾃定义组件⾥⾯的内容显示出来,我们就需要使⽤ slot 的插槽. ⽽插槽分别具名插槽和匿名插槽、以及作⽤域插槽. 我们⽤的⽐较多的具名插槽和匿名插槽,具名插槽需要 slot 标签上指定 name 属性,⽽在对应标签上添加 v-slot 属性. v-slot 可以简写成# 作用域插槽可以理解为 把子组件的数据拿到父组件的插槽的位置使用 在项⽬中我们⼀般在进⾏组件封装的时候会使⽤插槽,以上就是我对插槽的理解.

28.Vue.use()

安装使用 vue 的插件的,可以注册 方法和组件 在封装的组件和 js 方法中使用了 install 方法才能使用 Vue.use 注册 install 在调用 use 的时候会自动执行 会调⽤ install`⽅法将 Vue 的构建函数默认传⼊,在 插件中可以使⽤ vue,⽆需依赖 vue 库

29.vue 中 data 发⽣变化,视图不更新如何解决?(必问) 过⼀下

⾯试官,您好,接下来我先给您介绍⼀下为什么 data 发⽣变化,视图不更新,因为 Vue 实例中的数据是响应式的⽽我们新增的属 性并不是响应式的,由于受现在 JavaScript 的限制,Vue ⽆法检测到属性的新增或删除。所以有时⽆法实时的更新到视图上。 所以我在项⽬中遇到这类问题的时候⼀般是通过 this.$set,该⽅法⼀共有三个参数,分别是给谁添加,新增属性,新增的值. 以上就是我对视图不更新的理解.

30 为什么 vue 中 data 必须是⼀个函数?(必问) 过⼀下

如果 data 是⼀个函数的话,这样每复⽤⼀次组件,就会返回⼀份新的 data,类似于给每个组件实例创建⼀个私有的数据空间,让各个组件实例维护各⾃的数据。⽽单纯的写成对象形式,就使得所有组件实例共⽤了⼀份 data,就会造成⼀个变了全都会变的结果。 所以说 vue 组件的 data 必须是函数。这都是因为 js 的特性带来的,跟 vue 本身设计⽆关。

31.MVVM 模式的优点以及与 MVC 模式的区别?

MVVM 模式的优点: 1、低耦合: 视图(View)可以独⽴于 Model 变化和修改,⼀个 ViewModel 可以绑定到不同的"View"上,当 View 变化的时候 Model 可以不变,当 Model 变化的时候 View 也可以不变。 2、可重⽤性: 你可以把⼀些视图逻辑放在⼀个 ViewModel ⾥⾯,让很多 view 重⽤这段视图逻辑。 3、独⽴开发: 开发⼈员可以专注于业务逻辑和数据的开发(ViewModel),设计⼈员可以专注于⻚⾯设计。 4、可测试: 界⾯素来是⽐较难于测试的,⽽现在测试可以针对 ViewModel 来写。 MVVM 和 MVC 的区别: mvc 和 mvvm 其实区别并不⼤。都是⼀种设计思想。 主要区别 mvc 中 Controller 演变成 mvvm 中的 viewModel, mvvm 通过数据来显示视图层⽽不是节点操作。 mvvm 主要解决了: mvc 中⼤量的 DOM 操作使⻚⾯渲染性能降低,加载速度变慢,影响⽤户体验。

32. 怎样理解 Vue 的单向数据流?

数据总是从⽗组件传到⼦组件,⼦组件没有权利修改⽗组件传过来的数据,只能请求⽗组件对原始数据进⾏修改。这样会防⽌从⼦组件意外改变⽗级组件的状态,从⽽导致你的应⽤的数据流向难以理解。 注意:在⼦组件直接⽤ v-model 绑定⽗组件传过来的 prop 这样是不规范的写法 开发环境会报警告 如果实在要改变⽗组件的 prop 值 可以再 data ⾥⾯定义⼀个变量 并⽤ prop 的值初始化它 之后⽤$emit 通知⽗组件去修改

33.组件写 name 有啥好处?

增加 name 属性,会在 components 属性中增加组件本身,实现组件的递归调⽤。

可以表示组件的具体名称,⽅便调试和查找对应的组件,比如说 keep-alive 的 缓存谁不缓存谁的 include 和 exclude 属性就是通过 name 判断

二:-----es6 部分面试题

1.说⼀下 es6 新增的特性有那些?(必问)

  1. 新增了变量声明⽅式,也就是 let 和 const

  2. 新增了解构赋值

  3. 新增了⼀些数组⽅法 字符串⽅法 正则表达的⽅法 函数的⼀些写法 对象的⽅法

  4. promise

  5. async await

  6. class 以及 继承

  7. 模块化

  8. 新的数据类型

  9. ⼤概能想到暂时只有这么多,在项⽬中我经常使⽤ let 和 const 箭头函数。解构赋值 promise 还 有 async await

2.promise

2.1-promise 是什么

promise 是 es6 新增的一种解决异步回调的一种方案,可以理解为就是把异步操作的回调函数的写法变成 链式写法.then .catch promise ⼀共有三个状态,分别是进⾏中,成功或者失败 如何成功的话可以通过 resolve ⽅法将正确结 果返回出去,通过.then 的⽅式进⾏接受,失败的话可以通过 reject 的⽅式将失败的结果返回出去,通 过.catch 的⽅式进⾏接受,pending 状态是进⾏中,⼀旦进⾏之后,他的状态是不可逆的 如果要使⽤ promise,我们需要对 promise 进⾏实例化,在实例化的构造函数⾥⾯有⼀个回调函数,这个 回调函数⾥⾯有两个参数,分别是 resolve 和 reject,我们可以通过 promise 的实例化对象调⽤.then 或 者.catch ⽅式接受结果

2.2-我们用过的哪些东西是 promise 对象

axios uni.request $.ajax 都是已经封装好的 我们可以直接使用的 promise 对象

2.3-promise 是解决什么问题的

promise 是解决回调地狱的,回调地狱就是回调函数嵌套回调函数,存在的弊端就是写法⾮常臃肿,并且后期难以维护, 除了 promise 之外 还能用 generator async await 解决回调地狱 async await 是最终解决方案

2.4-promise 本身是同步的 .then() 是异步的

3.说⼀下 async 和 await、以及他们和 promise 的区别?(必问)

⾸先 async 和 await 是解决异步的终极⽅案,他是 generator(* ,yeild) 的语法糖. async 和 await ⼀般配和使⽤,当我 们给函数前⾯加上关键字 async,这个时候,这个函数的返回值就是⼀个 promise. ⽽ await 是⼀个同步 的操作,await 只能配合 async 只能,不然会报错,await 后⾯可以是表达式,也可以是⼀个 promise,在 await 下⾯的代码必须得等待 await 执⾏完之后才能在执⾏ 他们和 promise 的区别就是在写法上更加的简洁. 以上就是我对 async 和 await 的理解.

4.说⼀下 var 、let、const 之间的区别?(95%)

1.var 存在声明提升,我们能在声明之前使⽤。 let 、 const 因为暂时性死区的原因,不能在 声明前使⽤ 2.var 可以作为顶层属性 window.xx 使用 但是 let 和 const 不行 3.let 和 const 作⽤基本⼀致,但是后者声明的变量不能再次赋值。 4.let 定义的是块级作用域 var 定义的是函数作用域

5.const 定义的基本数据类型不能修改,但是 const 定义的引用数据类型可以修改

const 定义的数据并不是变量的值不得改动而是变量指向的内存地址的数据不能改动,对于基本数据类型,值就保存在变量指向的内存地址,对于复杂数据类型变量指向的内存地址只是保存了一盒指针,const 只能保证这个指针是固定的,但是指向的实际内存的变量是可变的。

6.说⼀下箭头函数与普通函数的区别?(80%)

在 es6 中,提供了⼀种简洁的函数写法,我们称作“箭头函数”。 写法:函数名=(形参)=>{……} 当函数体中只有⼀个表达式时,{}和 return 可以省略,当函数体中形 参只有⼀个时,()可以省略。 特点:箭头函数中的 this 始终指向箭头函数定义时的离 this 最近的⼀个函数,如果没有最近的函数 就指向 window。 箭头函数中没有 this 用的 this 是离他最近的函数的 this 区别:

  1. 箭头函数不能⽤于构造函数,不能使⽤ new** ⽽普通函数可以

  2. 在普通函数中,this 总是指向调⽤它的对象,如果⽤作构造函数,this 指向创建的对象实例, ⽽箭头函数指向箭头函数定义时的离 this 最近的⼀个函数,如果没有最近的函数就指向 window。

7.说⼀下 for in 与 for of 的区别?(80%)

For in 可以遍历对象 ⽽ for of 遍历对象会报错 for in 遍历数组得到的数组的下表 ⽽ for of 遍历得到的时候数组⾥⾯的每⼀个元素

8.说⼀下 es6 如何实现类以及如何实现类的继承?(80%)

es6 提供了类的这个概念,在 es5 中是没有类的这个概念,如果想在 es5 中实现⼀个类型,我们只能构造 函数的⽅式去创建⼀个类,⽽ es6 给我们提供⼀个更⽅便 的⽅.法,那就是 class,这个 class 理解为是构造 函数的语法糖. 我们创建⼀个类只需要⽤过关键词 class 去声明就可以了 , 他的调⽤⽅式和构造函数的调⽤⽅式是 ⼀样的 通过 es6 的类还给我们提供⼀个 extends 这样的⼀个关键字,借助 super 方法,来实现继承

9.es6 新增的数据类型和数据结构

es6 新增了一种数据类型 Symbol symbol 的特点就是值永远都是唯一的 永远不会相等 新增了两种数据结构 set 和 map set 的特点是 里面的数据不会重复 map 是一个键值对结构 对象也是 跟对象的区别是 key 值可以是任意类型的数据

js 部分面试题

1.闭包

闭包就是函数嵌套函数 内部函数可以访问外层函数的变量 因为内部函数 访问了外层的变量 所以外层函数的变量就不会被销毁 而是存在内存中

优点:可以隔离作⽤域,不造成全局污染 缺点:由于闭包⻓期驻留内存,则⻓期这样会导致内存泄露

2.说说 new 操作符具体⼲了什么?

在 JavaScript 中, new 操作符⽤于创建⼀个给定构造函数的实例对象 new 通过构造函数 Person 创建出来的实例可以访问到构造函数中的属性 new 通过构造函数 Person 创建出来的实例可以访问到构造函数原型链中的属性(即实例与构 造函数通过原型链连接了起来)

3.什么是面向对象

面向对象是一种编程思想,是相对于面向过程来说的,js 中一切皆对象。 对象包含两个含义,其中一个是属性,另外一个是动作。对象则是属性和动作的结合体。 把描述这个对象的东西看作这个对象的属性 方法是指对象能够进行的操作,方法同时还有另外一个名称,叫做函数。

一个对象包含属性和方法 属性是私有的 方法是共享的 所以一般我们把属性放在构造函数里 方法放在原型上 方法放在原型上就能够被所有的实例化对象所共享 (比如 axios 就是挂载在 Vue 的原型上 这样所有的组件都能使用)

4.什么是原型,原型链

显式原型 prototype 构造函数都有显式原型 在原型上放的就是能够被所有的实例化对象所共享的属性和方法

隐式原型 proto 每一个实例都有隐式原型 指向的就是构造函数的显式原型

constructor 这个单词本事是构造函数的意思,每一个 prototype 上都有一个 constructor,指向构造函数本身

原型链 原型对象也可能拥有原型,并从中继承⽅法和属性,⼀层⼀层、以此类推。这种关系常被称为原型链 (prototype chain),它解释了为何⼀个对象会拥有定义在其他对象中的属性和⽅法 自己有就用自己的 自己没有就用继承的 最终会找到 Object

5.Javascript 如何实现继承?

答:JS 继承实现⽅式也很多,主要分 ES5 和 ES6 先说⼀下 ES5 是如何实现继承的 ES5 实现继承主要是基于 prototype 来实现的,具体有三种⽅法 ⼀是原型链继承:即 B.prototype=new A() 这种继承只能继承原型上的不能继承构造函数里的 ⼆是借⽤构造函数继承(call 或者 apply 的⽅式继承) function B(name,age) { A.call(this,name,age) } 第二种继承只能继承构造函数里的不能继承原型 三是组合继承 组合继承是结合第⼀种和第⼆种⽅式

6.事件循环,同步异步,宏任务微任务

⾸先, JavaScript 是⼀⻔单线程的语⾔,意味着同⼀时间内只能做⼀件事,但是这并不意味着 单线程就是阻塞,⽽实现单线程⾮阻塞的⽅法就是事件循环(event loop) 在 JavaScript 中,所有的任务都可以分为

  1. 同步异步 同步任务:⽴即执⾏的任务,同步任务⼀般会直接进⼊到主线程中执⾏ 异步任务:异步执⾏的任务,⽐如 ajax ⽹络请求, setTimeout 定时函数等 碰到异步任务会进入任务队列进行等待 异步执行的时机是同步代码执行完了 异步任务准备好(比如计时器到时间了 ajax 请求成功了)才会执行异步任务

  2. 宏任务微任务 异步任务 又分为宏任务微任务 宏任务 计时器 ajax 请求 微任务 promise.then()

在异步任务中 微任务先执行 宏任务后执行

  1. 事件循环

理解 Event Loop

异步执行的运行机制如下:

所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。 主线程之外,还存在一个”任务队列”(task queue)。只要异步任务有了运行结果,就在”任务队列”之中放置一个事件。 一旦”执行栈”中的所有同步任务执行完毕,系统就会读取”任务队列”,看看里面有哪些事件。于是结束等待状态,进入执行栈,开始执行。 主线程不断重复上面的第三步。 主线程从”任务队列”中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为 Event Loop(事件循环)。只要主线程空了,就会去读取”任务队列”,这就是 JavaScript 的运行机制。这个过程会循环反复。

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

JavaScript 中,我们可以分成两种类型:基本类型 复杂类型 两种类型的区别是:存储位置不同: 基本数据类型存储在栈中 引⽤类型的对象存储于堆中 基本类型 基本类型主要为以下 6 种: Number String Boolean Undefined null symbol 引⽤类型 复杂类型统称为 Object ,我们这⾥主要讲述下⾯三种: Object Array Function

8.数据类型的判断

数据类型判断大概有四种 typeof、instanceof、constructor、Object.prototype.toString.call() 比如说检测 num=10 的数据类型 1.Typeof typeof 检测数据类型会返回对应的数据类型小写字符。 引用数据类型中的:Array,Object,Date,RegExp。不可以用 typeof 检测。都会返回小写的 object console.log(typeof num);

2 . instanceof 除了使用 typeof 来判断,还可以使用 instanceof。instanceof 运算符需要指定一个构造函数,或者说指定一个特定的类型,它用来判断这个构造函数的原型是否在给定对象的原型链上。 console.log(arr instanceof Array);

3.constructor constructor 是 prototype 对象上的属性,指向构造函数。我们可以用实例的隐式原型去找到构造函数的值。 console.log(num.————proto--.constructor);

4 . 使用 Object.prototype.toString.call()检测对象类型 可以通过 toString() 来获取每个对象的类型。每个对象都能通过 Object.prototype.toString() 来检测 console.log(Object.prototype.toString.call(num));

9. bind、call、apply 区别?

答:1. apply 、 call 、 bind 三者的区别在于: 三者都可以改变函数的 this 对象指向 三者第⼀个参数都是 this 要指向的对象,如果如果没有这个参数或参数为 undefined 或 null ,则默认指向全局 window 三者都可以传参,但是 apply 是数组,⽽ call 是参数列表,且 apply 和 call 是⼀次性传⼊ 参数,⽽ bind 可以分为多次传⼊ bind 是返回绑定 this 之后的函数, apply 、 call 则是⽴即执⾏

10.存储 *

本地存储分为 cookie、localStorage、sessionStorage, Cookie Cookie 设计初衷是用来和服务器通讯,而不是本地存储,他只是被‘借用’到本地存储。 Cookie 有一些缺点:存储空间小,最大 4k、用的时候比较麻烦

localStorage、sessionStorage 在 HTML5 中,新加入了一个 localStorage/sessionStorage 特性,这个特性主要是用来作为本地存储来使用的,解决了 cookie 存储空间不足的问题(cookie 中每条 cookie 的存储空间为 4k),localStorage 中一般浏览器支持的是 5M 大小。 优点:HTML5 专门为存储而设计,最大可存 5M、API 简单易用 setItem getItem、不会随着 http 请求被发送出去

localStorage、sessionStorage.cookie 不同点:

Cookie 可以设置过期时间 存储容量比较小 用起来没有方便的方法 localStorage 数据会永久存储,除非代码或手动删除 sessionStroage 数据只存在于当前会话,浏览器关闭则清空 一般用 localStorage 会更多一些

注意:localStorage 只支持 string 类型的存储。

11.作⽤域

作⽤域,即变量和函数⽣效的范围

作用域分为:全局作用域和局部作用域 全局 在哪儿都能用的变量 局部作用域 只在某一个范围内能用的 (有分为函数作用域和块级作用域) 局部作用域的划分: var 定义的变量 看到函数就是划分的标志,这种变量只在这个函数中能用出了函数就不能用了 let const 定义的变量 看到{}就是划分的标志,在⼤括号之外不能访问这些变量

作⽤域链 当在 Javascript 中使⽤⼀个变量的时候,⾸先 Javascript 引擎会尝试在当前作⽤域下去寻找该变 量,如果没找到,再到它的上层作⽤域寻找,以此类推直到找到该变量或是已经到了全局作⽤域 如果在全局作⽤域⾥仍然找不到该变量,它就会在全局范围内隐式声明该变量(⾮严格模式下)或是直接报错

12. This 的指向

在 js 中 this 不是固定不变的,它会随着执行环境的改变而改变。要注意的是 this 取什么值,是在执行时确认的,定义时无法确认。 this 的调用大概分为四种场景: 1.浏览器里,在全局范围内的 this 指向 window 对象; 2.在函数中,this 永远指向最后调用他的那个对象; 3.构造函数中,this 指向 new 出来的那个新的对象; 4.箭头函数中 this 比较特殊,箭头函数 this 为父作用域的 this,不是调用时的 this.要知道前四种方式,都是调用时确定,也就是动态的,而箭头函数的 this 指向是静态的,声明的时候就确定了下来;

有三个方法改变 this:

call:参数是单个使用的, apply:参数是一个数组 call 和 apply 都会立刻调用这个函数 bind:只改变 this 不会让函数立刻调用

13. 深拷贝 浅拷贝

深拷贝拷贝的是值 浅拷贝拷贝的是地址 深拷贝和浅拷贝的区别

  1. 浅拷贝: 将原对象的引用直接赋给新对象,新对象只是原对象的一个引用,而不复制对象本身,新旧对象还是共享同一块内存

  2. 深拷贝: 创建一个新的对象和数组,将原对象的各项属性的“值”(数组的所有元素)拷贝过来,是“值”而不是“引用”,新对象跟原对象不共享内存,修改新对象不会改到原对象

基本数据类型存储在栈中 引⽤类型的对象存储于堆中

数组 深拷贝的方法

  1. 可以用 JSON.parse(JSON.stringify())

  2. concat

  3. 展开运算符

对象的深拷贝

  1. 可以用 JSON.parse(JSON.stringify())

  2. 用递归实现

14. 垃圾回收和内存机制

垃圾回收: 浏览器的 js 具有自动垃圾回收机制,垃圾收集器会定期的找出那些不在继续使用的变量,然后释放内存。垃圾回收器会按照固定的时间间隔周期性的执行。 js 中最常用的垃圾回收方式就是标记清除。例如,在函数中声明一个变量,就将这个变量标记为“进入环境”。而当变量离开环境时,则将其标记为“离开环境”。 引用计数的含义是跟踪记录每个值被引用的次数。当这个值的引用次数变成 0 时,则说明没有办法再访问这个值了,就把它回收掉。

内存泄漏:

  1. 循环引用 一个很简单的例子:一个 DOM 对象被同一个多个 Javascript 对象引用,这个 DOM 对象可能会引发内存泄露。要想破坏循环引用,需要赋值为 null。

  2. 闭包 在闭包中引入闭包外部的变量时,当闭包结束时此对象无法被垃圾回收(GC)。

  3. DOM 泄露 当原有的 DOM 被移除时,子节点引用没有被移除则无法回收

  4. Times 计时器泄露

15. Jsonp 的原理 ****

JSONP 原理:

ajax 请求受同源策略影响,不允许进行请求,我们利用 script 标签的 src 属性不受同源策略的约束,利用这个特性 jsonp 需要以下步骤:

  1. 动态创建(document.createElement('script'))

  2. 设置 src 属性,(src 中要包含参数 callback=fn)进行跨域请求

  3. 添加到页面中执行 (body.appendChild('script'))

  4. 页面要提前定义好 callback。

  5. 后端会返回回调函数执行并包裹参数 callback(data) 备注: 服务端不再返回 JSON 格式的数据,而是返回回调函数包裹数据(fn({name:'tom',age:18}),在 src 中进行了调用,这样实现了跨域。

16. dom 操作 (不必要全背 挑几个背)

DOM 是网页中用来表示文档中对象的标准模型,他是由节点和对象组成的结构集合。在浏览器解析 HTML 标签时,会构建一个 DOM 树结构。 由此呢 js 也提供了一些 dom 的操作 一、dom 元素获取 1.document.getElementById(id 的值)  通过 id 来获取元素的对象,返回值是一个对象 2.document.getElementsByName(name)  通过 name 属性来获取对象的,返回值是一个数组,与 getElementById()方法类似,但他是查询的 name 元素,而不是 id 属性 3.document.getElementsByTagName()  通过标签来获取元素的对象, 返回值是一个数组 4.document.getElementsByClassName()  通过 class 类名来获取的对象,返回值是一个数组 5.document.querySelector() css 选择器,返回与该模式匹配的第一个元素,结果为一个元素;如果没找到匹配的元素,则返回 null 6.document.querySelectorAll() css 选择器,返回与该模式匹配的所有元素,结果为一个类数组二、dom 创建 二、dom 操作

  1. 创建:新的标签(元素节点) = document.createElement("标签名")

  2. 删除:父节点.removeChild(子节点);

  3. 插入:insertBefore(新插入的节点,参照物节点) 往某个节点的前面插入一个新的节点

  4. 追加:appendChild(新的节点的名) 当前对象追加一个子节点

17.事件

  1. 事件绑定的方法 一是直接在标签内直接添加执行语句,二是绑定函数。第三种 是事件监听(addEventListener)

  2. DOM 事件两种类型 (事件传播也背这个答案) 事件类型分两种:事件捕获、事件冒泡。 事件捕获就是由外往内,从事件发生的顶点开始,逐级往下查找,一直到目标元素。 事件冒泡就是由内往外,从具体的目标节点元素触发,逐级向上传递,直到根节点。

怎么阻止事件冒泡:vue 里是 stop 原生 js 可以 return false 还有 stopPropagation

18。事件委托

事件委托,又名事件代理。事件委托就是利用事件冒泡,就是把子元素的事件都绑定到父元素上。如果子元素阻止了事件冒泡,那么委托也就没法实现了

好处:提高性能,减少了事件绑定,从而减少内存占用 比如:瀑布流:无限上拉列表中,如果给每一个图片绑定点击事件,非常繁琐且消耗内存。所以我们可以把每张图片上的点击事件委托给共同的父元素。

19。封装一个通用的事件绑定函数

我们在封装这个函数的时候可以用事件监听来实现 ,封装的函数有三个参数,第一个是要绑定事件的元素,第二个是要绑定的事件类型,第三个是事件的执行函数。 调用这个函数 就可以实现给某个元素绑定一个事件了。

20. 数组方法 **

1.push() 可以添加一个或多个参数到数组的尾部,添加之后原来的数组会发生改变,返回的是添加后的数组的长度 2.pop() 从数组尾部删除一个元素,原数组会发生改变,返回数组中被删除的元素 3.unshift() 可以添加一个或多个参数到数组的头部,添加后原来的数组会发生改变,返回的是添加后的数组的长度 4.shift() 从数组头部删除一个元素,原数组会发生改变,返回数组中被删除的元素 5.slice() 从数组中截取,如果不传参,会返回原数组。返回截取的值,两个参数 一个是开始的位置,第二个是结束的位置,原数组不变,最多两个参数 6.splice()截取类 没有参数,返回空数组,原数组不变;一个参数,从该参数表示的索引位开始截取,直至数组结束,返回截取的 数组,原数组改变;两个参数,第一个参数表示开始截取的索引位,第二个参数表示截取的长度,返回截取的 数组,原数组改变;三个或者更多参数,第三个及以后的参数表示要从截取位插入的值。 7.reverse() 数组翻转 8.sort() 数组排序 9.join() 数组拼接 10.isArray() 判断是否是数组 11.toString() 数组转字符串 12.concat( ) 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组

遍历数组的方法 它们的参数都是回调函数 forEach 循环 没有 return 对原数组发生改变 filter 返回所有满足条件的新数组 返回值就是新数组 map 遍历数组,可重新构建一个新的数组 返回值就是新数组 find 查找数组中满足条件的第一个数组项 返回这个数组项 findIndex 查找数组中满足条件的第一个数组项的索引 返回这个数组项的索引或者-1 some 遍历数组中,数组中至少有一个满足条件的数组项,返回 true 否则 false every 遍历数组 所有的数组项都满足条件时 返回 ture 否则 false

21. 数组去重 *

  1. 使用 ES6 中的 set 是最简单的去重方法 let arr = [1, 2, 3, 4, 5, 5, 4, 3]; let res = [...new Set(arr)];

  2. 利用 Map 数据结构去重 创建一个空 Map 数据结构,遍历需要去重的数组,把数组的每一个元素作为 key 存到 Map 中。由于 Map 中不会出现相同的 key 值,所以最终得到的就是去重后的结果。 3 递归去重 4.forEach+indexOf 定义一个空数组,通过 forEach 循环,indexOf 判断值是否是-1,如果是 push 到新的数组中

22. 字符串的方法 **

字符串的方法 charAt( ) 方法从一个字符串中返回某个下标上的字符 concat( ) 方法将一个或多个字符串与原字符串连接合并,形成一个新的字符串并返回。

search( ) 获取某个字符或者字符串片段首次出现的位置 match( ) 检索字符串或者正则表达式 replace( ) 替换 replace(参数 1,参数 2); 参数 1:替换谁 参数 2:替换值 split( 参数 1,参数 2 ) 字符串切割,切割后返回数组

slice( ) 和 substring( ) ( 开始位置,结束位置 ) 获取两个索引值之间的字符串片段 从下标 2 开始截取,到下标 4 结束,但不包含 4 substr ( 开始位置,截取的长度 ) 获取两个索引值之间的字符串片段

indexOf( ) 获取某个字符或者字符串首次出现的位置,找到则返回索引值,找不到则返回-1 lastIndexOf( ) 获取某个字符或者字符串最后出现的位置,找到则返回索引值,找不到则返回-1

23.防抖节流

防抖:就是事件触发 n 秒之后在执行回调 如果 n 秒内再执行 则重新计算时间,若在 n 秒内重复触发,只有⼀次⽣效 节流:如果触发某个事件 每隔 n 秒执行一次 相同点: 都可以通过使⽤ setTimeout 实现 ⽬的都是,降低回调执⾏频率。节省计算资源 不同点: 函数防抖,在⼀段连续操作结束后,处理回调,利⽤ clearTimeout 和 setTimeout 实现。 函数节流,在⼀段连续操作中,每⼀段时间只执⾏⼀次,频率较⾼的事件中使⽤来提⾼性能。

防抖适合用在 表单提交或者表单搜索的场景 节流适合用在滚动条滚动事件

----TS 面试题----

1.什么是 TypeScript?

Typescript 是⼀个强类型的 JavaScript 超集,⽀持 ES6 语法,⽀持⾯向对象编程的概念,如类、接⼝、继承、泛型等。 Typescript 并不直接在浏览器上运⾏,需要编译器编译成纯 Javascript 来运⾏。

2.为什么要使⽤ TypeScript ? TypeScript 相对于 JavaScript 的优势是什么?

增加了静态类型,可以在开发⼈员编写脚本时检测错误,使得代码质量更好,更健壮。 优势:

  1. 杜绝⼿误导致的变量名写错;

  2. 类型可以⼀定程度上充当⽂档;

  3. IDE ⾃动填充,⾃动联想;

3. TypeScript 中 any 类型的作⽤是什么?

为编程阶段还不清楚类型的变量指定⼀个类型。 这些值可能来⾃于动态的内容,⽐如来⾃⽤户输⼊或第三⽅代码库。 这种 情况下,我们不希望类型检查器对这些值进⾏检查⽽是直接让它们通过编译阶段的检查。

4.TypeScript 中 any、never、unknown、null & undefined 和 void 有什么区别?

any : 动态的变量类型(失去了类型检查的作⽤)。 never : 永不存在的值的类型。例如:never 类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的 返回值类型。 unknown : 任何类型的值都可以赋给 unknown 类型,但是 unknown 类型的值只能赋给 unknown 本身和 any 类型。 null & undefined : 默认情况下 null 和 undefined 是所有类型的⼦类型。 就是说你可以把 null 和 undefined 赋值给 number 类型的变量。当你指定了 --strictNullChecks 标记,null 和 undefined 只能赋值给 void 和它们各⾃。 void`: 没有任何类型。例如:⼀个函数如果没有返回值,那么返回值可以定义为 void。

你可能感兴趣的:(理论题,vue.js,es6,前端,面试,前端框架)