2023前端面试题汇总(最新)-持续更新中_前端面试题2023(1)

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Web前端全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024c (备注前端)
img

正文

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

  1. 虚拟DOM中key的作用:

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

  1. 比较规则:

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

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

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

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

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

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

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

  1. dist打包生成的文件中有 .map 文件,可以删除。在 vue.config.js文件中配置:productionSourceMap: false
  2. 组价和路由使用懒加载、按需引入等
  3. 对于文件和图片进行压缩。 安装压缩组件: compression-webpack-plugin
    安装后进行导入配置:
    最小化代码 minisize: true
    分割代码: splitChunksl
    超过限定值的文件进行压缩,threshold: 文件大小(字节为单位)

3.8 computed 和 watch 的区别

  1. computed是计算属性,watch是监听器,用来监听某一个值的变化进而触发相应的回调
  2. computed中的函数必须要有return返回、watch没有必须的要求返回return
  3. computed是第一次加载就触发一次,watch首次加载不会触发,如果需要首次加载需要设置immediate属性
  4. computed中的函数所依赖的属性没有发生变化,那么调用当前的函数的时候会从缓存中获取;而watch在每次监听值发生变化的时候都会执行回调。

3.9 vue组件之间的数据传递

3.9.1 父 传 子

通过在子组件身上绑定自定义属性,然后再子组件里使用props属性来接收即可
​​在这里插入图片描述

3.9.2 子 传 父

1)第一种方式:通过父组件给子组件传递函数类型的props实现:子组件给父组件传递数据
父组件:
在这里插入图片描述
在这里插入图片描述

子组件:
在这里插入图片描述

2)第二种方式:通过父组件给子组件绑定一个自定义事件实现:子组件给父组件传递数据
父组件
在这里插入图片描述
在这里插入图片描述

子组件
在这里插入图片描述

3)第三种方式:通过父组件给子组件绑定一个自定义事件实现:使用ref实现
父组件
在这里插入图片描述
在这里插入图片描述

子组件
在这里插入图片描述

3.9.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)=>{

})
}

  1. 提供数据: pubsub.publish(‘xxx’, 数据)
  2. 最好在beforeDestory钩子中,用pubsub.unsubscribe(pid)取消订阅
4.0 vue原型 和 原型链

不管是普通函数还是构造函数,都有一个属性prototype,用来指向这个函数的原型对象。

同时原型对象也有一个constructor属性,用来指向这个原型对象的构造函数。

原型对象的作用:用来存储实例化对象的公有属性和方法

例如

function Person (name, age) {
this.name = name
this.age = age
this.say = function () {
console.log(‘我会说话’)
}
}

let person1 = new Person(‘小明’, 20)

console.log(person1.name) // 小明
console.log(person1.age) // 20
console.log(person1.say()) // 我会说话

如上:实例化出来的person1,就可以使用构造函数Person里面的属性和方法

总结

  1. 访问对象的一个属性,先在自身查找,如果没有,会访问对象的__proto__,沿着原型链查找,一直找到Object.prototype.proto
  2. 每个函数都有prototype属性,会指向函数的原型对象。
  3. 所有函数的原型对象的__proto__,会指向Object.prototype。
  4. 原型链的尽头是Object.prototype.proto,为null。

具体可参考下方文章链接地址(写的非常详细易懂):https://blog.csdn.net/weixin_56505845/article/details/119683904

4.1 深拷贝 与 浅拷贝

首先浅拷贝和深拷贝只针对引用类型
浅拷贝:拷贝的是地址,适合拷贝单层的数据
浅拷贝
创建一个对象,这个对象有着原始对象的值的精确拷贝。
这里要引入基本数据类型引用数据类型 ,浅拷贝的如果是基本数据类型,则拷贝的直接是这个数据的值;如果是引用类型,则是创建了一个新的指针,用来指向原类型的内存地址(即拷贝了这个内存地址),如果一个对象改变了,则会影响另一个对象

常见方法:
1、拷贝对象:Object.assign() / 展开运算符{…obj} 拷贝对象
2、拷贝数组:Array.prototype.concat() / […arr]

弊端:如果拷贝的数据复杂,此时修改拷贝对象,会影响原数据
例如:

const obj = {
name: ‘你好’,
age: 18,
family: {
son: ‘大儿子’
}
}
const newObj = {}
Object.assign(newObj, obj)
newObj.family.son = ‘小儿子’
console.log(obj) // 此时打印出来的原数据obj里面的family对象值被修改

浅拷贝代码实现:

function clone(target) {
let cloneTarget = {};
for (const key in target) {
cloneTarget[key] = target[key];
}
return cloneTarget;
};

深拷贝
与之相对,深拷贝指的是创建一个新的指针,同时也会生成一个新的内存地址,这个新的指针指向新的内存地址。与原对象是相互独立,互不影响

实现深拷贝的方法

  1. 最常用的

JSON.parse(JSON.stringify())

  1. 递归实现
    创建一个新的对象,遍历需要克隆的对象,将需要克隆对象的属性依次添加到新对象上,返回新对象。

考虑到要拷贝的对象可能嵌套很深,因此可以通过递归的方法来实现,优化上方代码:

  1. 如果是原始类型,无需拷贝,可以直接返回
  2. 如果是引用类型,可以创建一个新的对象,遍历需要克隆的对象,将需要克隆对象执行深拷贝后依次添加到新对象上

module.exports = function clone(target) {
if (typeof target === ‘object’) {
// 此处要先判断是否是数组,因为数组也属于对象
let cloneTarget = Array.isArray(target) ? [] : {};
for (const key in target) { // key 是属性名,target[key]是属性值
cloneTarget[key] = clone(target[key]); // cloneTarget[key] 是创建属性名
}
return cloneTarget;
} else {
return target;
}
};

详细可参考:https://www.jianshu.com/p/a447d7325736
3)js库lodash里面的cloneDeep实现
例如:

let obj = [{‘a’ : 1}, {‘b’: 2}]
let deepObj = _.cloneDeep(obj)
console.log(obj)

4.2 $nextTick的作用

首先我们要先明白一个道理:Vue的响应式并不是数据发生变化后,DOM立即跟着发生变化的,而是按一定的策略进行DOM更新的。

作用:$nextTick 是在下次 DOM 更新循环结束之后执⾏延迟回调,在修改数据之后使⽤ $nextTick,则可以在回调中获取更新后的 DOM,在下次 DOM 更新循环结束之后执行延迟回调

说白了就是:当数据更新了,在DOM更新后,自动执行该函数

什么时候用?

  1. Vue⽣命周期的created()钩⼦函数进⾏的DOM操作⼀定要放在Vue.nextTick()的回调函数中,原因是在created()钩⼦函数执⾏的时候,DOM 其实并未进⾏任何渲染,⽽此时进⾏DOM操作⽆异于徒劳,所以此处⼀定要将DOM操作的js代码放进Vue.nextTick()的回调函数中。
  2. 当项⽬中改变data函数的数据,想基于新的dom做点什么,对新DOM⼀系列的js操作都需要放进Vue.nextTick()的回调函数中

想了解更详细的🉑️参考:https://www.jianshu.com/p/8efa5cba7d07

4.3 Vue生命周期

可点击参考文档详细了解

4. ES6面试题

4.1 var let const 区别

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

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

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

4.2 解构赋值

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

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

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

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

let item = […new Set(arr)]

4.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,最后执行输出3

4.5 跨域的解决方法

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

解决办法:

4.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’: ‘’
}
},
}
}

4.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中 只有配置过的允许跨域:

map $http_origin KaTeX parse error: Expected '}', got '#' at position 272: …10 #̲ 允许 所有头部 所有corsHost域 所有方法
11 add_header ‘Access-Control-Allow-Origin’ KaTeX parse error: Double superscript at position 68: …Allow-Headers' '̲*'; 13 …request_method = ‘OPTIONS’) {
16 return 204;
17 }
18 }
19 …
20 }

4.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))

4.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 )

4.6 git命令(实际工作中常用)

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

  2. git status 查看文件状态

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

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

  5. git log 查看提交记录

4.6.2 分支明细

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

(2)开发分支(develop):作为开发的分支,基于 master 分支创建。

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

4.6.3 分支命令

(1)git branch 查看分支

(2)git branch 分支名称 创建分支

(3)git checkout 分支名称 切换分支

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

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

4.6.4 暂时保存更改的代码

