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

金三银四也开始了,学了这么久的前端,也要开始上上战场接受拷打了,于是昨天开始投简历,运气比较好,约到了3场面试,但不巧的是,三场面试都在今天。

昨天晚上安抚着紧张的心情,在背着自我介绍中入睡了,于是迎来了今天的第一场面试,上午10点,废话不多说,最新面经请过目。

面经一

上午10点左右面的这家是一家小公司,规模只有10个人左右。这场面试与其说是面试,不如说是聊天,什么八股文都没问。因为我在自我介绍中提到了对AIGC非常感兴趣,所以面试官一开始就和我在聊AIGC,AI可能有哪些应用啦、聊聊deepseek啦和AI对于前端开发的影响啦之类的,聊了有20多分钟以上,之后就是聊了一下项目是怎么做的。

差不多有40分钟吧,我问他之后还有面试吗,他说本来还有线下面试的,但因为我在外地,无法线下面试,于是给我发了一个链接,一个小作业,让我完成。

总之第一场面试就是这样,面试官挺和蔼的,基本上就是在聊天。

面经二

第二场面试就主打八股文了,面了25分钟左右,下午三点半。首先就是自我介绍,请参考这篇文章:

[](面试:百度一面,吓尿了前言 在百度的学长,帮我内推了下简历。高兴还没有两秒,就接到通知面试的电话,瞬间压力山大。小公司也 - 掘金)

1. 项目介绍

自我介绍完了就开始问你的项目,是不是自己写的,实现了什么功能之类的。

2. js的数据类型

第二个问题也是老生常谈的问题了,js有哪些数据类型。一开始因为很紧张回答顺序都是乱的,导致undefined和 null都没有说出来。

js的数据类型分为基本类型和引用类型。基本类型分为:

js 复制代码
string
number
boolean
undefined
null 
symbol 
bigint

引用类型有:

js 复制代码
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 对象。

js 复制代码
const count = ref(0);
const { value } = count; // 仍然具有响应性

而reactive解构后会丢失响应性,因为解构的是普通对象。如果需要解构并保持响应性,可以使用 toRefs

js 复制代码
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:

    • 需要针对每个属性单独设置拦截器。
    • 修改对象的现有行为,而不是创建一个新对象。
js 复制代码
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:

  • 创建一个代理对象,拦截对目标对象的所有操作。
  • 不修改原对象,而是返回一个新的代理对象。
js 复制代码
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:

    • 仅支持 getset 拦截。

    • 无法拦截以下操作:

      • 属性的删除(delete)。
      • 属性的枚举(for...inObject.keys)。
      • 新增属性。
  • Proxy:

    • 支持多种操作的拦截,包括:

      • get:读取属性。
      • set:设置属性。
      • deleteProperty:删除属性。
      • hasin 操作符。
      • ownKeysObject.keysfor...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.代码题

js 复制代码
const obj = reactive({
    a: 1,
    b: 2,
})

const { a, b } = obj
obj.a = 3
console.log(a);

输出值是多少?

还是1,因为解构reactive包装的对象会丢失响应性,那怎么可以让它输出3呢,用toRefs就可以了。

js 复制代码
const obj = reactive({
    a: 1,
    b: 2,
})

const { a, b } = toRefs(obj)
obj.a = 3
console.log(a);

12. 代码题

js 复制代码
const obj = {
    1: 1,
    '1': 2
}

console.log(obj[1]);

输出结果是什么?

结果会输出2,因为对象的键值只能为字符串或symbol类型,数字1会自动转换为字符串1,于是后面的覆盖前面的。

13. 代码题

js 复制代码
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. 代码题

js 复制代码
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 打印到控制台。
相关推荐
m0_748248941 小时前
MySQL如何解决幻读?
java·javascript·mysql
青红光硫化黑3 小时前
前端基础之组件自定义事件
前端
姑苏洛言5 小时前
从0到1搭建315答题抽奖小程序:技术踩坑与运营真相
前端
鱼樱前端5 小时前
vue3+three.js入门小demo
前端·vue.js
whyfail6 小时前
React + TypeScript 实战指南:用类型守护你的组件
前端·react.js·typescript
阿金要当大魔王~~6 小时前
post get 给后端传参数
前端·javascript·html
慌张的葡萄6 小时前
24岁裸辞,今年26岁了😭做乞丐做保安做六边形战士
前端·面试
weixin_535854226 小时前
快手,蓝禾,优博讯,三七互娱,顺丰,oppo,游卡,汤臣倍健,康冠科技,作业帮,高途教育25届春招内推
java·前端·python·算法·硬件工程
excel6 小时前
webpack 编译器
前端