2023前端面试记录-2

309 阅读5分钟

前言

今年的前端行情着实不太好。在面试太少的情况下,把握住每次面试机会,记录一下每次的面试题经历

4月初开始 投简历俩月了一共12个面试,去年疫情期间面试,半个月8个面试。欲哭无泪!!!

卷 + 少hc + 要求高 = [0] Offer

面试题如下

多个公司的面试题融合,不分公司!!!

问题:移动端是如何做适配的

答案:

  1. rem(根据根节点定义font-size) + viewport缩放【淘宝方案】
  2. 第三方插件+配置
  3. flex弹性布局
  4. 媒体查询 css3 的 @madia queries

问题:使用promise实现并发请求限制N个

答案:

(每次执行三个,一个执行完再补上一个,一直保持三个promise在执行)

image.png

  • 答案关键点:
    • promise.race()
    • 新建一个栈[]、长度
    • promise.allSettled()
    • promise.resolve().then()
    var urls = [
      "https://www.kkkk1000.com/images/getImgData/getImgDatadata.jpg",
      "https://www.kkkk1000.com/images/getImgData/gray.gif",
      "https://www.kkkk1000.com/images/getImgData/Particle.gif",
      "https://www.kkkk1000.com/images/getImgData/arithmetic.png",
      "https://www.kkkk1000.com/images/getImgData/arithmetic2.gif",
      "https://www.kkkk1000.com/images/getImgData/getImgDataError.jpg",
      "https://www.kkkk1000.com/images/getImgData/arithmetic.gif",
      "https://www.kkkk1000.com/images/wxQrCode2.png",
    ];

    function loadImg(url) {
      return new Promise((resolve, reject) => {
        const img = new Image();
        img.onload = function () {
          resolve(url);
        };
        img.onerror = reject;
        img.src = url;
      });
    }

    async function limitLoad(urls, handler, limit) {
      const promises = [];
      const queue = urls.splice(0, limit).map((url, index) => {
        const _p = handler(url);
        promises.push(_p);
        return _p.then((res) => {
          return [index, res];
        });
      });
      for (const item of urls) {
        const [index] = await Promise.race(queue);
        const _p = handler(item);
        promises.push(_p);
        queue[index] = _p.then((res) => {
          return [index, res];
        });
      }

      return Promise.allSettled(promises);
    }

    limitLoad(urls, loadImg, 3).then((res) => console.log(res));

问题:虚拟列表描述

答案:

按需显示的一种实现,只对可见区域进行渲染。 对非可见区域中的数据不渲染或部分渲染的技术,从而达到极高的渲染性能

  • 答案关键点:

    • 可视区域高度
    • item高度
    • list高度
    • 当前滚动位置
    • 偏移量(startOffset)
    • 可显示item个数
  • 定高

    • 计算当前可见区域起始数据的 startIndex
      • Math.floor(scrollTop / itemSize)
    • 计算当前可见区域结束数据的 endIndex
      • endIndex = startIndex + visibleCount
    • 计算当前可见区域的数据,并渲染到页面中
      • Math.ceil(screenHeight / itemSize)
    • 计算startIndex对应的数据在整个列表中的偏移位置startOffset并设置到列表上
      • startOffset = scrollTop - (scrollTop % itemSize);

