【面经】小红书前端社招面试分享

大家好,今天的分享由 uncle13 老师提供。

本篇面经是一位参加辅导小伙伴的真实面试经历,他的基本情况是:工作2.5年,年前被裁,准备了一个多月,是一个很努力的同学,除夕的时候还在背八股。

我们整理了下他提供的面试题,并简单作答。

小红书的架构部门比较注重基础知识的考查,对框架并没有具体要求。

一面(60min)

  1. 原型,原型链
  • 构造函数与原型:在JavaScript中,每个函数都有一个 prototype 属性,这个属性指向一个对象。当我们通过 new 关键字创建对象时,新对象的内部会包含一个指针 __proto__,指向构造函数的 prototype 对象。
  • 原型链:对象之间通过prototype属性连接起来,形成原型链。当查找对象的属性或方法时,如果当前对象不存在,就会沿着原型链向上查找。
  1. 以下代码的输出,简单讲一下为什么?
console.log('Start');

setTimeout(function({
  console.log('Timeout 1');
}, 0);

Promise.resolve().then(function({
  console.log('Promise 1');
});

Promise.resolve().then(function({
  console.log('Promise 2');
});

console.log('End');

输出解析:

  1. console.log('Start') 首先被执行,输出 "Start"。
  2. 然后遇到 setTimeout,它会被放入宏任务队列(setTimeout队列)。
  3. 接着两个 Promise.resolve().then 语句都被加入微任务队列(Promise队列)。
  4. console.log('End') 被执行,输出 "End"。

此时主线程任务执行完毕,开始执行微任务队列中的任务。

  1. 微任务队列中的第一个 Promise.resolve().then 输出 "Promise 1"。
  2. 微任务队列中的第二个 Promise.resolve().then 输出 "Promise 2"。

所有微任务执行完毕后,再执行宏任务。

  1. 此时执行 setTimeout 的回调函数,输出 "Timeout 1"。

因此,该代码的最终输出顺序是:

Start
End
Promise 1
Promise 2
Timeout 1
  1. 讲一下伪类、伪元素

    • 伪类和伪元素用于向选择器添加特殊的效果,以满足一些特殊的选择器需求。
  2. 盒模型、box-sizing

    • 盒模型描述了元素占据空间的特性。box-sizing属性用于控制盒模型的解析模式。
  3. BFC与清除浮动

    • BFC(块级格式化上下文)是页面中的一部分,它决定了其子元素如何布局。清除浮动意味着使父元素能够包含浮动的子元素。
  4. 选择器优先级

    • 选择器的优先级用于确定样式的应用顺序。通常由内联样式 > ID选择器 > 类选择器 > 标签选择器。
  5. min-width、max-width、width的包含(优先级关系)关系

    • min-width指定元素的最小宽度,max-width指定元素的最大宽度。在调整窗口大小时,元素的宽度会受到这三者的影响。
  6. 输入URL到渲染页面的全过程

    • 包括 DNS解析、TCP连接、发送HTTP请求、服务器处理请求、接收响应、浏览器解析、渲染等过程。
  7. 8中哪些阶段可以优化提升效率,你做过哪些性能优化工作

    • DNS解析、TCP连接、HTTP请求、页面渲染过程等均可进行优化。
  8. 强缓存、协商缓存发生在8中的哪些阶段

    • 强缓存和协商缓存是在接收响应后,根据缓存头信息判断是否使用本地缓存的过程。
  9. CDN

    • CDN(内容分发网络)是将网站的静态资源分发到全球各地的边缘节点,以加速用户对这些资源的访问速度。
  10. TLS/SSL

    • TLS(传输层安全)和 SSL(安全套接层)都是加密协议,用于确保数据在客户端和服务器之间的安全传输。
  11. vue router 和 route的区别

    • Vue Router是Vue.js官方的路由管理器,而route是指具体的路由路径。
  12. vue单向数据流的特点、vueX使用方式

    • Vue单向数据流指数据只能从父组件传递到子组件。VueX是Vue的状态管理工具,用于管理共享状态。
  13. es6 set和map特点和区别

    • Set是一组不重复的值的集合,Map是键值对的集合。Set是值的集合,Map是键值对的集合。
  14. 箭头函数和普通函数的区别

    • 箭头函数没有自己的this,arguments,super或new.target,因此相比普通函数更轻量且无法被作为构造函数使用。
  15. 讲一下js异步处理发展史

    • 发展历程包括回调函数、Promise、Generator、async/await等,逐步解决了回调地狱和异步编程中的复杂性。
  16. async await 原理

    • async/await是用于简化Promise的使用的语法糖,基于Promise实现,在函数前加上async标记为异步函数,内部可以使用await来等待Promise对象的状态。
  17. 手写题:Promise.all

    function customPromiseAll(promises{
      return new Promise((resolve, reject) => {
        if (!Array.isArray(promises)) {
          return reject(new TypeError('arguments should be an array'));
        }

        let results = [];
        let completedPromises = 0;

        promises.forEach((promise, index) => {
          Promise.resolve(promise)
            .then((value) => {
              results[index] = value;
              completedPromises++;
              if (completedPromises === promises.length) {
                resolve(results);
              }
            })
            .catch(reject);
        });
      });
    }

二面(50min)

  1. 介绍项目、难点、解决方案

  2. 项目私有定制功能举例

  3. 手写题:节流、防抖

节流(Throttling):

  • 作用:节流是限制函数在一定时间间隔内执行,即使触发多次也只会在固定的时间间隔后执行一次。

  • 场景:适用于需要稀释事件发生频率的情况,比如滚动事件或者鼠标移动事件。

function throttle(func, delay{
  let canRun = true;
  return function({
    if (!canRun) return;
    canRun = false;
    setTimeout(function({
      func.apply(thisarguments);
      canRun = true;
    }, delay);
  };
}

防抖(Debouncing):

  • 作用:防抖是在函数连续触发结束后,等待一段时间再执行该函数,如果在等待时间内再次触发,则重新计时。

  • 场景:适用于减少不必要的请求发送或者减轻用户输入产生的频繁操作。

function debounce(func, delay{
  let timer;
  return function({
    clearTimeout(timer);
    timer = setTimeout(() => {
      func.apply(thisarguments);
    }, delay);
  };
}
  • 节流 控制函数执行次数,以一定的频率去执行函数。
  • 防抖 控制函数的执行时机,只有在连续触发结束后才会执行。
  1. websocket如何建立连接,手写websocket建立过程

WebSocket建立连接需要经过握手(handshake)过程。下面是WebSocket建立连接的简要步骤:

  1. 客户端发起连接:客户端通过在HTTP头部添加Upgrade: websocketConnection: Upgrade字段,然后发送一个GET请求到服务器指定的WebSocket URL。

  2. 服务器回应握手:服务器接收到客户端发送的HTTP请求后,在响应头中包含Upgrade: websocketConnection: Upgrade字段,并附上Sec-WebSocket-Accept字段作为验证。

  3. 双方握手验收:客户端收到服务器的响应后,进行握手验证。如果验证通过,双方建立连接,可以开始进行双向通信。

下面是一个基本示例展示如何手写建立WebSocket连接的过程:

// 客户端代码
const socket = new WebSocket('ws://localhost:3000');

socket.onopen = function(event{
  console.log("WebSocket连接已打开");
  // 连接成功后可以发送数据
  socket.send('Hello Server');
};

socket.onmessage = function(event{
  console.log('从服务器收到消息:', event.data);
};

socket.onclose = function(event{
  if (event.wasClean) {
    console.log('WebSocket连接已关闭');
  } else {
    console.error('连接断开'); 
  }
};

socket.onerror = function(error{
  console.error('WebSocket错误:', error);
};

服务器端实现可能因语言和框架而异,以下是一个Node.js下的简单示例(使用ws库):

const WebSocket = require('ws');
const wss = new WebSocket.Server({ port3000 });

wss.on('connection'function connection(ws{
  console.log('WebSocket连接已建立');
  ws.on('message'function incoming(message{
    console.log('从客户端收到消息:', message);
    // 收到消息后可以进行处理并返回数据
    ws.send('收到您的消息:' + message);
  });
});

以上是WebSocket建立连接的简单描述和相关代码示例。在实际应用中,还需要考虑安全性、心跳检测、断线重连、协议版本等更多细节。

  1. CDN

    • 内容分发网络,用于加速用户对网站静态资源的访问。
  2. typeof、instanceof区别

    • typeof用于返回变量的数据类型,而instanceof用于检测对象的原型链是否存在一个构造函数的prototype属性。
  3. 手写题:instanceof

function myInstanceof(obj, constructor{
  if (typeof obj !== 'object' || obj === nullreturn false;
  let proto = Object.getPrototypeOf(obj);
  while (proto !== null) {
    if (proto === constructor.prototype) {
      return true;
    }
    proto = Object.getPrototypeOf(proto);
  }
  return false;
}

// 测试示例
function Person({}
const person1 = new Person();

console.log(myInstanceof(person1, Person)); // 输出 true
  1. ["1","2","3"].map(parseInt)结果,并解释原因

    • 结果为 [1, NaN, NaN]。这是因为parseInt接收两个参数,第一个是要转换的值,第二个是进制数。因为map会传入三个参数,所以导致了意外的结果。
  2. 如何让8返回[1,2,3] 用你能想到的最简单的方案(要求使用[].map())

    • 可以使用箭头函数或者自定义一个函数来解决这个问题。
  3. 怎么实现接口防刷

    • 接口防刷可以通过限制请求频率、使用验证码验证等方式来实现。
  4. DOS、DDOS攻击原理和防范

    • DOS(拒绝服务)攻击是通过发送大量合法请求导致服务不可用,DDOS(分布式拒绝服务)攻击则是通过多台机器协同进行DOS攻击。防范可以采用IP过滤、流量清洗等方式。
  5. JWT

    • JSON Web Token,用于在不同系统间传递信息的一种安全方式。
  6. 手写题:删除升序链表中重复出现的所有节点[1,2,3,3,4,4,5] => [1,2,5]

    function deleteDuplicates(head{
      let current = head;
      while (current !== null && current.next !== null) {
        if (current.val === current.next.val) {
          current.next = current.next.next;
        } else {
          current = current.next;
        }
      }
      return head;
    }

三面(50min)

  1. 工作经历有关提问

  2. 介绍项目、项目难点

  3. 平时怎么学习前端知识

  4. 项目给你的成长

  5. 工作地点考虑哪些城市

    • 说明你对工作地点的选择标准,包括城市的发展前景、生活成本、工作机会等方面。
  6. 说说你体会最有成就感的一件事

    • 分别从技术层面和非技术层面分享你觉得最有成就感的事件或事情。
  7. 进程、线程之间如何通信

    • 描述进程和线程之间通信的方式,比如共享内存、消息队列、信号量等。
  8. 浏览器处理AJAX请求和渲染页面是同一个进程吗,为什么

    • 浏览器中处理 AJAX 请求和页面渲染通常都在主线程中执行,因此它们是在同一个进程中进行。
  9. 输入URL到渲染页面的全过程

    • 包括 DNS 解析、TCP 连接、HTTP 请求响应、浏览器解析渲染等。
  10. HTTP1/HTTP1.1、HTTP2、HTTP3各自解决的问题

    • HTTP1/1.1存在的问题主要涉及并行连接限制、头部阻塞等;HTTP2引入了多路复用、头部压缩等特性;HTTP3则采用了QUIC协议,通过UDP传输数据,解决了TCP的队头阻塞问题。
  11. HTTP请求和TCP链接的对应关系

    • HTTP请求需要建立TCP连接,通过该连接来传输请求和响应的数据。
  12. 手写题:数组随机排序,写两种方案

    // 方案一
    function shuffleArray(arr{
      for (let i = arr.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [arr[i], arr[j]] = [arr[j], arr[i]];
      }
      return arr;
    }

    // 方案二
    function shuffleArray2(arr{
      return arr.sort(() => Math.random() - 0.5);
    }

面试题总结:

  1. 基础知识广泛而深入:涉及了JavaScript基础、ES6特性、异步处理、网络请求、数据结构与算法等多方面知识。

  2. 软技能考察:除技术能力外,还重点关注项目经验、沟通能力和解决问题的思路。

  3. 手写题设计理念:手写题旨在考察候选人对基础概念的理解程度和实际应用能力。

  4. 项目经验重视:通过询问项目经验,面试官希望了解候选人如何应对挑战和解决问题。

点评:

  • 重视基础架构部门注重基础,着重对候选人地基的考察。

  • 技术深度:部分问题具有一定的技术深度,要求候选人有较为扎实的技术功底。

  • 软技能考察:强调项目经验和解决问题的能力,表明该公司注重候选人的整体素养。

  • 手写题目设计:手写题注重实际应用场景,考察候选人对基础功能的掌握程度。

建议:

  1. 扎实基础:加强对JavaScript基础、ES6、异步处理等核心概念的理解和掌握。不同部门不同应对策略,八股文还是需要背的,一些场景提只是换了一种问法而已。

  2. 项目经验准备:准备讲解清晰、有针对性的项目经验,突出自己在项目中的贡献和解决问题的能力。

  3. 软技能展示:准备好谈论自己的沟通能力、团队合作经验和解决问题的思路,以展现出自己的全面素养。

  4. 多维度准备:除了技术准备外,也要注意对面试官可能提问的软技能问题进行准备。

  5. 实践演练:适当进行模拟面试、技术分享,以提高回答问题时的清晰度和流畅度。