2022前端面试题汇总(持续更新中~)_前端面试题

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

目录

1. 防抖和节流

2. js闭包

vue中的data为什么是一个函数?面试常问

3. ES6面试题

3.1 var let const 区别

3.2 解构 

3.3 如何利用es6快速的去重?

3.4 Promise 面试题 以下代码的执行结果是?

4. Vue相关

4.1 MVC和MVVM的区别

4.2 v-model 原理

4.3  vue中的data为什么是一个函数?面试常问

4.4 v-if 和 v-show的区别

4.5 v-for中为什么要有key

5. 跨域的解决方法

5.1. webpack 里的proxy

5.2. jsonp 需要后端支持

5.3. webpack plugin 插件

5.4. cors 后端解决

6.git命令

7.get与post请求有什么区别

8. cookie、localStorage、sessionStorage的区别 

9. async 和 await 的区别

10. setTimeout 时间为0 以及误差的原因

11. 求数组的最大值?

12. 求数组的最小值?

13. 数组去重 

14. 生成从0 到 指定数字的数组 

15. 数组求和

16. js的数据类型

17. js的变量提升

 18. this指向

19. map和forEach的区别

20. 箭头函数和普通函数的区别?

21. es6新增

22. 数组方法汇总

23. 项目性能优化方案


1. 防抖和节流

防抖触发事件后在n秒内事件只执行一次如果在n秒内又触发了事件则会重新计算函数的执行时间。

比如点击按钮2秒后调用函数结果在1.5秒的时候又点了则会重新计算2秒后在调用函数。

应用场景下拉触底加载下一页。

节流连续发生的事件在n秒内只执行为一次

应用场景比较多的是搜索查询

2. js闭包

什么是闭包闭包就是能够读取其他函数内部变量的函数

function a() {

        let a1 = 1;

        return function() {

                return a1

        }

}

闭包存在意义

可以延长变量的生命周期4可以创建私有的环境

闭包好处

可以读取其他函数的内部变量

将变量始终保存在内存中

可以封装对象的私有属性和方法

坏处消耗内存、使用不当会造成内存溢出问题

vue中的data为什么是一个函数?面试常问

Vue 中的 data 必须是个函数因为当 data 是函数时组件实例化的时候这个函数将会被调用返回一个对象计算机会给这个对象分配一个内存地址实例化几次就分配几个内存地址他们的地址都不一样所以每个组件中的数据不会相互干扰改变其中一个组件的状态其它组件不变。

简单来说就是为了保证组件的独立性和可复用性如果 data 是个函数的话每复用一次组件就会返回新的 data类似于给每个组件实例创建一个私有的数据空间保护各自的数据互不影响

3. ES6面试题

3.1 var let const 区别

var 存在变量提升;存在变量覆盖已经被定义且赋值的变量如果再次被赋值则以后一次值为准;没有块级作用域;

const定义的是常量声明之后必须赋值;定义的值不能去修改否则报错;有块级作用域;不存在变量提升和变量覆盖;对于数组和对象的元素修改不算做对常量的修改不会报错。

let 有块级作用域;不存在变量提升和变量覆盖;let不允许在相同的作用域中重复声明注意是相同作用域不同作用域重复声明不会报错

3.2 解构赋值 

let a = 1; let b = 2;  如果在不声明第三个变量的前提下使a=2, b=1?

答案[a, b] = [b, a]

3.3 如何利用es6快速的去重?

let arr = [23, 12, 13, 33, 22, 12, 21]

let item = [...new Set(arr)]

3.4 Promise 面试题 以下代码的执行结果是?

const promise = new Promise((resolve, reject) => {

        console.log(1)

        resolve()

        console.log(2)

})

promise.then(() => {

        console.log(3)

})

console.log(4)

答案1,2,4,3

解释以上考察的是关于promise的原理promise的构造函数是同步执行的当new Promise的一瞬间1,2 就立刻被执行而 .then方法是异步执行的当执行完1和2之后会执行输出4最后执行输出4

4. Vue相关

4.1 MVC和MVVM的区别

MVCMmodel数据、Vview试图Ccontrolle控制器缺点是前后端无法独立开发必须等后端接口做好了才可以往下走;前端没有自己的数据中心太过依赖后台

MVVMMmodel数据、Vview试图、VMviewModel控制数据的改变和控制试图
html部分相当于View层可以看到这里的View通过通过模板语法来声明式的将数据渲染进DOM元素当ViewModel对Model进行更新时通过数据绑定更新到View。 Vue实例中的data相当于Model层而ViewModel层的核心是Vue中的双向数据绑定即Model变化时VIew可以实时更新View变化也能让Model发生变化

