今天下午去了一个线下面试,书面考试+技能面,这是我人生中第一次面着面着心态炸了的面试,成果十分糟糕。现在回想起来,不管是在心态上,仍是在根底知识上,我都存在着许多问题。为了直面我自己存在的问题,所以有了这篇复盘文章。

书面考试题复盘

下面是在书面考试中我答复的欠好的题目:

  • 什么是防抖和节流,具体怎么完成

  • map、forEach 和 for 的区别

  • ReactsetState什么时候是同步的,什么时候是异步的

  • 怎么做到监听某个组件特点更新时才去调用函数履行

  • 说说Vue数据的双向绑定,并说说Model是怎么改动ViewView是怎么改动 Model

  • 介绍下BFC及其运用

  • 异步输命题:

    async function async1() {
      console.log("async1 start");
      await async2();
      console.log("async1 end");
    }
    async function async2() {
      console.log("async2");
    }
    console.log("script start");
    setTimeout(function () {
      console.log("setTimeout");
    }, 0);
    async1();
    new Promise(function (resolve) {
      console.log("promise1");
      resolve();
    }).then(function () {
      console.log("promise2");
    });
    console.log("script end");
    
  • 数组扁平化+排序(不能运用数组原生 api)

    var arr = [[1,2,2],[3,4,5,5],[6,7,8,9,[11,12,[12,13,[14]]]],10]

回头看看,这些都是很根底的问题。下面提供上面问题的答复(部分答案来自chatGPT)。

1.什么是防抖和节流,具体怎么完成

这两个函数都是闭包的运用,很常考。

debounce

function debounce(fn, time) {
  let timer = null;
  return function (...args) {
    if (timer) clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, args);
    }, time);
  };
}

throttle

function throttle(fn, time) {
  let lock = false;
  return function (...args) {
    if (lock) return;
    lock = true;
    setTimeout(() => {
      fn.apply(this, args);
      lock = false;
    }, time);
  };
}

2.map、forEach 和 for 的区别

三者都可用于数组的遍历,其中map更多用于将一个数组处理成另外一个数组的场景。

至于forEachfor的区别嘛,能够参阅:有了 for 循环 为什么还要 forEach? – 掘金 ()。除了上面文章提及的内容,还有结合async/await运用时的问题:当 async/await 遇上 forEach – 掘金 ()。

3.React 中 setState 什么时候是同步的,什么时候是异步的

React中,setState有时是同步的,有时是异步的,具体取决于怎么调用它。当你在 React 生命周期办法或事件处理程序中调用setState时,它是异步的。这是由于React将一切的setState调用推迟到后续的更新批处理中进行,这样能够减少更新次数并提高性能。在这种状况下,React将更新队列中的一切状况更新合并在一起,然后一次性进行 DOM 更新。

然而,在异步代码中调用setState时,它是同步的。例如,在定时器回调函数中调用setStateReact会立即更新状况,而不会等候批处理。

你能够运用回调函数作为setState的第二个参数来处理异步setState的状况。在回调函数中运用最新的状况,而不是依赖于setState的成果。

4.React 怎么做到监听某个组件特点更新时才去调用函数履行

在 React 中,你能够运用组件的生命周期办法componentDidUpdate()来监听组件特点的更新,并在特点更新后调用函数履行。

componentDidUpdate()办法会在组件更新后被调用,而且会传入两个参数:prevPropsprevState。你能够在这个办法中比较当时的特点和状况与前一个特点和状况,来确定是否需求调用某个函数。

例如,假定你有一个<Counter>组件,它承受一个count特点和一个onUpdate回调函数。你希望在count特点更新时调用onUpdate函数,你能够在componentDidUpdate()办法中进行比较,如下所示:

class Counter extends React.Component {
  componentDidUpdate(prevProps) {
    if (this.props.count !== prevProps.count) {
      this.props.onUpdate();
    }
  }
  render() {
    return <div>{this.props.count}</div>;
  }
}

在上面的代码中,咱们比较了当时的count特点和前一个count特点,假如它们不相等,就调用了onUpdate回调函数。

