2024年前端面试遇到的手写代码题总结

237 阅读3分钟

面试岗位是高级前端开发岗,要求3-5年工作经验。面试内容主要由前端基础知识问答+工作项目细节问题+手写代码题组成。这次主要分享面试中是实际遇到过的手写代码题。附上的答案仅供参考。

1. rbga转换为hex

function rgbaStringToHex(rgbaStr) {

  if (!rgbaStr.startsWith('rgba(') || !rgbaStr.endsWith(')')) {
       throw new Error("Invalid RGBA format.");
  } else {
       // 去掉 "rgba"、括号和空格
      rgbaStr = rgbaStr.replace('rgba(', '').replace(')', '').trim();
      
      // 将字符串按逗号分割为数组,并将每个值转为数字
      let [r, g, b, a] = rgbaStr.split(',').map(num => num.trim());
    
      // 将透明度从0-1的小数转换为0-255的整数
      a = Math.round(parseFloat(a) * 255);
    
      // 将每个值转换为16进制并保证两位数
      const toHex = (num) => {
        const hex = parseInt(num).toString(16);
        return hex.length === 1 ? '0' + hex : hex;
      };
    
      // 拼接成hex字符串
      const hex = `#${toHex(r)}${toHex(g)}${toHex(b)}${toHex(a)}`;
    
      return hex;
  }
}

2.写一个Dialog类,可以创建一个可拖动 / 拖拽的对话框

class Dialog {
    constructor(title = "Dialog Title", content = "This is a draggable dialog.") {
        // 创建对话框元素
        this.dialogElement = document.createElement('div');
        this.dialogElement.style.width = '300px';
        this.dialogElement.style.padding = '20px';
        this.dialogElement.style.border = '1px solid #ccc';
        this.dialogElement.style.backgroundColor = '#fff';
        this.dialogElement.style.position = 'absolute';
        this.dialogElement.style.display = 'none';

        // 创建标题部分
        const header = document.createElement('div');
        header.innerText = title;
        header.style.backgroundColor = '#f1f1f1';
        header.style.padding = '10px';
        header.style.position = 'relative';

        // 创建关闭按钮
        const closeButton = document.createElement('span');
        closeButton.innerText = 'x';
        closeButton.style.cursor = 'pointer';
        closeButton.style.color = 'red';
        closeButton.style.float = 'right';
        closeButton.addEventListener('click', () => this.close());

        // 将标题和关闭按钮添加到对话框
        header.appendChild(closeButton);
        this.dialogElement.appendChild(header);

        // 创建内容部分
        const contentDiv = document.createElement('div');
        contentDiv.innerText = content;
        contentDiv.style.marginTop = '10px';
        this.dialogElement.appendChild(contentDiv);
        this.dialogElement.style.cursor = 'move';

        // 将对话框添加到文档
        document.body.appendChild(this.dialogElement);

        // 初始化拖动相关变量
        this.isDragging = false;
        this.offsetX = 0;
        this.offsetY = 0;

        // 绑定鼠标事件
        document.addEventListener('mousedown', (event) => this.onMouseDown(event));
        document.addEventListener('mouseup', () => this.onMouseUp());
        document.addEventListener('mousemove', (event) => this.onMouseMove(event));
    }

    open(x = 100, y = 100) {
        this.dialogElement.style.left = `${x}px`;
        this.dialogElement.style.top = `${y}px`;
        this.dialogElement.style.display = 'block';
    }

    close() {
        this.dialogElement.style.display = 'none';
    }

    onMouseDown(event) {
        this.isDragging = true;
        this.offsetX = event.clientX - this.dialogElement.getBoundingClientRect().left;
        this.offsetY = event.clientY - this.dialogElement.getBoundingClientRect().top;
        event.preventDefault(); // 防止选择文本
    }

    onMouseUp() {
        this.isDragging = false;
    }

