2023年前端常见面试题(附答案)

1,128 阅读22分钟

总结一下最近遇到的面试题,答案仅为个人理解,若有欠缺或不准确的还望大家多多指点。

image.png

  1. 什么是跨域,如何解决跨域问题。
  2. vue中的data为什么是一个函数。
  3. vue组件之间的通信方式。
  4. v-model。
  5. vue的双向数据绑定是如何实现的。
  6. vue2和vue3有什么区别。
  7. this指向。
  8. webpack打包机制。
  9. 闭包。
  10. async、await、promise之间的区别
  11. call、bind、apply三者的区别
  12. v-for 和 v-if是否可以同时使用。
  13. 防抖和节流。
  14. 项目如何优化。
  15. vue.delete 和 delete的区别。
  16. 同步异步的区别。
  17. localStorage、sessionStorage、cookies之间的区别。
  18. 深拷贝、浅拷贝。
  19. es6中新增的数组的操作方法。
  20. v-for中key的作用
  21. diff算法
  22. 数组与字符串之间的互转。
  23. 一次完整的网络请求。
  24. 父元素加载子元素的生命周期。
  25. 路由有几种模式。
  26. url的组成。
  27. 原型链。
  28. watch 和component之间的区别。
  29. 如何实现大文件上传。
  30. 用户登录的流程.
  31. 数组为什么需要遍历。
  32. axios 拦截器
  33. vue全局定义了哪些方法,有什么用。
  34. axios如何实现异步队列
  35. 如何理解渐进式框架
  36. rem 多端适配。
  37. Vuex
  38. 开发中常见问题。
  39. 什么是bfc
  40. 跨域的原理
  41. 如何实现网页置灰效果
  42. 如何实现三栏布局,中间部分自适应。
  43. position具有哪些属性,每个属性相对定位的对象是?sticky定位有用到过吗?
  44. flex:1代表什么。
  45. 如何实现一个数组乱序方法。
  46. 如何实现一个深拷贝方法
  47. 箭头函数和普通函数的区别是什么。
  48. 能说说你对promise的理解吗?以及常用的api
  49. es6中set和map有什么区别,可以遍历吗?
  50. commonJS和es molue的区别
  51. array中的哪些方法可以改变原数组。
  52. 什么是受控组件。
  53. 高阶函数,高阶组件是什么
  54. vue和react的区别
  55. plus babel有什么作用。
  56. 虚拟dom
  57. v-model语法糖

1、什么是跨域,如何解决跨域问题?

  1. 使用jsonp。
  2. 在vue.config里面设置proxy代理。
  3. 通过nginx代理。

2、vue中的data为什么是一个函数?

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

3、vue组件之间的通信方式。

1.父子通信

子组件可以通过props接收父组件传过来的值

2.子父通信

子组件可以通过emit给父组件传值。

3.兄弟组件通信

1.可以通过一个中间件进行通信。

2.可以使用EventBus直接进行兄弟组件之间的通信。

4、v-model的原理/vue的双向数据绑定是如何实现的?

v-model实际上是一个语法糖。它包括了v-bind:value和v-on两个操作。采用数据劫持 发布者-订阅者 模式的方式,通过object.definedproperty()来劫持各个属性的setter,getter在数据变动时发布消息给订阅者,触发相应的监听回调从而达到数据和视图同步,也就是双向绑定。

5、vue2和vue3的区别

  1. 数据双向绑定的原理发生改变。(vue2采用数据劫持方式实现双向绑定,vue3使用proxy实现数据双向绑定)
  2. 支持碎片化(可以使用多个根元素)
  3. Vue2 与vue3 最大的区别是vue2使用选项类型api,对比vue3合成型api。()
  4. vue2是把数据放入data中,vue3就需要使用一个新的setup()方法,此方法在组件初始化构造得时候触发。使用一下三个步骤来简=建立反应性数据: 1. 从vue引入reactive;使用reactive() 方法来声明数据为响应性数据;3. 使用setup()方法来返回我们得响应性数据,从而template可以获取这些响应性数据。
  5. 生命周期。
  6. 父子传参不同。 注:选项型api是通过vue实例里面一系列的data、methods、mounted等方法来实现页面逻辑,但是组合型api则可以使用函数方法实现页面逻辑。