<Counter>组件的count特点更新时,React 会重新烘托组件并调用componentDidUpdate()办法。在办法中,咱们检查count特点是否已经更新,假如是,就调用onUpdate回调函数。

需求留意的是,在componentDidUpdate()办法中调用this.setState()会导致无限循环更新,因而应该谨慎运用。

5.说说 Vue 数据的双向绑定,并说说 Model 是怎么改动 View、View 是怎么改动 Model 的

Vue 中的数据双向绑定是经过数据绑架来完成的。Vue 会在初始化数据时,运用Object.defineProperty()办法将数据模型中的特点转换为“呼应式”的,然后在特点被访问或修改时,触发相应的更新操作。这个进程是在 Vue 实例化时进行的,因而,只有在 Vue 实例化后,才能开端双向绑定的作业。

当数据模型中的数据发生变化时,Vue 会自动更新视图。具体来说,当咱们改动数据模型中的某个特点时,Vue 会遍历一切运用这个特点的视图,而且更新这些视图中对应的元素的值,然后完成了数据模型到视图的绑定。

6.介绍下 BFC 及其运用

BFC(块级格式化上下文)是CSS中一个很重要的概念,它是指一个独立的块级烘托区域,具有自己的布局规则。BFC 的形成需求满意必定的条件,包括:

  • 根元素或包括它的元素的内容;

  • 起浮元素(元素的 float 不是 none);

  • 绝对定位元素(元素的 positionabsolutefixed);

  • 行内块元素(元素的 displayinline-block);

  • 表格单元格(元素的 displaytable-cell,HTML 表格单元格默以为这个值);

  • 表格标题(元素的 displaytable-caption,HTML 表格标题默以为这个值);

  • overflow 值不为 visible 的块元素。

BFC 的运用包括:

  1. 铲除起浮

在 BFC 中,起浮元素不会影响BFC中非起浮元素的布局,因而能够用BFC来铲除起浮。通常将父元素设为BFC,即可铲除其子元素的起浮影响。

  1. 避免margin堆叠

当两个相邻的元素都处于同一个 BFC 中时,它们的笔直margin会发生堆叠,这可能会影响布局。能够经过创建新的 BFC 来避免这个问题。

  1. 完成自适应两栏布局

能够运用BFC的特性来完成自适应的两栏布局。具体做法是,将左右两个元素都设置为BFC,并设置宽度和起浮等特点,然后完成自适应布局。

  1. 避免元素被起浮元素掩盖

BFC中,起浮元素不会掩盖 BFC 中的其他元素,因而能够运用BFC来避免元素被起浮元素掩盖。

事实上,我对这个概念了解不可深化,只是知道BFC能够用来铲除起浮……

6.异步输命题

async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
}
async function async2() {
  console.log("async2");
}
console.log("script start");
setTimeout(function () {
  console.log("setTimeout");
}, 0);
async1();
new Promise(function (resolve) {
  console.log("promise1");
  resolve();
}).then(function () {
  console.log("promise2");
});
console.log("script end");

下面是输出成果:

script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout

剖析:

  • 首要输出:’script start’

  • 然后履行async1()函数,输出:’async1 start’

  • 接着履行async2()函数,输出:’async2′

  • 由于async2()函数中没有异步操作,因而直接回来,继续履行async1()函数

  • async1()函数中遇到await async2(),此时async1()函数被挂起,等候async2()函数履行结束

  • 履行new Promise(),输出:’promise1′

  • 履行console.log('script end'),输出:’script end’

  • 然后履行resolve(),接着履行.then(),输出:’promise2′

  • async2()函数履行结束,async1()函数继续履行,输出:’async1 end’

  • 最终履行setTimeout(),输出:’setTimeout’

主要难点在于,await后边的代码相当于创建了一个新的微使命履行。

7.数组扁平化+排序(不能运用数组原生 api)

首要是扁平化:

const isArray = (arr) => Object.prototype.toString.call(arr) === '[object Array]'
function flatten(arr) {
  let res = []
  for(let i = 0; i < arr.length; i++) {
    if(isArray(arr[i])) res = [...res, ...flatten(arr[i])]
    else res = [...res,arr[i]]
  }
  return res
}