MVVM与MVC最大的区别就是它实现了View和Model的自动同步也就是当Model的属性改变时我们不用再自己手动操作Dom元素来改变View的显示而是改变属性后该属性对应View层显示会自动改变

4.2 v-model 原理

是采用数据劫持结合发布者-订阅者模式的方式通过Object.defineProperty()来劫持各个属性的settergetter在数据变动时发布消息给订阅者触发相应的监听回调从而达到数据和视图同步。

4.3  vue中的data为什么是一个函数?面试常问

实际上就是一个闭包因为vue是单页面应用是由很多组件构成每一个组件中都有一个data所以通过闭包给每一个组件创建了一个私有的作用域这样就不会相互影响。

4.4 v-if 和 v-show的区别

v-if是通过添加和删除元素来进行显示或者隐藏

v-show是通过操作DOM修改display样式来修改元素的显示和隐藏

如果需要频繁的进行元素的显示和隐藏使用v-show性能更好

4.5 v-for中为什么要有key

key 可以提高虚拟DOM的更新效率。

在vue中默认“就地复用”的策略在DOM操作的时候如果没有key 就会造成选项错乱

key 只能是字符串或者number其他类型不可以

1. 虚拟DOM中key的作用

key是虚拟DOM对象的标识当数据发生变化时Vue会根据【新数据】生成【新的虚拟DOM】随后Vue进行【新的虚拟DOM】与【旧的虚拟DOM】差异比较比较规则如下

2. 比较规则

1旧虚拟DOM找到了与新虚拟DOM相同的key

若虚拟DOM中内容没变直接使用之前的真实DOM

若虚拟DOM中内容变了则生成新的真实DOM随后替换掉页面中之前的真实DOM

3. 用index作为key可能会引发的问题

1若对数据进行逆序添加、逆序删除等破坏顺序的操作会产生没有必要的真实DOM更新==>界面效果没问题但效率低

2如果结构中还包含输入类的DOM会产生错误的DOM更新 ==> 界面有问题

4.6 打包后 dist 目录过大解决办法?

1. dist打包生成的文件中有 .map 文件可以删除。在 vue.config.js文件中配置productionSourceMap: false

2. 组价和路由使用懒加载、按需引入等

3. 对于文件和图片进行压缩。 安装压缩组件 compression-webpack-plugin

安装后进行导入配置 

最小化代码 minisize: true

分割代码 splitChunksl

超过限定值的文件进行压缩threshold: 文件大小字节为单位

4.7 watch和computed的区别

computed能完成的功能watch都可以完成

watch能完成的小功能computed不一定能完成。例如watch可以进行异步操作

两个重要小原则

1.所有被vue管理的函数最好写成普通函数这样this的指向才是vm或组件实例对象

2. 所有不被vue管理的函数定时器的回调、ajax的回调、promise的回调等最好写成箭头函数这样this的指向才是vm或组件实例对象

4.8 vue组件之间的数据传递

1. 父组件给子组件传递数据

通过给子组件身上绑定自定义属性然后再子组件里使用props属性来接收即可

2. 子组件给父组件传递数据

1第一种方式通过父组件给子组件传递函数类型的props实现子组件给父组件传递数据

父组件

 

 子组件

 

2第二种方式通过父组件给子组件绑定一个自定义事件实现子组件给父组件传递数据

 父组件

 

 子组件

3第三种方式通过父组件给子组件绑定一个自定义事件实现使用ref实现

 父组件

 

 子组件

 

3. 全局事件总线可以实现任意组件间的数据传递 

main.js将全局事件bus挂载到Vue的原型上这样所有的组件都可以使用

 兄弟组件A:

 兄弟组件B

4. 消息订阅与发布

一种组件间的通信方式适用于任意组件间通信。

使用步骤

1安装pubsub: npm i pubsub-js

2) 引入 import pubsub from 'pubsub-js'

3) 接收数据 A组件想要接收数据则在A组件中订阅消息订阅的回调留在A组件自身

mounted() {

        this.pid = punsub.subscribe('xxx', (data)=>{

                ......

        })

}

4) 提供数据 pubsub.publish('xxx', 数据)

5最好在beforeDestory钩子中用pubsub.unsubscribe(pid)取消订阅

5. 跨域的解决方法

跨域只要协议、域名和端口号有一个不相同就会产生跨域问题。同源策略是一个安全策略。同源指的是协议域名端口相同。浏览器处于安全方面的考虑只允许本域名下的接口交互不同源的客户端脚本在没有明确授权的情况下不能读写对方的资源。

解决办法

5.1. webpack 里的proxy

