面试岗位是高级前端开发岗,要求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; // 如果完全相同,则保持顺序
});
}