前言
昨天下午刚刚经历过了蔚来的一面,今天一大早就起来把这面经复盘记录一下。先说下个人感受吧,面蔚来之前笔者是去面过几家小公司的。虽然说这几家小公司的面试反馈还算不错,但由于笔者是第一次面蔚来这样的大厂,心情难免是有点紧张的,就差睡不着觉了。
也是在掘金上找了一些面经看,但感觉完全是于事无补(毕竟冰冻三尺,非一日之寒嘛)。就这样笔者忐忑的去面蔚来了,面完之后我突然觉得原来我这么强啊,所谓的大厂好像也没有想象中的那么难面嘛
。
当然玩笑话还是不能当真,面蔚来的时候还是学到了一些东西的,也巩固了一些知识点。
在笔者答完第9道题时(前9道题基本是对的),那个面试官叫我等两分钟,他叫他上级来面,当时心里窃喜我难道第一次面大厂就要过了吗,后面来看事情显然是没有这么简单,第二个面试官问的比第一个面试官问的难多了,有些题目还是被拷打了的,下面就跟笔者一起来看看这次蔚来的面试题吧。
1.自我介绍
各位 jym可以去点开这篇文章,看下其中的自我介绍然后来整合一下适合自己的自我介绍,有一个好的开场白往往能给面试官留下一个不错的印象。
2.es6新增特性
es6引入了诸如let和const进行块级作用域管理、箭头函数简化函数表达、模板字符串支持嵌入表达式、解构赋值快速提取数据、以及类和模块化还有就是新增了Symbol和BigInt这两种数据类型,面试官听到我说新增了这两种数据类型后紧接着就问了我下一个问题js有哪几种数据类型
3.js有哪几种数据类型
听到面试官问这个问题我还是有点窃喜的,因为笔者之前正好写过关于这个的文章,有还不懂的uu可以去看笔者写的七上八下:彻底掌握js的数据类型引言 这篇文章
4.行内元素和块级元素的特点(有哪些行内元素和块级元素)
行内元素(如<a>、<span>、<img>、<input>、<button>等)不换行,宽度由内容决定,适合小范围的内容标记;块级元素(如<div>、<p>、<h1>到<h6>、<ul>、<table>、<form>等)以新行开始,可设置宽高和内外边距,适合布局和定义页面结构。通过CSS的display属性可以改变这两种元素的默认显示方式。
5.css中的样式权重
笔者之前的文章也详细讲了,有不懂的uu可以去看这篇文章 CSS基础入门:关于选择器的全面解析。
6.v-show 和 v-if的用法和区别
这个问题应该算是老生常谈的一个问题了。
在本质上,
v-show 是把标签⾥的 display 设置为 none,所以⻚⾯上是可⻅的
v-if 是动态的操作DOM元素,⻚⾯上不可⻅的。
在性能上,
要是需要频繁的操作的话,肯定是v-show,因为他只是操作css的值,不会频繁触发重排。但是 v-if 是不断
的向 DOM树添加或删除元素,在⽐较少改变的时候⽐较合适。而且
v-show ⽆论任何条件,初始都会渲染,但v-if是惰性的,如果初始条件为 false,初始不会渲染 DOM ,为 true
才会渲染。因此 v-if 有更⾼的切换开销,⽽ v-show有更⾼的初始渲染开销。
7.有没有了解过History 和 HashHistory,区别是什么
History模式使用HTML5 History API来管理URL,提供更干净的路径而不带#,但需要服务器端支持以避免刷新页面时出现404错误;而HashHistory则利用URL中的哈希部分(#后的内容)进行路由跳转,不会向服务器发起请求,但URL包含#,对SEO不太友好。
笔者在这里举了个例子,我向面试官说像我经常使用的掘金和b站这种网站为了追求页面的美观,通常都会使用History模式。
8.vue中虚拟dom
在Vue中,虚拟DOM是一个轻量级的js对象,它是对真实DOM的一个抽象表示。每当应用的状态发生变化时,Vue会先更新虚拟DOM树,然后通过高效的算法比较新旧两棵虚拟DOM树的差异(这一过程称为Diff算法),最后仅将这些差异应用到真实DOM上,从而最小化直接操作真实DOM的次数,提升性能。这种方式不仅提高了效率,还使得开发更加声明式和简单,开发者只需要关注数据的变化,Vue负责处理如何高效地更新UI。
9.vue中响应式是怎么实现的,不仅仅是响应式数据
笔者在这里最先答的是使用ref和reactive来实现响应式数据,然后面试官说不仅仅是响应式数据,这时才想起来Proxy对象,Vue 3使用Proxy对象来创建一个对象的代理,以此拦截对这个对象的基本操作比如读取和写入属性。当响应式对象的属性被访问或修改时,Proxy会触发相应的处理器方法(如get、set),这些处理器允许Vue自动追踪依赖关系并在数据变化时通知相关的视图进行更新,这样就实现了无需手动干预的数据响应性。通过这种方式,Vue能够高效地监听数据变化并更新用户界面。
10.图片懒加载是什么,怎么实现
图片懒加载是一种优化技术,旨在页面初始加载时只加载用户可见区域内的图片,而其他部分的图片则在用户滚动到相应位置时才进行加载,以此来减少初始加载时间,提升页面性能。实现懒加载通常包括以下几个步骤:
首先,在HTML中为需要懒加载的图片设置data-src属性存储图片的真实路径,同时可以使用一个占位符图片作为src。
然后,通过JavaScript监听用户的滚动事件,当某个图片元素即将进入视口(浏览器窗口可见区域)时,将data-src中的图片地址赋值给src属性,从而触发图片的实际加载。
此外,现代浏览器也支持原生的懒加载方案,只需在<img>标签中添加loading="lazy"属性即可轻松实现懒加载效果,这种方式简化了实现流程,并且具有较好的兼容性和性能表现。
11.软件开发中有哪些设计模式
面试的时候我记得好像是一共有23种设计模式,但因为不确定以及不熟悉就回答了单例模式、工厂模式和代理模式,面试官就叫我详细的说了一下这三种模式。一共有如下23种设计模式:
单例模式、抽象工厂模式、工厂方法模式、建造者模式、原型模式、适配器模式、桥接模式、组合模式、装饰模式、外观模式、享元模式、代理模式、责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式、访问者模式
jym可以先看下,到时候在面试时可以挑自己熟悉的几个去聊
12.进程和线程之间有什么区别
进程是操作系统进行资源分配和调度的基本单位,每个进程都有独立的内存空间、系统资源以及一个或多个线程。线程是进程内的基本执行单元,在进程内部共享资源如内存和文件句柄,因此同一进程中的线程间切换更快,通信也更高效。简而言之,进程之间相互独立,而线程依赖于所属的进程,进程提供运行环境,线程在其中执行具体任务。多线程可以提高程序的响应速度和执行效率,但需要处理好同步与互斥的问题以避免数据不一致。
13.虚拟内存是什么
虚拟内存是一种内存管理技术,它让操作系统可以使用磁盘空间作为内存的扩展,使得应用程序认为自己拥有连续可用的内存地址空间。实际上,这些地址可能映射到物理内存或硬盘上的交换文件。当物理内存不足时,系统会自动将不活跃的数据移至硬盘上的交换区,释放物理内存给更需要的程序使用。这不仅增加了系统的总可用内存,还提高了多任务处理能力,允许每个进程拥有独立、隔离的内存空间,增强了系统的稳定性和安全性。
14.内核态和用户态是什么
到这里时笔者前面自我感觉都算良好,基本都能说的大差不差,这里当时就有点懵了,就随便扯了点 ,下面一起来看看标准答案吧
内核态和用户态是操作系统为区分不同权限级别的执行环境而设定的两种模式。内核态拥有最高权限,可以直接访问硬件资源如CPU、内存和外设,执行任何指令,常用于操作系统的底层管理任务。用户态则限制较多,只能执行受限的指令集,访问受保护的资源需要通过系统调用来请求内核的帮助。这种分离确保了系统的稳定性和安全性,防止用户程序直接干扰系统或其他用户的数据与运行环境。当需要进行I/O操作、内存分配等高级功能时,用户态程序通过系统调用切换到内核态来完成这些任务。
15.我们常会说DNS出了问题,你对这个有什么理解
DNS指的是域名解析系统,它是互联网的一项服务,用于将便于人类记忆的域名转换为计算机用于相互通信的IP地址。通过这种方式,DNS使得用户能够使用易于记忆的网址访问网站,而不需要记住复杂的IP地址。当您在浏览器中输入一个网址时,DNS服务器会将这个域名解析成相应的IP地址,从而使浏览器能够连接到正确的服务器并加载网页内容。如果DNS出现问题,可能会导致无法正确解析域名,进而影响对网站或网络服务的访问。
16.学校里学了操作系统、计算机网络吗?这些对你写代码有什么用
这个就属于开放性问题了,笔者还是比较喜欢这类问题的,因为这类问题就说明了它没有标准答案,可以肆无忌惮的去和面试官聊。像笔者这题就说的是这些计算机基础能提升写代码时的开发效率等。
17.我们开发的时候会遇到跨域问题,什么是跨域问题(知道同源策略吗,跨域问题怎么加入白名单)
跨域问题是由于浏览器的同源策略限制了不同源之间的资源交互,只有当协议、域名和端口都相同才允许直接访问。为了解决这个问题,可以采用几种方法:JSONP通过<script>标签加载数据并绕过同源限制,但它只支持GET请求且存在安全风险;CORS是一种更现代和安全的方式,通过在服务器端设置HTTP响应头如Access-Control-Allow-Origin来指定哪些源可以访问资源,对于需要发送凭据的情况还需额外配置Access-Control-Allow-Credentials;另外,还可以使用服务器代理的方法,即前端向同一域下的代理服务器发送请求,由该代理服务器转发请求到目标服务器,并将结果返回给前端,这种方法增加了复杂性但能有效避开跨域限制。根据具体需求和技术栈选择合适的解决方案非常重要。
18.怎么想到从java转前端,有什么感受
这也是属于开发性问题,当时就是讲了一下自己对比后端怎么怎么喜欢前端及一些原因
19.聊了点项目和ai,问我对ai的看法
面了几次,发现这些面试官或多或少都会问些ai相关的问题,我认为ai就是目前的趋势,我们都应当去学习ai,拥抱ai然后结合ai来写代码提高我们的编程效率。
20.两道算法
查找的N大的数,给你一个数组[2,3,4,5,6,7],当n=2的时候,输出6
刚做这道题时,笔者第一想法就是调用sort方法来实现排序,写之前我问面试官能不能用js的api的方法,他说行,等我快要写完时又突然跟我改口说不能,叫我重写...说真的,当时我都想一板砖拍他脑壳上了,没办法,谁让他是面试官,我是牛马呢,于是乎还是得老老实实重新写。
好在平常有这种题目的积累,我第一时间就想到了冒泡排序。代码如下:
function findLargestNum(arr, n) {
if (!arr.length || n < 1 || n > arr.length) return null
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
let temp = arr[j]
arr[j] = arr[j + 1]
arr[j + 1] = temp
}
}
}
return arr[arr.length - n]
}
面试时我其实是没有考虑到边界问题的,这段考虑边界的代码是后面复盘时添加的,当我写完这里时,面试官又问我能不能优化,我答在内层循环里我写了-i-1,这已经优化了,面试官又问还能再优化一下吗,我懵了,他又给了我点提示,如果是有序的呢怎么去优化?到这里脑子都要炸了,最终也是没有写出来,现在一起看下正确的优化后的代码吧
function findLargestNum(arr, n) {
if (!arr.length || n < 1 || n > arr.length) return null;
// 检查数组是否已经有序
let isSorted = true;
for (let i = 0; i < arr.length - 1; i++) {
if (arr[i] > arr[i + 1]) {
isSorted = false;
break;
}
}
if (isSorted) {
return arr[arr.length - n];
} else {
// 创建数组副本并排序
const sortedArr = [...arr].sort((a, b) => a - b);
return sortedArr[sortedArr.length - n];
}
}
看完后惊呼当时怎么那么蠢,就再写个判断就解决的事当时怎么愣是没想到呢。
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个n级的台阶总共有多少种跳法。
经典的爬楼梯问题,有兴趣的uu可以看下我写的这篇文章掌握算法精髓:用递归和动态规划解决爬楼梯问题,正好用多种方法完美的讲解了这道题目。
总结与思考
蔚来面试的时候还是有一些紧张导致本来该发挥的水平没有发挥出来,希望自己下次面大厂不要这么紧张了。经过这几次面试后,我也发现有些面试官就是喜欢pua你一下,恶心你一下,碰到这种情况大家也不要去自我矮化,认为自己就真比他差到哪去。面试官只是说比我们早几年接触这个业务,进入这个行业而已,说不定就是入职后的同事呢对吧 。如果有掘友面试没通过的话也不要气馁,多面几次总能进到自己想进的那家公司,最后祝大家都能找到理想的工作,进入自己想进的公司。