devServer: {
    proxy: {  //配置跨域
      '/api': {
        target: 'http://121.121.67.254:8185/',  //这里后台的地址模拟的;应该填写你们真实的后台接口
        changOrigin: true,  //用于控制请求头中的post值默认开启
        pathRewrite: {
          /* 重写路径当我们在浏览器中看到请求的地址为http://localhost:8080/api/core/getData/userInfo 时
            实际上访问的地址是http://121.121.67.254:8185/core/getData/userInfo,因为重写了 /api
           */
          '^/api': '' 
        }
      },
    }
  }

5.2. jsonp 需要后端支持

方案1 *通配符全部允许存在安全隐患(不推荐)。

一旦启用本方法表示任何域名皆可直接跨域请求
  1     server {
  2         ...
  3         location / {
  4             # 允许 所有头部 所有域 所有方法
  5             add_header 'Access-Control-Allow-Origin' '*';
  6             add_header 'Access-Control-Allow-Headers' '*';
  7             add_header 'Access-Control-Allow-Methods' '*';
  8             # OPTIONS 直接返回204
  9             if ($request_method = 'OPTIONS') {
 10                 return 204;
 11             }
 12         }
 13         ...
 14     }

方案2多域名配置(推荐)

配置多个域名在map中 只有配置过的允许跨域

  1  map $http_origin $corsHost {
  2         default 0;
  3         "~https://zzzmh.cn" https://zzzmh.cn;
  4         "~https://chrome.zzzmh.cn" https://chrome.zzzmh.cn;
  5         "~https://bz.zzzmh.cn" https://bz.zzzmh.cn;
  6     }
  7     server {
  8         ...
  9         location / {
 10             # 允许 所有头部 所有$corsHost域 所有方法
 11             add_header 'Access-Control-Allow-Origin' $corsHost;
 12             add_header 'Access-Control-Allow-Headers' '*';
 13             add_header 'Access-Control-Allow-Methods' '*';
 14             # OPTIONS 直接返回204
 15             if ($request_method = 'OPTIONS') {
 16                 return 204;
 17             }
 18         }
 19         ...
 20     }

5.3. webpack plugin 插件

npm i -S webpack-dev-middleware  安装中间键把前端和后端的服务绑在一起

中间件

let webpack = require('webpack')

let middle = require('webpack-dev-middleware')

let compiler = webpack(require('./webpack.config.js'))

app.use(middle(compiler))

5.4. cors 后端解决

var allowCrossDomain = function(req,res,next) {

        // 请求源

        res.header("Access-Control-Allow-Origin", "*")

        // 请求头 token

        res.header("Access-Control-Allow-Headers", "*")

        // 请求方法 get post put del

        res.header("Access-Control-Allow-Methods", "*")

        next();

}

app.use(allowCrossDomain )

6.git命令

1. git init 初始化git仓库 (mac中Command+Shift+. 可以显示隐藏文件)

2. git status 查看文件状态

3. git add 文件列表 追踪文件

4. git commit -m 提交信息 向仓库中提交代码

5. git log 查看提交记录

6.1.分支明细

1主分支master第一次向 git 仓库中提交更新记录时自动产生的一个分支。

2开发分支develop作为开发的分支基于 master 分支创建。

3功能分支feature作为开发具体功能的分支基于开发分支创建

6.2.分支命令

1git branch 查看分支

2git branch 分支名称 创建分支

3git checkout 分支名称 切换分支

4git merge 来源分支 合并分支 (备注必须在master分支上才能合并develop分支)

5git branch -d 分支名称 删除分支分支被合并后才允许删除-D 强制删除

6.3.暂时保存更改

1存储临时改动git stash

2恢复改动git stash pop

7.get与post请求有什么区别

get是从服务器上获取数据post是向服务器传送数据。

POST比GET安全因为数据在地址栏上不可见。

get方式提交的数据最多只能有1024字节而post则没有此限制。

GET使用URL或Cookie传参。而POST将数据放在request BODY中。

GET与POST都有自己的语义不能随便混用。

据研究在网络环境好的情况下发一次包的时间和发两次包的时间差别基本可以无视。而在网 络环境差的情况下两次包的TCP在验证数据包完整 性上有非常大的优点。post 发送两次get 只发送一次。

8. cookie、localStorage、sessionStorage的区别 

共同点: 都是保存在浏览器端、且同源的

不同点

cookie数据始终在同源的http请求中携带即使不需要即cookie在浏览器和服务器间来回传递。cookie数据还有路径path的概念可以限制cookie只属于某个路径下sessionStorage和localStorage不会自动把数据发送给服务器仅在本地保存。

存储大小限制也不同

cookie数据不能超过4KsessionStorage和localStorage可以达到5M

sessionStorage仅在当前浏览器窗口关闭之前有效;

localStorage始终有效窗口或浏览器关闭也一直保存本地存储因此用作持久数据;