6、this指向

  1. 普通函数:谁调用就指向谁,如果没人调用,则指向window.
  2. 箭头函数:指向函数作用域所用的对象。
  3. 全局作用域:始终指向window.

7、webpack打包机制

将应用中的各个模块打包成一个或者多个bundle文件。借助loaders和plugins,他可以改变压缩和优化各种各样的文件。输入不同资源,比如html\css\js\img\font文件等,然后将他们输出浏览器可以正常解析的文件。 模块化:模块化开发是一种管理方式,一种生产方式,一种解决问题的方案,他按照功能将一个软件分成许多部分单独开发,然后再组装起来,每一个部分即为模块。提供开发效率,方便后期的维护。

8、闭包

闭包就是能够读取其他函数内部变量的函数,可以延长变量的生命周期,也可创建私有的环境。 优点:可以读取其他函数的内部变量。将变量始终保存在内存中,可以封装对象的私有属性和方法。 缺点:消耗内存、使用不当会导致内存泄漏。

9、async、await、promise三者之中的区别。

async是用来定义异步函数的,打印函数名即可得到一个promise对象,言外之意可以通过这个函数名称.then这个方法。 await后面跟的是任意表达式,一般使用promise的表达式。async内部实现,有返回值,返回值用catch捕获。 promise是es6语法,promise中包含catch、async需要定义的catch.promise提供的方法会多一些,all、race等方法,async是没有的。

10、call、bind、apply三者的区别。

同:都可以动态的修改this的指向,但不会改变函数原来的this指向。

异:

  1. 执行方式不同 ----- call和apply都是同步执行,bind是异步执行

  2. 传参方式不同 ----- apply可以使用数组的方式进行传参,call和bind不可以。

  3. 修改this的性质不同,call和apply都是修改一次,bind是永久修改

11、v-for 和 v-if是否可以同时使用,v-if和v-for的优先级。

不建议直接使用,v-for的优先级高于v-if的优先级,如果同时使用的时候会导致性能的浪费。但是在vue3中对比进行了优化,v-if的优先级高于v-for的优先级。

12、防抖和节流

防抖:在规定时间内只能掉用一次,如果在规定时间内重复调用则会重新计算时间。

节流:连续时间内只能执行一次。

13、项目如何优化

  1. 减少http请求。
  2. 减少dns请求。
  3. 使用cdn加载。
  4. 图片懒加载。
  5. 路由懒加载。
  6. 减少dom元素的操作。
  7. 压缩js,css,图片。
  8. 使用雪碧图。
  9. 优化代码,使代码模块化,提高代码复用。

14、vue.delete 和 delete 的区别。

vue.delete直接删除了数组的元素,且改变了数组的长度。

delete只是删除了对应的元素使对应的元素变成了emty/undefined,数组的长度不会改变。

15、同步异步的区别

因为在单线程的语言中,代码的执行顺序是从上到下,从左到右执行的。 所以同步执行的方法则会出现在主线程中,如果上面的任务未完成则会造成线程阻塞。 但是异步的话,则会等主线程里面的任务执行完毕之后,再从eventqueue中推入到主线程中执行。

16、event loop机制

首先判断一个函数是同步还是异步,如果是同步则进入主线程,如果是异步则进入event table,然后在event table中找到满足条件的放入到event queue中,当主线程空闲的时候,会从event queue中把任务推入到主线程中。

17.localstorage、sessionstorage和cookies三者的区别。

同:都可以用来存储数据。

异:1. 存储大小;2. 存储时间;3. 安全性。

18.深拷贝 、浅拷贝

深拷贝和浅拷贝是指在赋值一个对象时,赋值的深度不同。 深拷贝是在拷贝一个对象时,直至拷贝到该对象最基层的一个类型位置,即使原对象内容发生改变后,该对象的不会受到任何影响。 浅拷贝只会拷贝该对象的第一层,如果该对象发生改变,则拷贝出来的对象也会受到影响。