    onMouseMove(event) {
        if (this.isDragging) {
            const x = event.clientX - this.offsetX;
            const y = event.clientY - this.offsetY;
            this.dialogElement.style.left = `${x}px`;
            this.dialogElement.style.top = `${y}px`;
        }
    }
}

// 使用示例
const myDialog = new Dialog("My Dialog", "This is a draggable dialog.");
myDialog.open(200, 150); // 打开对话框,位置 (200, 150)

3.手写一个计时器,实现开始/暂停/重置功能

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>计时器</title>
  <style>
    .timer-container {
      width: 300px;
      height: 200px;
      background-color: aliceblue;
    }
  </style>
</head>
<body>
  <div class="timer-container">
    <h1>计时器</h1>
    <div class="timer">00:00:00</div>
    <button id="controlButton">开始</button>
    <button id="resetButton">重置</button>
  </div>
  <script>
    let timer;
    let isRunning = false;
    let elapsedTime = 0;

    const timerDisplay = document.querySelector('.timer');
    const controlButton = document.getElementById('controlButton');
    const resetButton = document.getElementById('resetButton');

    function updateTimer() {
      if(isRunning) {
        elapsedTime += 100;
        const totalSeconds = Math.floor(elapsedTime / 1000);
        const minutes = String(Math.floor(totalSeconds / 60)).padStart(2, '0');
        const seconds = String(totalSeconds % 60).padStart(2, '0');
        const mSeconds = String(Math.floor(elapsedTime % 1000) / 10).padStart(2, '0').slice(0,2);
        timerDisplay.textContent = `${minutes}:${seconds}:${mSeconds}`;
      }
    }
    function toggleTimer() {
      if(isRunning) {
        clearInterval(timer);
        controlButton.textContent = '开始';
      }else {
        timer = setInterval(updateTimer, 100);
        controlButton.textContent = '暂停';
      }
      isRunning = !isRunning;
    }
    function resetTimer() {
      clearInterval(timer);
      isRunning = false;
      elapsedTime = 0;
      timerDisplay.textContent = '00:00:00';
      controlButton.textContent = '开始';
    }
    controlButton.addEventListener('click', toggleTimer);
    resetButton.addEventListener('click', resetTimer);
  </script>
</body>
</html>

4.实现Typescript 工具类型Required Partial Omit

// 可选属性变为必选属性 的类型工具
type Required<T> = {  
  [P in keyof T]-?: T[P];  
};

// 必选属性变为可选属性 的类型工具
type Partial<T> = {  
  [P in keyof T]?: T[P];  
};

// 从现有的类型中“省略”一些属性,从而创建一个新的类型
type Omit<T, K extends keyof T> = {  
  [P in keyof T as P extends K ? never : P]: T[P];  
}; 

5.最长无重复子串

// Input s = "abcabcbb" 
// Output: 3 
// Explanation: The answer is "abc", with the length of 3.
var lengthOfLongestSubstring = function(s) {
    let left = 0;
    let res = 0;
    let map = new Map();
    for(let right=0; right<s.length; right++){
        if(map.has(s[right]) && left <= map.get(s[right])) {
            left = map.get(s[right])+1;
        }
        res=Math.max(res, right-left+1);
        map.set(s[right],right);
    }
    return res;
}

6.版本号排序

// 版本号排序:
// 输入: ['2.1.0.1', '0.402.1', '10.2.1', '5.1.2', '1.0.4.5']
// 输出: ['10.2.1', '5.1.2', '2.1.0.1', '1.0.4.5', '0.402.1']
function versionSortDesc(versions) {
  return versions.sort((a, b) => {
      const v1 = a.split('.').map(Number); // 将版本号按点分割并转为数字数组
      const v2 = b.split('.').map(Number);
      
      for (let i = 0; i < Math.max(v1.length, v2.length); i++) {
          const num1 = v1[i] || 0; // 如果长度不足,用0补齐
          const num2 = v2[i] || 0;
          
          if (num1 !== num2) {
              return num2 - num1; // 降序排序
          }
      }
      return 0; // 如果完全相同,则保持顺序
  });
}