likes
comments
collection

大厂面试:别拯救了,拯救不了一点

作者站长头像
站长
· 阅读数 54

北京滴滴(实习)

1. 使用flex实现子元素左上角、中间、右下角布局

大厂面试:别拯救了,拯救不了一点
<html>
<head>
    <style>
        .parent {
            width: 200px;
            background-color: gray;
        }

        .child {
            background-color: pink;
            width: 50px;
            height: 50px;
        }
    </style>
</head>

<body>
    <!-- 实现左上角、中间、右下角 -->
    <div class="parent">
        <div class="child child1">
        </div>
        <div class="child child2">
        </div>
        <div class="child child3">
        </div>
    </div>
</body>

</html>

在面试官疯狂的提示下,就差明示,然后在他的拯救指导下。。。换了种题

<html>
<head>
  <style>
    .parent {
      width: 200px;
      background-color: gray;
    }
    .child {
      background-color: pink;
    }
    .sub {
        width: 50px;
        height: 50px;
        background-color: skyblue;
    }
  </style>
</head>
<body>
  <!-- 实现左上角、中间、右下角 -->
  <div class="parent">
    <div class="child child1">
        <div class="sub"></div>
    </div>
    <div class="child child2">
        <div class="sub"></div>
    </div>
    <div class="child child3">
        <div class="sub"></div>
    </div>
  </div>
</body>
</html>

诶~这个不就简单了吗,直接上手

<!-- 初始代码看3.html -->
<html>
<head>
  <style>
    .parent {
      width: 200px;
      background-color: gray;
    }
    .child {
      background-color: pink;
      display: flex;
    }
    .sub {
        width: 50px;
        height: 50px;
        background-color: skyblue;
    }
    .child2 {
        justify-content: center;
    }
    .child3 {
        justify-content: flex-end;
    }
  </style>
</head>
<body>
  <!-- 实现左上角、中间、右下角 -->
  <div class="parent">
    <div class="child child1">
        <div class="sub"></div>
    </div>
    <div class="child child2">
        <div class="sub"></div>
    </div>
    <div class="child child3">
        <div class="sub"></div>
    </div>
  </div>
</body>
</html>

2. 手写一个限制请求次数的方法

场景:把fn当作一个请求,现在creator中的2表示限制一次性最多只能发送2次请求,超过了2次就需要等上一个执行完毕才能继续。

试问:现连续执行了5次setup请求,如何在creator中限制它的请求数

function creator(count) {
    // fn: () => Promise
    function setup(fn) {
        
    }
    return setup
}
const setup = creator(2)

setup(fn)
setup(fn)
setup(fn)
setup(fn)
setup(fn)

function fn() {
    return new Promise((resolve) => {
        setTimeout(() => {
            console.log(1)
            resolve(1)
        }, 2000)
    })
}

我看了半天题目,然后面试官也等了半天。最后忍不住尝试着问道:你是在思考题目还是在思考题意?我羞涩地向面试官表示:emm...我没理解题意(其实无从下手),然后面试官缓了缓,心平气和地说到:还是拯救一下吧。。。就把题改得简单了一点点,也就一点点

function setup(arr, count) {
    
}
function fn() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log(1)
      resolve(1)
    }, 2000)
  })
}

setup([fn, fn, fn, fn, fn], 2)

来自面试官的提示:可以使用promise里中的finally

题1 答案

function creator(count) {
    let executionCount = 0;

    // fn: () => Promise
    function setup(fn) {
        if (executionCount < count) {
            executionCount++;
            return fn().finally(() => {
                executionCount--;
            });
        } else {
            console.log('已达到最大执行次数,等待上一个执行完毕。');
            return Promise.resolve(); // 或者可以选择返回一个表示等待的 Promise
        }
    }

    return setup;
}
const setup = creator(2)

setup(fn)
    .then(() => setup(fn))
    .then(() => setup(fn))
    .then(() => setup(fn))
    .then(() => setup(fn));
function fn() {
    return new Promise((resolve) => {
        setTimeout(() => {
            console.log(1)
            resolve(1)
        }, 2000)
    })
}

题2 答案

function setup(arr, count) {
  let executionCount = 0;

  function executeNext() {
      if (arr.length === 0) {
          console.log('所有任务已完成');
          return;
      }

      const currentFn = arr.shift();

      if (executionCount < count) {
          executionCount++;
          currentFn().finally(() => {
              executionCount--;
              executeNext();
          });
      } else {
          console.log('已达到最大执行次数,等待上一个执行完毕。');
          setTimeout(() => {
              currentFn().finally(() => {
                  executionCount--;
                  executeNext();
              });
          }, 1000); // 模拟等待
      }
  }

  executeNext();
}

function fn() {
  return new Promise((resolve) => {
      setTimeout(() => {
          console.log(1);
          resolve(1);
      }, 2000);
  });
}

setup([fn, fn, fn, fn, fn], 2);

3. 判断嵌套标签里文本字体大小

试问:I'am here的字体大小,为什么?