image.png

    export default {
      name:'VirtualList',
      props: {
        //所有列表数据
        listData:{
          type:Array,
          default:()=>[]
        },
        //每项高度
        itemSize: {
          type: Number,
          default:200
        }
      },
      computed:{
        //列表总高度
        listHeight(){
          return this.listData.length * this.itemSize;
        },
        //可显示的列表项数
        visibleCount(){
          return Math.ceil(this.screenHeight / this.itemSize)
        },
        //偏移量对应的style
        getTransform(){
          return `translate3d(0,${this.startOffset}px,0)`;
        },
        //获取真实显示列表数据
        visibleData(){
          return this.listData.slice(this.start, Math.min(this.end,this.listData.length));
        }
      },
      mounted() {
        this.screenHeight = this.$el.clientHeight;
        this.start = 0;
        this.end = this.start + this.visibleCount;
      },
      data() {
        return {
          //可视区域高度
          screenHeight:0,
          //偏移量
          startOffset:0,
          //起始索引
          start:0,
          //结束索引
          end:null,
        };
      },
      methods: {
        scrollEvent() {
          //当前滚动位置
          let scrollTop = this.$refs.list.scrollTop;
          //此时的开始索引
          this.start = Math.floor(scrollTop / this.itemSize);
          //此时的结束索引
          this.end = this.start + this.visibleCount;
          //此时的偏移量
          this.startOffset = scrollTop - (scrollTop % this.itemSize);
        }
      }
    };

  • 非定高

追问:那懒加载呢?是什么原理

答案:

图片先用占位符表示,不要将图片地址放到src属性中,而是放到其它属性(data-original)中 页面加载完成后,监听窗口滚动,当图片出现在视窗中时再给它赋予真实的图片地址,也就是将data-original中的属性拿出来放到src属性中 在滚动页面的过程中,通过给scroll事件绑定lazyload函数,不断的加载出需要的图片

  • 答案关键点:
    • data-src
    • src默认图片占位符
    • 监听窗口滚动

问题: BFC

答案:

块级格式化上下文,是css的一个布局的统称,独立的渲染区域。让处于BFC内部的元素与外部的元素相互隔离,使内外元素的定位不会相互影响。

  • 触发条件:

    1. 根元素
    2. float不为none
    3. overflow不为visible
    4. display的值为inline-block、inline-flex、flex、flow-root、table-caption、table-cell。
    5. position的值为absolute或者fixed
  • 作用:

    1. 开启bfc不会被浮动元素覆盖
    2. 开启bfc的元素子元素和父元素不会重叠
    3. 开启bfc的元素高度不会塌陷
    4. 取消margin的重叠

问题:解释闭包

答案:

(真的被这个问题问到怀疑人生!!!)

父函数被销毁的情况下,返回的子函数的[[scope]]中仍然保留着腹肌的单变量对象和作用域链,因此可以继续访问到父级的变量对象

  • 解决了什么问题

    • 维持变量,不被垃圾回收
    • 实现变量、方法私有化
  • 用途

    • 可以读取函数内部的变量
    • 让这些变量的值一直保持在内存中
  • 闭包产生的内存泄露怎么办

    • 退出函数前,将不使用的局部变量赋值为null
    • 避免变量的循环赋值和引用

问题:数组有多少个遍历方法,性能比较

答案:

  • for : 频率最高,性能中等 但仍有优化空间
  • 优化的for : 提取变量放在第一个参数里
  • forEach : 频率较高,性能比for弱
  • for in : 效率最低 性能最差
  • map : 代码优雅,但效率低,比forEach还差
  • forof : 性能比forin好,比for循环差
  • while : 效率较好

image.png

追问:map foreach

  • 相同点:

    1. 循环遍历每一项,
    2. 每次遍历都有三个参数,
    3. 匿名函数中this都是指window,
    4. 都可以在cb中改变原数组
  • 不同点:

    • map:

        1. 有返回值,可以return出一个length和原数组一样的数组;
        1. 会分配内存空间存储新数组并返回
    • foreach:

        1. 没有返回值,是undefined;
        1. return不会终结遍历,除了异常不能终止 3. 不会分配空间

追问:forof forin

  1. for in 用它可以遍历数组,对象,集合。遍历数组遍历的值是数组index索引,遍历对象和集合时遍历的是key值。

    1. 遍历顺序有可能不是按照实际数组的内部顺序
    2. 会遍历数组所有的可枚举属性,包括原型。最好不要遍历数组
  2. for of 是es6 新加入的语法,适用于遍历数组,字符串,map/set等拥有iterator迭代器的的集合。

    1. 不能直接遍历对象,会报错。因为Object对象中没有内置的迭代器iterator