19.es6中新增的操作数组的方法。

  1. map 返回一个新数组 map是对原数组加工,形成一个一一对应的数组。
  2. fifter 返回一个新数组 filter是满足条件的留下
  3. reduce 数组各项之和累加和累乘,求和,去重,
  4. some 有一个符合条件 则为true
  5. every 有一个不符合条件,则为false
  6. Array
  7. new set

20.v-for 中 key 的作用(简述)

  1. key可以提高dom更新的效率。
  2. key可以防止一些更新时出现的错误。

21.diff 算法

把树形结构分解成层级结构,只比较同级元素,不同级的节点只进行创建和删除操作。

22.数组和字符串之间的互转

--- 数组转字符串

  1. toString
  2. join

--- 字符串转数组

  1. array.form
  2. split
  3. 展开运算符
  4. 解构赋值

23.一次完成的网络请求

  1. 应用层
  2. 传输层
  3. 网络层
  4. 链路层

24.父元素加载子元素的生命周期

  1. 父组件的创建前
  2. 父组件的创建完成后
  3. 父组件的加载前 -> 子组件的创建前 -> 子组件的创建完成后 -> 子组件的加载前 -> 子组件的加载完成后
  4. 父组件的加载完成后
  5. 父组件的更新前 -> 子组件的更新前 -> 子组件的更新完成后
  6. 父组件的更新完成后
  7. 父组件的销毁前 -> 子组件的销毁前 -> 子组件的销毁完成后
  8. 父组件的销毁完成后

25.路由有几种模式。

全局导航守卫, 路由守卫, 组件内导航守卫

26.url的组成

协议、主机、端口、路径、

27.原型链

当在实例化的对象中访问一个属性时,首先会在该对象内部(自身属性)寻找,如找不到,则会向其__proto__指向的原型中寻找,如仍找不到,则继续向原型中__proto__指向的上级原型中寻找,直至找到或Object.prototype.__proto__为止(值为null),这种链状过程即为原型链。

28.watch和computed的区别。

computed:计算属性computed 必须要返回一个值 通过return来返回的。会缓存,只要数据不发生变化,就使用缓存的数据. watch:watch的回调函数里面有两个参数,分别是newval和oldval。不会缓存 只要数据发生变化 就会重新的去计算.

29.如何实现大文件上传

对大文件上传,我们希望最少做到一下几点

  1. 大文件切割,分片上传
  2. 如果有部分切片上传失败了,我们希望提醒用户重新上传,并且上传成功不需要上传
  3. 最好能有上传的进度提示

30.登录流程

请求登录接口 ------ 判断新用户/老用户 数据库/缓存 新用户注册->完成注册后生成token->token验证成功后->返回客户端 老用户验证账号密码/手机验证 ->验证成功后生成token->token验证成功后->返回客户端

31.数组为什么需要遍历

遍历数组是为了访问数据里面的每一个元素并处理每一个元素。

32.axios拦截器

请求拦截器 axios.interceptors.request.use 响应拦截器 axios.interceptors.response.use

33.vue全局定义了哪些方法,有什么用。

1.vue.extend

使用基础vue构造器,创建一个子类,参数是一个包括组件选项的对象。data选项是特例,需要注意在 vue.extend()中他必须是个函数。

2.vue.nextTick

在下次DOM更新循环结束之后执行延迟回调。再修改数据之后立即使用这个方法,获取更新后的DOM。

3.vue.set

想响应式对象中添加一个property,并确保这个心property同样是响应式的,且触发视图更新。他必须用于向响应式对象上添加新property,因为Vue无法探测普通的新增property

4.vue.delete

删除对象的property.如果对象是响应式的,确保删除能触发更新视图。这个方法主要用于避开vue不能检测到property被删除的限制,一般很少被使用。

5.vue.directive

注册或获取全局指令。

6.vue.filter

注册或获取全局过滤器

7.vue.component

注册或获取全局组件。组册还会自动使用给定的ID设置组件的名称。