(1)存储临时改动:git stash

(2)恢复改动:git stash pop

4.6.5 代码回退

git reset --soft HEAD~n

4.7 get与post请求有什么区别

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

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

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

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

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

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

4.8 cookie、localStorage、sessionStorage的区别

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

不同点:

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

存储大小限制也不同:

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

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

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

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

作用域不同

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

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

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

4.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中包含catch,async需要自己定义catch

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

5.0 setTimeout 时间为0, 以及误差的原因

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

5.1 求数组的最大值?

function getMaxArryNum(arr) {
return Math.max(…arr)
}

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

5.2 求数组的最小值?

const getMinArryNum= (arr) => {
return Math.min(…arr)
}
getMinArryNum([1,2,3,4,5,6])

5.3 数组去重

5.3.1 利用filter 、indexof

const removeEqual = (arr) => {
const result = arr.filter((item, index, self) => {
return self.indexof(item) === index
})
return result
}

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

第一次循环,传入元素1,index(1)的索引为0,而此时1的索引本来就是0,OK,满足。
第二次循环,传入元素2,index(2)的索引为1,而此时2的索引也是1,OK,也满足。
第三次循环,传入元素2,index(2)的索引为1,而此时2的索引为2,OK,不满足,被PASS,这里就是巧妙的借用了indexOf始终查找到第一次出现的位置。
总结
filter(x,index,self)可以为数组提供过滤功能,其中x代表元素,index是与X一同传入元素的索引,而self代表数组本身。
14. 生成从0 到 指定数字的数组

5.3.2 sort 、splice 实现去重

先对数组进行排序,再循环,如果相邻的两项相同,则删除一项,i–,再继续对比。

var arr = [1, 2, 3, 2, 4, 1];
arr.sort();

for (var i = 0; i < arr.length; i++) {
if (arr[i] === arr[i+1]) {
arr.splice(i,1);
i–;
}
}
console.log(arr); // [1, 2, 3, 4]

5.3.3 最短的方法,使用new Set([…])

var arr = [1, 2, 3, 2, 4, 1];
var newArr = new Set(arr);

console.log(newArr); // [1, 2, 3, 4];

5.3.4 indexOf 、push 实现

最简单的思路,先创建一个新数组作为容器,遍历原数组,判断每一项在新数组中是否存在,若不存在则把这一项push到新数组中,若存在则忽略。

var arr = [1, 2, 3, 2, 4, 1];
var newArr = [];

for (var i = 0; i < arr.length; i++) {
if (newArr.indexOf(arr[i]) === -1) {
newArr.push(arr[i]);
}
}

console.log(newArr); // [1, 2, 3, 4]

5.3.5 对象属性名唯一性实现 数组去重

利用对象的属性名不能重复这一特性,创建一个对象,把数组中的值依次添加为对象的属性,再使用Object.keys()得到包含所有属性名的数组。但要注意对象的属性名都是字符串形式,所以在本例中还要把它们转回数字。

var arr = [1, 2, 3, 2, 4, 1];
var obj = {};
var res = [];

arr.forEach(n => obj[n] = 1); // 把每一项添加为对象的属性,重复的属性不会再次添加,而是修改已存在的属性的值

res = Object.keys(obj).map(n => +n); // 得到包含字符串属性名的数组并把每一项转换成数字

console.log(res); // [1, 2, 3, 4]

5.4. 数组求和

const arrSum = (arr) => {

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

return pre+now

},0)

return temp

}

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

5.5 生成从0 到 指定数字的数组

const getArr = (startNum, endNum) => {

let arr = []

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

arr.push(i)

}

return arr

}

getArr(0,4)

5.6 js的数据类型

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

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

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

文末

篇幅有限没有列举更多的前端面试题,小编把整理的前端大厂面试题PDF分享出来,一共有269页

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

pre, now) => {

return pre+now

},0)

return temp

}

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

5.5 生成从0 到 指定数字的数组

const getArr = (startNum, endNum) => {

let arr = []

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

arr.push(i)

}

return arr

}

getArr(0,4)

5.6 js的数据类型

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

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

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

文末

篇幅有限没有列举更多的前端面试题,小编把整理的前端大厂面试题PDF分享出来,一共有269页

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-B5dXNaCH-1713625181928)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 14
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值