<style>
    #a {font-size:12px}
    div p{ font-size:13px }
    div .c{ font-size:14px }
    .a .b .c{ font-size:15px }
    #b{ font-size:16px }
</style>
    
<div id="a" class="a">
    <div id="b" class="b">
        <p id="c" class="c">I’am here</p>
    </div>
</div>

答案:15px,继承问题

4. 王炸!!!判断created生命周期函数里setTimeout里的this.nextTick里的值?

直接看题,在面试官心里权重很大

大厂面试:别拯救了,拯救不了一点

自己进行了一波自认为理性的分析,然后一本正经的胡说八道,面试官想了想:还是再拯救下。。。放出了改版

created() {
    setTimeout(() => {
        this.$nextTick(() => {
            console.log(this.$refs.text.innerHTML)
        })
        this.a = 3
        this.b = 4
    }, 1000)
}

题1 答案: 3,4

不理解(评论区蹲个佬)

题2 答案: 1,2

个人理解:

created是在vue实例创建完之后,挂载之前的生命周期,此时并没有实际的dom,

this.$nextTick()的执行条件是下一次dom更新完之后触发

所以在created阶段,此时data中的a和b的值已经初始化完毕,为1,2,而在created中里的setTimeout中修改a,b并没有触发dom更新,而这时我调用了this.$nextTick(),由于没有触发dom更新,所以输出的仍为1,2

5. commonjs和ESModule的区别

  1. 两者的模块导入导出语法不同,CommonJs是通过module.exports,exports导出,require导入;ESModule则是export导出,import导入。

    CommonJS:

    // moduleA.js
    const data = 'Some data';
    exports.data = data;
    
    // moduleB.js
    const moduleA = require('./moduleA');
    console.log(moduleA.data);
    

    ESModule:

    // moduleA.mjs
    export const data = 'Some data';
    
    // moduleB.mjs
    import { data } from './moduleA.mjs';
    console.log(data);
    
  2. CommonJs是运行时加载模块,ESModule是在静态编译期间就确定模块的依赖。

  3. ESModule导入路径必须是字符串常量, CommonJs导入路径可以是常量也可以是变量 ESModule:

    // moduleA.mjs
    export const message = 'Hello from moduleA';
    
    // moduleB.mjs
    // 正确的静态导入方式
    import { message } from './moduleA.mjs';
    console.log(message); // 'Hello from moduleA'
    
    // 错误的导入方式,会导致 SyntaxError
    const modulePath = './moduleA.mjs';
    import { message } from modulePath;
    console.log(message);
    
    

    CommonJS:

    // moduleA.mjs
    exports.message= 'Hello from moduleA';
    const modulePath = './moduleA'; 
    const moduleA = require(modulePath);
    console.log(moduleA.message);
    
  4. ESModule在编译期间会将所有import提升到顶部,CommonJs不会提升require。

  5. CommonJs中顶层的this指向这个模块本身,而ESModule中顶层this指向undefined。

    CommonJS:

    // moduleA.js
    console.log(this === module.exports); // true
    

    ESModule:

    // moduleA.mjs
    console.log(this === undefined); // true
    
  6. CommonJS加载的是整个模块,将所有的接口全部加载进来,ESModule可以单独加载其中的某个接口

    CommonJS:

    // moduleA.js
    const data = 'Some data';
    exports.data = data;
    exports.anotherData = 'Another data';
    
    // moduleB.js
    const { data } = require('./moduleA');
    console.log(data); // 'Some data'
    

    ESModule:

    // moduleA.mjs
    export const data = 'Some data';
    export const anotherData = 'Another data';
    
    // moduleB.mjs
    import { data } from './moduleA.mjs';
    console.log(data); // 'Some data'
    

北京顺丰同城科技(实习 150-200/天)

1. 你刚刚说的实习经历,可以简单说一说你做的那个后台管理系统?

2. 想要使全局的样式发生变化,应该如何设置(比如使用了vant中的input组件,我所有的input组件的样式都要去改变,应该怎么做?组件使用了less或者sass预编译的css语言编写样式,应该如何修改)

通过全局样式表修改: 创建一个全局的 CSS 文件,例如 global-styles.css,并在应用的主入口文件(通常是 main.jsmain.ts)中引入它:

// main.js 或 main.ts

import { createApp } from 'vue';
import App from './App.vue';
import 'vant/lib/index.css'; // 引入 Vant 样式
import './global-styles.css'; // 引入全局样式表

createApp(App).mount('#app');

global-styles.css 中,你可以设置全局样式,包括修改 Vant 中的组件样式:

/* global-styles.css */

.van-input {
  /* 修改 vant input 组件的样式 */
}

3. keep-alive你知道它缓存的是什么东西吗?

<keep-alive> 主要用于缓存组件的实例以及其渲染的虚拟 DOM。虚拟 DOM 是 Vue 在运行时维护的一种内存中的数据结构,它表示组件的 UI 结构。

