一天三场面试,口干舌燥要晕倒

金三银四也开始了,学了这么久的前端,也要开始上上战场接受拷打了,于是昨天开始投简历,运气比较好,约到了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: 有原型链,可能包含一些默认的属性和方法(如 toStringhasOwnProperty 等),可能会与自定义键冲突。

5. 序列化

  • Map: 不能直接使用 JSON.stringify 序列化,需要手动转换为数组或其他格式。
  • Object: 可以直接使用 JSON.stringify 序列化。

6. 迭代

  • Map: 可以直接使用 for...of 循环迭代,或者使用 forEach 方法。
  • Object: 需要使用 for...in 循环(注意会遍历原型链上的属性),或者使用 Object.keysObject.valuesObject.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 的理由可以归纳为以下几点:

  1. 性能更好:渲染速度更快,包体积更小。
  2. 开发体验更佳:组合式 API、TypeScript 支持、Vite 集成。
  3. 灵活性更高:适合各种规模的项目,逻辑复用更方便。
  4. 生态成熟:Vue Router、Pinia、Nuxt 3 等工具支持完善。
  5. 未来趋势: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,这是因为:

  1. async function fn() :async 关键字用于定义一个异步函数。异步函数会自动返回一个 Promise 对象。在函数体内,return 的值会被包装成一个 resolved 状态的 Promise。
  2. return 1:这里的 return 1 相当于 return Promise.resolve(1)。即使返回的是一个普通值(如 1),async 函数也会将其包装成一个 Promise。
  3. 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.

  1. new Promise((resolve) => { resolve(1); }) :创建一个新的 Promise 对象。在 Promise 构造函数中,立即调用 resolve(1),将 Promise 的状态设置为 resolved,并将值 1 作为 resolved 值。
  2. .then(() => 2) :then 方法用于处理 Promise 的 resolved 状态。这里的回调函数 () => 2 会接收上一个 Promise 的 resolved 值(即 1),但并没有使用它。回调函数返回 2,这会成为新的 resolved 值。
  3. fn().then(console.log) :调用 fn(),返回一个 Promise。这个 Promise 的最终 resolved 值是 2(由 .then(() => 2) 决定)。then(console.log) 会将 2 打印到控制台。

——转载自作者:午后书香

全部评论

相关推荐

不放弃的小鱼干很洒脱:好可爱的离职理由
点赞 评论 收藏
分享
评论
2
6
分享

创作者周榜

更多
牛客网
牛客企业服务