一天三场面试,口干舌燥要晕倒
金三银四也开始了,学了这么久的前端,也要开始上上战场接受拷打了,于是昨天开始投简历,运气比较好,约到了3场面试,但不巧的是,三场面试都在今天。
昨天晚上安抚着紧张的心情,在背着自我介绍中入睡了,于是迎来了今天的第一场面试,上午10点,废话不多说,最新面经请过目。
面经一
上午10点左右面的这家是一家小公司,规模只有10个人左右。这场面试与其说是面试,不如说是聊天,什么八股文都没问。因为我在自我介绍中提到了对AIGC非常感兴趣,所以面试官一开始就和我在聊AIGC,AI可能有哪些应用啦、聊聊deepseek啦和AI对于前端开发的影响啦之类的,聊了有20多分钟以上,之后就是聊了一下项目是怎么做的。
差不多有40分钟吧,我问他之后还有面试吗,他说本来还有线下面试的,但因为我在外地,无法线下面试,于是给我发了一个链接,一个小作业,让我完成。
总之第一场面试就是这样,面试官挺和蔼的,基本上就是在聊天。
看新机会顺便吆喝一声
技术大厂,待遇之类的给的还可以,就是偶尔有加班(放心,加班有加班费)
前、后端/测试,多地有空位,感兴趣的可以试试~
面经二
第二场面试就主打八股文了,面了25分钟左右,下午三点半。
1. 项目介绍
自我介绍完了就开始问你的项目,是不是自己写的,实现了什么功能之类的。
2. js的数据类型
第二个问题也是老生常谈的问题了,js有哪些数据类型。一开始因为很紧张回答顺序都是乱的,导致undefined和 null都没有说出来。
js的数据类型分为基本类型和引用类型。基本类型分为:
string number boolean undefined null symbol bigint
引用类型有:
object array function Date Map Set
2. map与普通对象的区别
因为提到了Map这种数据类型,于是就问了一下Map和普通对象的区别。
这里我只回答到了Map的键可以是任意类型而Object的键只能是字符串,并且Map可以迭代Object不行。
所以请看一下Map和Object的详细差别吧:
1. 键的类型
- Map: 键可以是任意类型,包括对象、函数、基本类型等。
- Object: 键只能是字符串或 Symbol,其他类型会被自动转换为字符串。
2. 大小获取
- Map: 可以通过
size
属性直接获取键值对的数量。 - Object: 需要手动计算属性数量,例如使用
Object.keys(obj).length
。
3. 性能
- Map: 在频繁增删键值对的场景下性能更好,因为它是专门为这种操作优化的。
- Object: 在频繁增删键值对的场景下性能较差,因为它不是为这种操作优化的。
4. 默认键
- Map: 没有默认键,只包含显式插入的键值对。
- Object: 有原型链,可能包含一些默认的属性和方法(如
toString
、hasOwnProperty
等),可能会与自定义键冲突。
5. 序列化
- Map: 不能直接使用
JSON.stringify
序列化,需要手动转换为数组或其他格式。 - Object: 可以直接使用
JSON.stringify
序列化。
6. 迭代
- Map: 可以直接使用
for...of
循环迭代,或者使用forEach
方法。 - Object: 需要使用
for...in
循环(注意会遍历原型链上的属性),或者使用Object.keys
、Object.values
、Object.entries
等方法。
7. 原型链
- Map: 没有原型链,不会继承额外的属性或方法。
- Object: 有原型链,可能会继承一些不期望的属性或方法。
3. vue中能实现数据响应的api
vue中能实现数据响应的api,有watch和computed,ref和reactive。
4. ref 与 reactive 的区别
ref与reactive的区别,首先第一点,ref可以包装基本类型和引用类型,reactive只能包装引用类型。第二点,ref取值要用.value,而reactive可以直接访问。
第三点,ref解构后仍然保持响应性,因为解构的是 ref
对象。
const count = ref(0); const { value } = count; // 仍然具有响应性
而reactive解构后会丢失响应性,因为解构的是普通对象。如果需要解构并保持响应性,可以使用 toRefs
。
const state = reactive({ count: 0 }); const { count } = state; // 失去响应性 const { count } = toRefs(state); // 保持响应性
5. object.defineProperty 和 proxy 的区别
1. 功能范围
- Object.defineProperty:只能拦截对象属性的读取(get)和设置(set)。只能对已知属性进行拦截,无法拦截新增属性或删除属性。适用于对单个属性的精细化控制。
- Proxy:可以拦截对象的多种操作,包括属性读取(get)、设置(set)、删除(deleteProperty)、枚举(enumerate)、函数调用(apply)等。可以拦截未知属性的操作,支持动态属性的拦截。功能更强大,适用于对整个对象的全面控制。
2. 使用方式
- Object.defineProperty:需要针对每个属性单独设置拦截器。修改对象的现有行为,而不是创建一个新对象。
const obj = {}; Object.defineProperty(obj, 'name', { get() { console.log('读取 name 属性'); return this._name; }, set(value) { console.log('设置 name 属性'); this._name = value; }, }); obj.name = 'Alice'; // 设置 name 属性 console.log(obj.name); // 读取 name 属性
Proxy
:
- 创建一个代理对象,拦截对目标对象的所有操作。
- 不修改原对象,而是返回一个新的代理对象。
const target = {}; const proxy = new Proxy(target, { get(obj, prop) { console.log(`读取 ${prop} 属性`); return obj[prop]; }, set(obj, prop, value) { console.log(`设置 ${prop} 属性`); obj[prop] = value; return true; }, }); proxy.name = 'Alice'; // 设置 name 属性 console.log(proxy.name); // 读取 name 属性
3. 拦截操作
- Object.defineProperty:仅支持 get 和 set 拦截。无法拦截以下操作:属性的删除(delete)。属性的枚举(for...in 或 Object.keys)。新增属性。
- Proxy:支持多种操作的拦截,包括:get:读取属性。set:设置属性。deleteProperty:删除属性。has:in 操作符。ownKeys:Object.keys、for...in 等。apply:函数调用(当代理目标是函数时)。等等。
4. 性能
- Object.defineProperty:性能较好,适合对少量属性进行拦截。但如果需要对大量属性进行拦截,代码会变得冗长且难以维护。
- Proxy:性能略低于 Object.defineProperty,因为需要处理更多的拦截操作。但在需要拦截多种操作或动态属性的场景下,Proxy 的性能优势更明显。
7. ref 与 reactive 的实现原理
reactive 是借助proxy的代理作用,代理该引用类型的属性,当该属性被读取值时,返回该属性的值并且为该属性添加副作用函数;当该属性被修改值时,触发掉该属性的副作用函数
ref既可以代理原始类型也可以代理引用类型,当代理的是引用类型时,返回的是一个RefImpl的实例,通过类身上的get和set属性去读取值和修改值;当代理的是引用类型时,其实走的还是reactive的调用机制。
8. 聊一聊webpack 和 vite
1. 工作原理
- Webpack:采用打包机制,将所有模块打包成一个或多个 bundle 文件。开发模式下使用 webpack-dev-server 提供服务,通过热更新(HMR)实现快速开发。构建时需要遍历所有依赖,生成依赖图,然后进行打包。
- Vite:采用基于浏览器 ES 模块的开发模式,直接利用浏览器加载模块。开发模式下使用原生 ES 模块,按需加载文件,无需打包。生产模式下使用 Rollup 进行打包,生成优化的静态文件。
2. 开发体验
- Webpack:开发模式下,随着项目规模增大,启动时间和热更新速度可能变慢。配置复杂,尤其是需要处理多种资源类型时。
- Vite:开发模式下启动速度极快,因为不需要打包,直接按需加载文件。热更新速度更快,因为只更新修改的模块。配置简单,开箱即用。
9. 前端领域的一些新框架
- Svelte:一个编译型前端框架,将组件编译为高效的原生 JavaScript 代码。无虚拟 DOM,运行时开销极小,适合构建高性能应用。SvelteKit 是 Svelte 的全栈框架,支持 SSR、SSG 和客户端渲染。
- Solid.js:一个高性能的响应式 UI 框架,语法类似 React,但无虚拟 DOM。通过细粒度的响应式更新实现高性能。适合需要极致性能的应用。
- Qwik:一个专注于即时加载性能的框架,通过延迟加载 JavaScript 实现极快的首屏加载。适合内容密集型网站,如电商或新闻站点。
10. 为什么选择vue3
选择 Vue 3 的理由可以归纳为以下几点:
- 性能更好:渲染速度更快,包体积更小。
- 开发体验更佳:组合式 API、TypeScript 支持、Vite 集成。
- 灵活性更高:适合各种规模的项目,逻辑复用更方便。
- 生态成熟:Vue Router、Pinia、Nuxt 3 等工具支持完善。
- 未来趋势:Vue 3 是 Vue 生态的未来,学习和使用 Vue 3 是前端开发的必然选择。
11.代码题
const obj = reactive({ a: 1, b: 2, }) const { a, b } = obj obj.a = 3 console.log(a);
输出值是多少?
还是1,因为解构reactive包装的对象会丢失响应性,那怎么可以让它输出3呢,用toRefs就可以了。
const obj = reactive({ a: 1, b: 2, }) const { a, b } = toRefs(obj) obj.a = 3 console.log(a);
12. 代码题
const obj = { 1: 1, '1': 2 } console.log(obj[1]);
输出结果是什么?
结果会输出2,因为对象的键值只能为字符串或symbol类型,数字1会自动转换为字符串1,于是后面的覆盖前面的。
13. 代码题
async function fn() { return 1 } fn().then(console.log)
结果会输出1,这是因为:
- async function fn() :async 关键字用于定义一个异步函数。异步函数会自动返回一个 Promise 对象。在函数体内,return 的值会被包装成一个 resolved 状态的 Promise。
- return 1:这里的 return 1 相当于 return Promise.resolve(1)。即使返回的是一个普通值(如 1),async 函数也会将其包装成一个 Promise。
- fn().then(console.log) :fn() 调用异步函数,返回一个 Promise。使用 .then() 方法处理 Promise 的 resolved 值。当 Promise 成功解决(resolved)时,then 中的回调函数 console.log 会被调用,并接收 resolved 的值(这里是 1)。
14. 代码题
function fn() { return new Promise((resolve) => { resolve(1) }).then(() => 2) } fn().then(console.log);
输出结果是2.
- new Promise((resolve) => { resolve(1); }) :创建一个新的 Promise 对象。在 Promise 构造函数中,立即调用 resolve(1),将 Promise 的状态设置为 resolved,并将值 1 作为 resolved 值。
- .then(() => 2) :then 方法用于处理 Promise 的 resolved 状态。这里的回调函数 () => 2 会接收上一个 Promise 的 resolved 值(即 1),但并没有使用它。回调函数返回 2,这会成为新的 resolved 值。
- fn().then(console.log) :调用 fn(),返回一个 Promise。这个 Promise 的最终 resolved 值是 2(由 .then(() => 2) 决定)。then(console.log) 会将 2 打印到控制台。
——转载自作者:午后书香