然后是排序(简略写个冒泡):

function sort(arr) {
  let tmp = null
  for(let i = 0; i < arr.length; i++) {
    for(let j = i + 1; j < arr.length; j++) {
      if(arr[i] > arr[j]) {
        tmp = arr[i]
        arr[i] = arr[j]
        arr[j] = tmp
      }
    }
  }
}

在面试进程中,由于我是十分冲突直接手写代码的,所以涉及到手写代码的题都没写。当然,在面试中也给面试官教育了,也起了一些口头冲突。回头想想,自己实在是心浮气躁,情商也很低。

面试进程复盘

关于面试,其实这次和面试官的沟通谈不上愉快,根本我答复他的一个问题,他的回复都是“你的答案并不是我想听到的,我想听到的是xxx而不是xxx”。作为一个只是作业一年多的普通菜鸡前端,我也自知手头项目的确乏善可陈,不过由于之前面试没有遇到过这种场面,所以这次面试我面到一半心态就开端炸了,以至于到后边闹了一些不愉快。

下面先提一下聊项目进程聊到的一些点:

  • 恳求一致封装:

    • 聊了axios的拦截器处理,以及取消恳求的办法

    • AbortController的底层逻辑完成,答复不上来(具体的答复参阅:中断操作:AbortController学习笔记 – zhoulujun – 博客园)

  • webpack优化:

    • 我提了运用swc-loader来增加编译速度,面试官觉得不可,说swc不可稳定不能上出产。

    • 说我没有说到tree-shakingWebpack 5内部有关于Tree-shaking的一些处理如scope hoisting,不过我寻思着咱们聊的是我做了啥呀……)

    • 说我没有说到他想听到的点,如用Webpack做微前端之类的东西,事实上,我只研讨了webpack在项目构建层面的东西,这个天然是说不了。

  • 事务开发方面:

    • 问了表单是怎么封装的,我提了封装了一个能依据js目标装备去生成表单的组件(事实上也没啥技能含量)。面试官不满意了,说他希望听到关于JSON Schema的运用以及低代码渠道的内容。

    • 问了项目中有没有复杂的表单,虚拟表格完成过吗,这个由于没做过也没答复上来。

    • 问了怎么完成表单,我就答复了一下Antd表单是怎么完成的,不过仍是不满意,说不是自己写出来的东西

    • 问了怎么处理有相关关系的表单项(提了运用AntduseWatch被批了,说只会调库不会自己完成)

在这个难熬的进程和面试官也聊了一些东西,他的意思是他想招做低代码的(岗位jd压根就没写……),然后我也提了下我的观点,由于我现在遇到的场景的确少,以事务开发为主,天然对这些东西接触有限,之后就给批评了。结合前面书面考试写的欠好的状况,面试官也直接跟我说面试不经过了。在面试进程中由于我心态炸了,答复问题也是有些不耐烦,最终也被面试官指出了面试情绪有问题。至此,一场失利的面试结束了。

面试往后,我觉得自己的确问题很大,也在BOSS上托HR向面试官道了个歉。

总结和经验

离任到现在已经有两个月了,由于行情的确不大行,面试机会十分少。这阵子我的心情都是处于一个很down的状况,个人的性情变得有些烦躁易怒,乃至发展到线下书面考试都有不耐烦没有写完题的状况。这次的面试算是一个经验,告诉我必须要调整好自己的心态,至少在应对面试这件事上,必定要抱以敬畏和学习的情绪去面对,而不是心浮气躁自以为是。

其次是关于面试八股文的问题,八股文里边许多手写题,有些功能我用api很快完成就不管了,实际上自己也不确定假如不运用这些api自己会不会写。然后还有对书面考试的冲突问题,这个也是我要去改动的一个点。现在前端面试的话书面考试真不少,还要用纸写代码的,所以自己平常得在纸上多练练。

除了这些现有的问题,还有一些对自己项目阅历以及未来规划的思考。项目的亮点从何挖掘,怎么让自己的项目愈加具有深度,当然这些我现在还没有太好的答案,只能说走一步看一步了。