8.vue.use

安装Vue.js插件。如果插件是一个对象,必须提供install方法。如果插件是一个函数,他会被作为install方法。install方法调用时,会被Vue作为参数传入。 该方法需要再调用new Vue()之前被调用。 当install方法被同一个插件多次调用,插件将只会被安装一次

9.vue.mixin

全局注册一个混入,影响注册之后所有创建的每个Vue实例。插件作者可以使用混入,向组件注入自定义的行为。不推荐在应用代码中使用

10.vue.compile

将一个模版字符串编译成render函数。只在完整版时可用。

11.vue.observable

让一个对象可响应。Vue内部会用它来处理data函数返回的对象。返回的对象可以直接用于渲染函数和计算属性内,并且会在发生变更时触发相应的更新。也可以作为最小化的跨组件转台存储器,用于简单的场景。

12.vue.version

提供字符串形式的Vue安装版本号,这对社区的插件和组件来说非常有用,你可以根据不同的版本号采取不同的策略

34.axios如何实现同步异步。

axios本身是异步的,如果要进行同步请求则需要配合async和await一起使用。

35.如何理解渐进式框架

分层设计,每层可选,不同层可以灵活接入其他方案架构模式 那么Vue分为哪几层呢?

  • declarative rendering(声明式渲染)
  • component system(组件系统)
  • client-side routing(前端路由)
  • state management(状态管理)
  • build system(构建系统)

36.rem端适配

  1. 屏幕宽为 clientWidth(px)。 设计稿宽度为 750 (px), 假设 n = clientWidth(px)/750(px);单位化简===> n= clientWidth/750 ;
  2. 将 html的 font-size: n(px);
  3. 则有 n(px) = 1(rem) ,因为1rem为根节点(html节点)字体的大小一倍;
  4. 假设有一个 div ,在设计稿测量的宽度为 ruleW(px);
  5. 则需要写入的宽度 width: 设为 w (单位暂不确定)
  6. 则有 w/clientWidth(px) = ruleW(px)/750(px) 单位化简===> w/clientWidth(px) = ruleW/750
  7. 化简 w = (clientWidth/750)ruleW(px) 化简==> w = nruleW(px) 转换 w = ruleW * n(px)
  8. 最后得出 w = ruleW * 1(rem) = ruleW(rem);

37.vuex

状态管理器 ,1、数据的存取一步到位,不需要层层传递

2、数据的流动非常清晰

3、存储在Vuex中的数据都是响应式的 状态 -> <- 视图 -> <- 操作 五个属性

  1. state 数据源
  2. getters 可以把共享数据抽取出来
  3. mutation 更改数据的方法
  4. actions 提交mutation
  5. modules store模块化

38.实现动态路由

在用户登录后先拦截路由 -> 根据用户权限渲染路由并存储到本地

39 什么是bfc

1、BFC即 Block Formatting Contexts (块级格式化上下文), 是 W3C CSS2.1 规范中的一个概念。
2、BFC是指浏览器中创建了一个独立的渲染区域,并且拥有一套渲染规则,他决定了其子元素如何定位,以及与其他元素的相互关系和作用。

40 跨域的原理

同源策略:端口、域名、协议

跨域是指浏览器不能执行其他网站的脚本,是因为浏览器的同源策略造成的,是浏览器的安全限制。

41 如何实现网页置灰效果

html {  
    filter: progid:DXImageTransform.Microsoft.BasicImage(grayscale=1);  
    -webkit-filter: grayscale(100%);  
}

42 如何实现三栏布局,中间部分自适应。

  1. flex布局
  2. float
  3. postion

43 position具有哪些属性?每个属性相对定位的对象是?sticky定位有用到过嘛。

  1. static position的默认值,有时候设置的固定定位,可以使用默认值取消。
  2. relative relative(相对定位),不影响元素本身特性,不会使元素脱离文档流,相对于自身原本位置进行偏移
  3. absolute absolute(绝对定位),使元素完全脱离文档流,绝对定位配合相对定位一起使用。
  4. fixed 固定定位,相对于body定位
  5. inherit 继承父元素的position属性,没有什么用
  6. sticky (黏性定位,吸顶效果)

