前端(2)

54 阅读11分钟

1. 好未来

  1. 大数相加
let a = "9007199254740991"; 
let b = "1234567899999999999";

function add(a, b) {
    // 确保字符串 a 是较长的那个
    if (a.length < b.length) [a, b] = [b, a];

    // 将两个字符串反转,以便从最低位开始相加
    a = a.split('').reverse();
    b = b.split('').reverse();

    let carry = 0;  // 进位
    let result = [];  // 存储相加结果的数组

    // 遍历每一位数字进行相加
    for (let i = 0; i < a.length; i++) {
        // 将字符转换为整数
        let digitA = parseInt(a[i], 10);
        let digitB = i < b.length ? parseInt(b[i], 10) : 0;  // 如果 b 比 a 短,则超过 b 长度的部分用 0 填充

        // 计算当前位的和,加上进位
        let sum = digitA + digitB + carry;
        // 将和的个位数存入结果数组
        result.push(sum % 10);
        // 计算进位
        carry = Math.floor(sum / 10);
    }

    // 如果最后还有进位,则添加到结果数组中
    if (carry) result.push(carry);

    // 将结果数组反转并转换为字符串
    return result.reverse().join('');
}

// 测试函数
let a = "9007199254740991";
let b = "1234567899999999999";
console.log(add(a, b));  // 输出: "1243575099254740990"
  1. 优化上面的代码
  • 减少数组操作
  • 减少条件判断
  • 提升代码可读性
  1. 有技术难度的业务、技术挑战
  • 讲一下当前业务
  • ab测解耦:职责链模式
  • class转hook
  • 打包优化
  • 业务sop
  1. 组件渲染方面优化
  • 避免不必要的重新渲染
    • react.memo
    • shouldComponentUpdate 或 PureComponent
  • 使用合适的数据结构和算法 Immutable 数据结构
  • 延迟和分割渲染
    • 使用 React.lazySuspense
    • 分割代码
    // Webpack
    output: {
      filename: '[name].bundle.js',
      chunkFilename: '[name].chunk.js'
    }
    
  1. 浏览器的缓存策略
  • 强缓存
    • no-cache:强制客户端在使用缓存资源前进行重新验证。
    • no-store:禁止任何缓存,不存储响应数据。
    • public:表示响应可以被任何缓存存储(客户端和代理服务器)。
    • private:表示响应仅能被客户端缓存,不能被共享缓存存储。
    • must-revalidate:要求缓存必须重新验证过期资源。
    • Cache-Control 头部指令。
    • max-age=seconds 指定资源可以缓存的最长时间(以秒为单位。
  • 协商缓存
    • ETag:是资源的唯一标识符,通常是由服务器生成的哈希值或其他唯一标识。每次资源修改时,ETag 的值会变化。
    • Last-Modified:Last-Modified 表示资源的最后修改时间。每次资源修改时,Last-Modified 的值会更新。
  1. 浏览器的本地存储方式
  • cookie
  • localStorage
  • sessionStorage
  • IndexedDB
    IndexedDB 提供了一个低级API,用于在用户浏览器中存储大量结构化数据。
// 打开(或创建)一个数据库
let request = indexedDB.open("MyTestDatabase", 1);

request.onupgradeneeded = function(event) {
  let db = event.target.result;
  let objectStore = db.createObjectStore("customers", { keyPath: "id" });
  objectStore.createIndex("name", "name", { unique: false });
};

request.onsuccess = function(event) {
  let db = event.target.result;

  // 添加数据
  let transaction = db.transaction(["customers"], "readwrite");
  let objectStore = transaction.objectStore("customers");
  objectStore.add({ id: 1, name: "John Doe" });

  // 读取数据
  objectStore.get(1).onsuccess = function(event) {
    console.log(event.target.result);
  };
};

request.onerror = function(event) {
  console.log("Database error: " + event.target.errorCode);
};
  • File System Access API File System Access API 允许Web应用直接与用户的文件系统交互。注意:此API目前在某些浏览器中需要实验性标志。
// 请求文件访问权限
async function getFile() {
  const [fileHandle] = await window.showOpenFilePicker();
  const file = await fileHandle.getFile();
  const contents = await file.text();
  console.log(contents);
}

// 保存文件
async function saveFile() {
  const options = {
    types: [
      {
        description: 'Text Files',
        accept: { 'text/plain': ['.txt'] },
      },
    ],
  };
  const handle = await window.showSaveFilePicker(options);
  const writable = await handle.createWritable();
  await writable.write('Hello World');
  await writable.close();
}

// 调用示例
getFile();
saveFile();
  1. 提高cookie的安全性
  • 使用HttpOnly标志:使Cookie只能通过HTTP(S)请求发送,防止JavaScript在客户端访问Cookie,减小XSS攻击的风险。
  • 设置合适的过期时间和Max-Age:根据需要设置合理的过期时间或最大存活时间,避免长期存储不必要的敏感信息
  • 使用Secure标志:仅允许在HTTPS连接上传输Cookie,防止Cookie在不安全的网络中被拦截
  • 避免存储敏感信息
  • 加密Cookie的值
  • 定期清理和更新Cookie
  1. 提高编译速度、提高开发效率
  2. 跨域
  3. 移动端适配兼容性问题
  1. 事件循环机制
  • JavaScript 的事件循环机制(Event Loop)是处理异步操作的核心。它允许 JavaScript 在处理 I/O 操作(如用户输入、网络请求、定时器等)时不阻塞主线程,保持高性能和响应性
  • 概念
    • 调用栈(Call Stack):
      • 调用栈是一个 LIFO(后进先出)结构,用于跟踪正在执行的函数调用。当一个函数被调用时,它会被推入调用栈,当函数执行完成后,它会从调用栈中弹出。
    • 任务队列(Task Queue 或 Message Queue):
      • 任务队列是一个 FIFO(先进先出)结构,用于存储异步任务的回调函数。这些回调函数将在调用栈为空时被执行。
    • 微任务队列(Microtask Queue) :
      • 微任务队列也是一个 FIFO 结构,存储微任务(microtasks),如 Promise 的回调函数。微任务会在当前事件循环的任务完成后立即执行,优先级高于任务队列中的任务。
    • 事件循环(Event Loop) :
      • 事件循环是一个不断检查调用栈和任务队列的循环机制。它负责从任务队列中取出任务并将其放入调用栈中执行。
  • 原理
    • 同步任务:
      • 当 JavaScript 引擎开始执行脚本时,所有的同步任务会按照顺序被推入调用栈,并依次执行。同步任务执行完成后,调用栈会被清空。
    • 异步任务:
      • 异步任务(如 setTimeoutsetIntervalI/O 操作Promise 等)的回调函数会被放入相应的任务队列(任务队列或微任务队列)中。
    • 事件循环流程:
      • 事件循环不断地检查调用栈是否为空。
      • 如果调用栈为空,事件循环会首先检查微任务队列,依次执行所有的微任务。
      • 微任务执行完毕后,事件循环会检查任务队列,从中取出一个任务,将其推入调用栈中执行。
      • 这个过程会不断重复,以确保所有的任务都被执行。

2. 白龙马

  1. 所做的工作
  • ab测新增、迭代、下线、解耦,提升业务的灵活性和效果
  • 解决手机兼容性问题:安卓、ios
  • 根据日志、可回溯、埋点、数据大盘寻找并解决线上问题
  • 新人讲解业务逻辑,制定团队开发、业务需求评审流程sop,组织CR
  • 根据业务代码痛点,推动组内工具开发
  1. ab测、背景是什么、目的: AB测试是一种在实验设计和统计学中常用的方法,用于比较两个或多个版本的产品或服务,以确定哪个版本在用户中表现更好。这种方法常用于Web设计、产品开发、营销策略等领域
  2. 怎么处理线上问题
  • 上线完成即遇到问题
    • 回滚
    • 确认问题是否是问题
    • 如果能解决则解决,再次发版
    • 如果定位不到问题,则根据报错模拟出问题,并解决再发版
  • 可回溯定位的问题
    • 这种问题一般是根据可回溯、大盘数据、埋点异常发现的问题
    • 解决这一类的问题首先根据异常的大盘数据、埋点寻找到时间、页面、用户
    • 根据上述查看可回溯录屏,查看用户可能出现的操作
    • 根据操作前后的日志查看后端数据、前端代码是否有问题
  1. 怎么判断ab测的结论
  2. 最有成就的事
  • 负责项目重构,从需求评审、人员调配、任务分发、开发设计、测试CR、平滑上线
  • 共计50多人天
  1. 怎么看待加班
  • 加班不是关键、,高效工作才是关键
  1. 一个场景做两个页面、都需要开发
  2. 怎么判断哪个页面需要增加ab测
  3. hook和class组件的区别
  4. hook性能提升点
  5. react的key的作用
  6. 什么是高阶组件、应用场景
  7. es6新增的语法
  8. 实现一个promise
  9. 控制并发的请求数量,一起进来6个请求,限制一次最多可以发送两个请求,请求完成后进来一个新的请求,直到所有请求完成之后把结果返回出来。
// 模拟需要发送的请求列表,假设这里有6个请求需要发送
const requests = [1, 2, 3, 4, 5, 6];

// 控制并发请求数量的函数
async function controlConcurrentRequests(requests, limit) {
  // 存储当前正在进行的请求的 Promise
  const inProgress = [];

  // 存储所有请求的结果
  const results = [];

  // 定义一个辅助函数,用于处理单个请求
  const handleRequest = async (request) => {
    try {
      // 发送请求,这里用一个简单的延时模拟请求
      console.log(`Sending request ${request}`);
      await new Promise(resolve => setTimeout(resolve, 1000)); // 模拟请求耗时

      // 将请求结果存入结果数组
      results.push(`Request ${request} completed`);

      console.log(`Request ${request} completed`);
    } catch (error) {
      console.error(`Request ${request} failed:`, error);
      results.push(`Request ${request} failed`);
    }
  };

  // 开始处理请求
  for (let i = 0; i < requests.length; i++) {
    // 添加新的请求到进行中的数组
    const requestPromise = handleRequest(requests[i]);

    // 添加到进行中的数组
    inProgress.push(requestPromise);

    // 如果进行中的请求数达到限制,等待最早完成的请求
    if (inProgress.length >= limit) {
      await Promise.race(inProgress);
    }
  }

  // 等待所有请求完成
  await Promise.all(inProgress);

  // 返回所有请求的结果
  return results;
}

// 调用函数来控制并发请求
controlConcurrentRequests(requests, 2)
  .then(results => {
    console.log("All requests completed:");
    console.log(results);
  })
  .catch(error => {
    console.error("Error handling requests:", error);
  });

3. IM30

  1. rem、vw原理,响应式
  • vw:基于视口宽度的相对单位,1vw等于视口宽度的1%,例如宽1000px,1vw=10px
  • rem:是相对于根元素字体大小的单位(html)
  • 区别:
    • vw适用于响应式设计,因为它根据视口宽度进行缩放,无论视口变得多窄
    • rem适用于一致排版的场景,可以通过调整根元素的字体大小,调整使用rem元素的大小
  1. useRef
  • 访问DOM:通过useRef获取一个引用
  • 存储持久值:用来存储渲染周期不变的任意值,而不会触发重新渲染。
  • 避免闭包陷阱:通过将变量存储在ref中,可以确保在事件函数中始终访问到最新的值,而不是事件绑定时的值。
  • 存储定时器引用
  • 与forwardRef一起使用:useRef可以与React.forwardRef一起使用,将ref转发到子组件中。
  1. webPack4和5的区别
  • 性能和优化
    • 更快的构建速度
      • webpack5引入了持久缓存,通过将缓存写入磁盘,webpack5能够在后续的构建中重用缓存,加快构建速度
      • webpack4依赖内存缓存
    • 更好的Tree Shaking
      • webpack5改进了Tree Shaking算法,能更好的识别、删除未使用的代码
  • 模块联邦
    • webpack5引入了模块联邦,允许应用动态加载来自不同远程服务器的模块。4没有
    • 打包输出改进:更小的包体积
      • webpack5在打包输出上进行了改进,生成的包体积通常更小。得益于更好的代码分割和优化技术。
  1. webpack的打包流程
  • juejin.cn/post/735974…
  • 初始化阶段
    • 读取配置
    • 初始化创建compiler对象,管理整个编译过程
    • 读取入口
  • 模块递归处理
    • 解析文件
    • 使用loader
    • 解析依赖:处理模块时,webpack会解析模块的依赖关系,并将依赖模块添加到依赖图中,递归处理,直到所有依赖都被解析。
  • 输出资源
    • 模块打包:将所有模块按照依赖关系打包成一个或者多个文件
    • 使用plugin:在输出过程中,webpack会调用plugin,优化代码、注入环境变量、生成html文件
  • 生成输出文件
    • 指定到output目录
  1. 什么时候挂载自定义plugin
  • beforeRun: 在编译开始之前触发
  • compile: 在创建新的编译开始时触发
  • compilation: 在每个新的编译中触发(子编译和增量编译)
  • emit: 在生成资源到输出目录之前触发
  • afterEmit: 在生成资源到输出目录之后触发
  • done: 在编译完成后触发
  • 一个自定义plugin是具有apply方法的js类,接受一个compiler对象,通过该对象可以访问webpack的钩子
  • 插件的挂载时机与他们绑定的钩子有关’
  1. ts项目引入js第三方库导致报错,如何解决
  • 创建自定义类型声明:在src文件下创建一个.d.ts文件,然后在tsconfig.json文件中确保包含自定义类型声明文件的路径
  • 使用ts-ignore注释
  1. type、interface的区别
  • interface接口
    • ts中用来命名一个特定的结构化类型的方式,用于定义对象的形状,包括属性、方法、其他。
    • 可以扩展、合并,ts会自动合并同名接口的成员
  • Type类型别名
    • 允许为现有类型提供一个更具意义的名称。不仅限于对象类型,还可以为任何类型创建别名,包括基本类型、联合类型、元祖等。
    • 可以使用联合类型、交叉类型等高级类型,类型别名可以使用TS中所有类型系统功能。
    • 可以给现有类型起一个更具描述性的名字
    • 可以在联合类型、交叉类型中进行复杂类型定义。
  • 接口更适合定义对象的结构和方法,对类进行抽象
  • 类型别名更适用给现有类型起一个新名字,或者服用联合类型、交叉类型等复杂类型定义。
  1. 捕获react内部错误
  • try-catch
  • compnentDidCatch
  • Error Boundary
  1. js中的this
  2. flex:1