当一个组件被包裹在 <keep-alive> 中时,该组件的实例和其虚拟 DOM 树都会被缓存。这包括组件的数据状态、计算属性的值、以及 DOM 结构。这样,当组件被切换到新的位置时,Vue 会直接从缓存中拿出组件的实例,并重新渲染其虚拟 DOM,而不会重新创建组件实例或重新生成整个组件的 DOM 结构。

4. 让你设计一个按钮级别的这种权限,你会怎么去考虑?

5. hash路由和history路由这两个有什么区别吗?

Hash 路由:

  • URL 表示: 使用 URL 中的哈希部分(即 # 后面的部分)来表示路由状态。例如,http://example.com/#/about

  • 实现原理: 当用户点击链接或进行前进/后退操作时,只有哈希部分会变化,而不会导致页面整体的刷新。JavaScript 监听 hashchange 事件来响应路由的变化,从而更新页面内容。

    window.addEventListener('hashchange', function (event) {
      console.log('Hash changed!', event.newURL, event.oldURL);
    });
    
    

    需要注意的是,hashchange 事件在 URL 中的哈希部分发生变化时触发,但不会在通过 JavaScript 直接调用 location.hashlocation.href 来改变哈希部分时触发。这是因为通过代码直接修改哈希部分不会触发浏览器的历史管理机制。

  • 优势: 简单易用,不需要服务器端配置,可以兼容不支持 History API 的浏览器。

  • 劣势: 哈希部分对 SEO 不友好,因为搜索引擎通常不会解析哈希部分。

History 路由:

  • URL 表示: 使用 HTML5 的 History API 来管理路由状态,URL 可以更直观,例如 http://example.com/about

  • 实现原理: 通过 History API 中的 pushStatereplaceState 方法,可以在不刷新页面的情况下改变 URL,并将新的状态推入浏览器的历史堆栈中。同时,可以通过 popstate 事件监听浏览器历史记录的变化,从而响应路由的变化。

  • 优势: URL 更友好,对 SEO 更有利,不会带有哈希部分。可以使用真实的 URL 路径。

  • 劣势: 需要服务器端的配置支持。在不支持 History API 的浏览器中,需要有一些降级方案。

7. history配置的路由变化的时候会向后端发送请求吗?后端需要做什么配置?

  • 在前端使用history模式时,路由的切换是在客户端浏览器中处理的,不会触发对后端的直接请求。

  • 如果用户直接访问history模式下的URL,浏览器会向服务器发送请求,此时后端需要进行相应的配置来处理这些请求,确保正确返回相关的内容。

后端路由配置: 后端需要配置路由以映射到相应的处理逻辑。这是确保用户直接访问的URL能够找到正确的后端资源或数据的关键。例如,如果你的前端应用有一个路由是/user/profile,那么后端需要配置一个路由处理器来处理这个路径,可能返回用户的个人资料信息。如果不配置的话,后端缺少对/a 的路由处理,将返回404错误。

8. 说一下你项目中登录的流程?

9. token过期怎么处理?

10. vue里面为什么它在v-for的时候不能用index作为它的key?

  1. 稳定性问题: 数组索引可能会在数据变动时发生变化。如果你删除或插入数组的元素,索引就会改变,这可能导致Vue失去对应关系,从而引发不必要的DOM重新渲染。

  2. 性能问题: 使用索引作为key可能导致不必要的DOM操作,因为Vue可能会错误地认为两个不同的元素实际上是同一个,从而不进行重新渲染。这可能导致渲染不一致或出现奇怪的问题。

  3. 不易调试: 当使用索引作为key时,你可能会在调试时很难追踪到问题的根本原因,不容易准确的找到错误问题发生的位置。

11. 如果说让你设计一个类似百度的那个搜索框,主要是功能上的考虑,你会考虑哪些方面?

  1. 实时搜索建议: 提供实时搜索建议,以便用户在输入的过程中就能看到可能的搜索结果,提高搜索的效率。这通常需要与后端交互,获取与用户输入匹配的建议词。

  2. 搜索历史: 支持显示用户的搜索历史,方便用户查看之前的搜索记录,并快速重新搜索。

  3. 搜索过滤和排序: 提供搜索结果的过滤和排序选项,让用户能够按时间、相关性等因素进行定制化排序和筛选。

  4. 国际化支持: 如果是全球性的搜索引擎,需要考虑多语言和多地域的支持,确保用户能够使用他们熟悉的语言进行搜索。

  5. 安全性考虑: 对用户输入进行合适的过滤和验证,以防止潜在的安全问题,如XSS攻击。

  6. 图片搜索,语音输入,OpenAI

12. (场景题)防抖节流它是有个局限性。比如说你现在用了防抖,间隔时间为0.5秒,你先输入了数字12发送了一个请求,这个时候服务器它反应比较慢,它如果超过了0.5秒的返回时间。这个时候用户又删除了一个数字,那同时发了这两个请求,这两个请求你不确定哪个对应哪个,你会怎么处理?

13. 给你设计一个组件库,按需加载的功能你觉得怎么去设计?