44.flex:1代表什么

flex:1 ==> flex:1 1 auto flex实际上是flex-grow、flex-shrink和flex-basis三个属性的缩写

45.如何实现一个数组乱序的方法

//随机抽取法
function randomSortArray(arr) {
    var stack = [];
    while (arr.length) {
        //Math.random():返回 [0,1) 之间的一个随机数
        var index = parseInt(Math.random() * arr.length);  // 利用数组长度生成随机索引值
        stack.push(arr[index]);  // 将随机索引对应的数组元素添加到新的数组中
        arr.splice(index, 1);  // 删除原数组中随机生成的元素
    }
    return stack;
}
var arr = [1, 2, 3, 4, 5, 6];
var res = randomSortArray(arr);
console.log(res);  // [ 5, 2, 4, 6, 3, 1 ]


//主要是将数组里的索引值随机打乱,然后将当前的索引值与随机变化之后的索引值互换

function randomSortArray2(arr) {
    var len = arr.length;
    //首先从最大的数开始遍历,之后递减
    for(var i = len - 1; i >= 0; i--) {
        var randomIndex = Math.floor(Math.random() * (i + 1));  //随机索引值randomIndex是从0-arr.length中随机抽取的,因为Math.floor()方法是向下取整的,所以这里是i+1
        //下面三句相当于把从数组中随机抽取到的值与当前遍历的值互换位置
        var temp = arr[randomIndex];
        arr[randomIndex] = arr[i];
        arr[i] = temp;
    }
    //每一次的遍历都相当于把从数组中随机抽取(不重复)的一个元素放到数组的最后面
    return arr;
}
var arr = [1, 2, 3, 4, 5, 6];
var res = randomSortArray2(arr);
console.log(res);  // [ 1, 3, 5, 2, 4, 6 ]

46. 如何实现一个深拷贝方法

使用完美方案—lodash.cloneDeep

重点使用递归实现,JSON、Object.assgin、MessageChannel都可以作为补充,这基本上就已经回答的非常好了。

1.如果对象的层级比较浅:json.parse 和 json.stringify。 2.如果对象的层级比较深:可以使用递归。 3.如果对象的层级比较浅,并且需要处理函数和正则表达式的特殊表达方式等特殊类型的数据。可以使用object.assign。

47.箭头函数和普通函数的区别是什么

  1. 箭头函数比普通函数更加简洁
  2. 箭头函数没有自己的this
  3. 箭头函数继承的this指向不会被改变
  4. call()、apply()、bind()等方法不能改变箭头函数中this的指向
  5. 箭头函数不能作为构造函数使用
  6. 箭头函数没有自己的arguments
  7. 箭头函数没有prototype
  8. 箭头函数的this指向哪⾥?

48. 能说说你对promise的理解吗?以及常用的api

Promise是一个对象,它用来标识JavaScript中异步操作的状态(pending,resolve,reject)及结果(data),从语法上说,Promise是一个对象,从它这里可以获取异步操作的信息,Promise提供统一的API,各种异步操作都可以用同样的方法处理。

49. es6中set和map有什么区别,可以遍历吗?

1.map中的元素是key-value(键值对)对:关键字起到索引的作用,值则表示与索引相关联的数据;Set与之相对就是关键字的简单集合,set中每个元素只包含一个关键字。 2.set的迭代器是const的,不允许修改元素的值;map允许修改value,但不允许修改key。 其原因是因为map和set是根据关键字排序来保证其有序性的,如果允许修改key的话,那么首先需要删除该键,然后调节平衡,再插入修改后的键值,调节平衡,如此一来,严重破坏了map和set的结构,导致iterator失效,不知道应该指向改变前的位置,还是指向改变后的位置。所以STL中将set的迭代器设置成const,不允许修改迭代器的值;而map的迭代器则不允许修改key值,允许修改value值。 3.map支持下标操作,set不支持下标操作。map可以用key做下标,map的下标运算符[]将关键码作为下标去执行查找,如果关键码不存在,则插入一个具有该关键码和mapped_type类型默认值的元素至map中,因此下标运算符[]在map应用中需要慎用,const_map不能用,只希望确定某一个关键值是否存在而不希望插入元素时也不应该使用,mapped_type类型没有默认值也不应该使用。如果find能解决需要,尽可能用find。