cookie只在设置的cookie过期时间之前有效即使窗口关闭或浏览器关闭

作用域不同

sessionStorage不在不同的浏览器窗口中共享即使是同一个页面;

localstorage在所有同源窗口中都是共享的;也就是说只要浏览器不关闭数据仍然存在

cookie: 也是在所有同源窗口中都是共享的.也就是说只要浏览器不关闭数据仍然存在

9. async 和 await 的区别

区别

async是来定义函数的定义异步函数打印函数名可以得到一个promise对象言外之意可以通过这个   函数名称.then 这个方法

await 后面跟的是任意表达式一般使用promise的表达式

async 内部实现又返回值 成功返回promise.resolve() ,出错返回promise.reject() 返回值用catch捕获

await 等待后面的promise对象执行完毕拿到了promise.resolve的值之后执行后面的代码。await后面的表达式能是promise.reject所以建议await放在try....catch语句中

优点async和await 属于es7语法。编写方便提高程序效率避免了回调地狱

补充promise和async和await的区别

promise es6语法promise中包含catchasync需要自己定义catch

promise 提供的方法会多一些all、race等方法aync中是没有的。

10. setTimeout 时间为0 以及误差的原因

setTimeout如果

时间为0则会立即插入队列不是立即执行等待前面的代码执行完毕。

11. 求数组的最大值?

function getMaxArryNum(arr) {

        return Math.max(...arr)

getMaxArryNum([1,2,3,4,5,6])

12. 求数组的最小值?

const getMinArryNum= (arr) => {

        return Math.min(...arr)

getMinArryNum([1,2,3,4,5,6]) 

13. 数组去重 

const removeEqual = (arr) => {

        const result = arr.filter((item, index, self) => {

                return self.indexof(item) === index

        })

        return result

removeEqual([1,2,3,4,5,6,1,2,3,42,1])

14. 生成从0 到 指定数字的数组 

const getArr = (startNum, endNum) => {

        let arr = []

        for(var i=startNum; i<=endNum; i++){

                arr.push(i)

        }

        return arr

getArr(0,4)

15. 数组求和

const arrSum = (arr) => {

        const temp = arr.reduce((pre, now) => {

                return pre+now

        },0)

        return temp

}

arrSum([1,2,3,4])

16. js的数据类型

js 数据类型分为基本数据类型和复杂数据类型

基本数据类型Boolean、Number、String、Null、Undefined

复杂数据类型 Object、Array、Function、Date

17. js的变量提升

在js中变量和函数的声明会被提升到最顶部执行

函数提升高于变量的提升

函数内部如果用var声明了相同名称的外部变量函数将不再向上寻找

匿名函数不会提升

 18. this指向

this总是指向函数的直接调用者。

如果有new关键字this指向new出来的对象

在事件中this指向触发这个事件的对象

19. map和forEach的区别

forEach方法是最基本的方法遍历和循环。默认有3个参数分别是遍历的每一个元素item遍历的索引index遍历的数组array

map方法和foreach一致不同的是会返回一个新的数组所以callback需要有return返回值如果没有会返回undefined

20. 箭头函数和普通函数的区别?

函数体内的this对象就是定义时所在的对象而不是使用时所在的对象

不可以当作构造函数也就是说不可以使用new命令否则会报错

不可以使用arguments对象该对象在函数体内不存在如果要用可以使用Rest参数代替

不可以使用yield命令因此箭头函数不能用作Generator函数

21. es6新增

新增模版字符串

箭头函数

增加let、const来声明变量

for-of用来遍历数据-例如数组中的值

解构赋值

新增简单数据类型Symbol独一无二的不会与其他属性名冲突

将Promise对象纳入规范提供了原生的Promise对象

22. 数组方法汇总

map 循环遍历数组、返回一个新的数组

forEach 循环遍历数组不改变原数组

push/pop 在数组的末尾添加/删除元素  改变原数组

unshift/ shift 在数组的头部添加/删除元素改变原数组

join  把数组转化为字符串

some  有一项返回为true则整体为true

every  有一项返回为true则整体为false

filter 数组过滤

slice(start, end)  数组截取包括开头不包括截取返回一个新的数组

splice(start, number, value)  删除数组元素改变原数组

indexof/lastindexof 查找数组项返回对应的下标

concat数组的拼接不影响原数组浅拷贝

sort数组排序 改变原数组

reverse 数组反转改变原数组

23. 项目性能优化方案

减少http请求

减少DNS查询

使用CDN

避免重定向

图片懒加载

路由懒加载

减少DOM元素操作

使用外部js和css

压缩js、css、字体、图片等

使用iconfont字体图标、雪碧图等

避免图片的src为空

把样式表放在link中

把js放在页面的底部

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6