50. commonJS和es molue的区别

  1. 两者的模块导入导出语法不同,CommonJs是通过module.exports,exports导出,require导入;ESModule则是export导出,import导入。
  2. CommonJs是运行时加载模块,ESModule是在静态编译期间就确定模块的依赖。
  3. ESModule在编译期间会将所有import提升到顶部,CommonJs不会提升require。
  4. CommonJs导出的是一个值拷贝,会对加载结果进行缓存,一旦内部再修改这个值,则不会同步到外部。ESModule是导出的一个引用,内部修改可以同步到外部。
  5. CommonJs中顶层的this指向这个模块本身,而ESModule中顶层this指向undefined。
  6. CommonJS加载的是整个模块,将所有的接口全部加载进来,ESModule可以单独加载其中的某个接口

51. array中的哪些方法可以改变原数组。

不会改变原来数组的有

  1. conact
  2. every
  3. some
  4. filter
  5. map
  6. slice

会改变原来数组的方法有

  1. pop
  2. push
  3. shift
  4. unshift
  5. reverse
  6. sort
  7. splice

52. 什么是受控组件。

受控组件即受状态控制,标签input\textarea\select的值的改变通常是根据用户输入来进行更新,所以受控组件必须要有value和onchange 非受控组件即不受状态控制,获取的数据就是相当于操作dom.一般没有value,我们就可以任务这个组件是非受控组件,但是我们可以通过defaultvalue来给初始值。

53.高阶函数,高阶组件是什么

一个函数可以就接受另一个函数作为参数

54. vue和react的区别

  1. 数据流管理的区别 react 使用redux进行状态管理,vue使用vuex进行状态管理。
  2. 数据绑定的区别 react使用的是单向数据流绑定,vue使用的是双向数据流绑定。
  3. react通过引入虚拟dom,降低了直接操作dom带来的性能损耗。在每次状态更新的时候,react会创建一个新的虚拟dom树,并于上一次的树进行比较,然后高效的更新实际DOM。
  4. Vue也使用了虚拟dom,在初始化应用时,Vue会预先计算出模板种的依赖关系,因此在重新渲染时,只需要更新依赖部分,从而提高渲染效率。

55. plus babel有什么作用。

56. 虚拟dom

57. v-model 语法糖

1.v-model是value 和 input 事件的封装。 2.提供数据的双向绑定 页面发生变化会触发,v-bind:value,数据发生变化会触发 v-on:input 3.等价于直接给input提供了一个v-bind和v-on:input事件。

58. 数据类型有几种

string number boolean object null undefined symbol symbol ES6新增数据类型,主要解决对象的属性名冲突

59. Object.assign、Object.keys、Object.values、object.entries

Object.assign(target,source),用来复制一个或者多个可枚举属性到目标对象; Object.keys(object),返回一个由参数对象键名组成的数组; Object.values(object),返回一个为参数对象属性值组成的数组; object.entries(object),返回一个为参数对象键值对组成的数组;

        let test = {
            name:'张三',
            age:'12',
            sex:'男'
        }
        
        let a = Object.assign({'job':'12243'},test)

        let objKey = Object.keys(a)
        let objValue = Object.values(a)
        let objEntries = Object.entries(a)

        console.log('a:',a,'test:',test)
        // a: {job: '程序员', name: '张三', age: '12', sex: '女'} 
        console.log('objKey:',objKey,'objValue:',objValue)
        // objKey: (4) ['job', 'name', 'age', 'sex'] objValue: (4) ['程序员', '张三', '12', '女']
        console.log(objEntries)
        //  [['job', '12243'],['name', '张三'],['age', '12'],['sex', '男']]