前言
大家好,我是木斯佳。
相信很多人都感受到了,在AI浪潮的席卷之下,前端领域的门槛在变高,纯粹的"增删改查"岗位正在肉眼可见地减少。曾经热闹非凡的面经分享,如今也沉寂了许多。但我们都知道,市场的潮水退去,留下的才是真正在踏实准备、努力沉淀的人。学习的需求,从未消失,只是变得更加务实和深入。
这个专栏的初衷很简单:拒绝过时的、流水线式的PDF引流贴,专注于收集和整理当下最新、最真实的前端面试资料。我会在每一份面经和八股文的基础上,尝试从面试官的角度去拆解问题背后的逻辑,而不仅仅是提供一份静态的背诵答案。无论你是校招还是社招,目标是中大厂还是新兴团队,只要是真实发生、有价值的面试经历,我都会在这个专栏里为你沉淀下来。专栏快速地址

温馨提示:市面上的面经鱼龙混杂,甄别真伪、把握时效,是我们对抗内卷最有效的武器。
面经原文内容
📍面试公司:秦丝科技
🕐面试时间:近期
💻面试形式:笔试(全手写题)
❓笔试题:
-
说明box-sizing属性
-
判断obj是否为空对象
-
判断3==true
-
小数四舍五入,3.335 → 3.34
-
反转字符串,i am developer → developer am i
-
闭包程序输出(名称随便取的)
javascriptvar a = 8; function output() { (function () { return function inner(){ var a = 10; a++; console.log(a); } })()(); } output(); output(); -
文字截断样式
-
三列响应式布局
-
防抖
-
给出一个数组包含10个对象,每个对象包含名称字符串和分数数组,要求先去掉分数数组中的最大小值,然后取平均值
来源:牛客网 牛客745503321号
💡 木木有话说(刷前先看)
AI写代码习惯了,来份笔试题收录一下。
秦丝科技这场笔试,是一份非常典型的前端基础笔试题 。全是手写题,覆盖CSS(box-sizing、文字截断、三列布局)、JS基础(对象判空、类型转换、小数精度、反转字符串、闭包输出)、防抖实现、数组对象处理。难度中等偏基础,适合校招/实习同学用来检验基本功。第6题闭包输出有点绕,需要仔细分析作用域;第4题小数四舍五入涉及浮点数精度问题,是常见坑点。
📝 秦丝科技前端笔试·深度解析
🎯 笔试整体画像
| 维度 | 特征 |
|---|---|
| 笔试风格 | 基础实战型 + 手写代码型 + 细节考察型 |
| 难度评级 | ⭐⭐⭐(三星,基础为主,闭包/精度题有坑) |
| 考察重心 | CSS基础属性、JS对象/类型转换、字符串处理、闭包作用域、数组算法 |
| 特殊之处 | 全手写无选择,考察实际编码能力 |
🔍 逐题深度解析
一、说明box-sizing属性
回答思路 :box-sizing控制元素宽高的计算方式。
取值:
content-box(默认):宽度 = content宽度,padding和border额外增加border-box:宽度 = content + padding + border,padding和border内缩
css
/* 示例 */
.box1 {
width: 200px;
padding: 20px;
box-sizing: content-box; /* 实际宽度240px */
}
.box2 {
width: 200px;
padding: 20px;
box-sizing: border-box; /* 实际宽度200px */
}
使用建议 :全局设置* { box-sizing: border-box; },布局计算更直观。
二、判断obj是否为空对象
回答思路:空对象指没有自身可枚举属性。
javascript
// 方法1:Object.keys()(最常用)
function isEmpty(obj) {
return Object.keys(obj).length === 0
}
// 方法2:JSON.stringify()
JSON.stringify(obj) === '{}'
// 方法3:for...in + hasOwnProperty
function isEmpty(obj) {
for (let key in obj) {
if (obj.hasOwnProperty(key)) return false
}
return true
}
// 注意:null/undefined需先判空
function isEmpty(obj) {
if (obj == null) return true
return Object.keys(obj).length === 0
}
三、判断3==true
答案 :false
原因 :==会进行类型转换,但true转换为数字1,3 == 1为false。
javascript
3 == true // false(true → 1)
1 == true // true(1 == 1)
0 == false // true(0 == 0)
'3' == 3 // true(字符串转数字)
扩展 :严格相等===不进行类型转换,3 === true为false。
四、小数四舍五入,3.335 → 3.34
回答思路 :JS浮点数精度问题,直接toFixed可能不准确。
javascript
// 方法1:Math.round + 乘除(注意精度)
function round(num, decimals = 2) {
const factor = Math.pow(10, decimals)
return Math.round(num * factor + 1e-10) / factor
// +1e-10 解决浮点数精度问题,如 3.335 * 100 = 333.4999999
}
console.log(round(3.335, 2)) // 3.34
// 方法2:toFixed + 修正(不推荐,有精度问题)
// 3.335.toFixed(2) 在某些环境下可能输出 '3.33'
// 方法3:使用Number.EPSILON
function round(num, decimals = 2) {
const factor = Math.pow(10, decimals)
return Math.round((num + Number.EPSILON) * factor) / factor
}
原理 :3.335在二进制中是近似值,乘以100后得到333.499999...,导致Math.round向下取整。
五、反转字符串,"i am developer" → "developer am i"
题目:单词顺序反转,不是字符反转。
javascript
function reverseWords(str) {
return str.split(' ').reverse().join(' ')
}
// 处理多个空格的情况(保留一个空格)
function reverseWordsAdvanced(str) {
return str.trim().split(/\s+/).reverse().join(' ')
}
console.log(reverseWords('i am developer')) // "developer am i"
六、闭包程序输出
题目代码:
javascript
var a = 8;
function output() {
(function () {
return function inner(){
var a = 10;
a++;
console.log(a);
}
})()();
}
output();
output();
逐步解析:
- 外层
output函数执行时,内部是一个立即执行函数表达式(IIFE) - IIFE返回
inner函数,然后立即调用 (末尾的()) inner函数内部声明了局部变量var a = 10,然后a++变为11- 这个
a是inner内部的局部变量,与外层的var a = 8无关(作用域隔离) - 每次调用
output(),都会重新执行IIFE,重新创建inner函数并调用
输出 :两次都是11
javascript
// 执行流程
output() // inner执行:var a = 10; a++ → a = 11; console.log(11)
output() // 完全独立的新执行:var a = 10; a++ → a = 11; console.log(11)
常见陷阱 :如果inner中没有var a = 10,则会向外层查找,访问外层的var a = 8并修改它(形成闭包)。
七、文字截断样式(单行/多行)
单行截断:
css
.ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
多行截断(WebKit):
css
.multi-ellipsis {
display: -webkit-box;
-webkit-line-clamp: 3; /* 显示行数 */
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
多行截断(通用方案):
css
/* 使用max-height + 伪元素渐变遮罩 */
.multi-ellipsis-fallback {
max-height: 4.5em; /* line-height * 行数 */
overflow: hidden;
position: relative;
}
.multi-ellipsis-fallback:after {
content: '...';
position: absolute;
bottom: 0;
right: 0;
background: linear-gradient(to right, transparent, white);
}
八、三列响应式布局
方案1:Flexbox
css
.container {
display: flex;
flex-wrap: wrap;
}
.column {
flex: 1;
min-width: 200px; /* 最小宽度,小于则换行 */
}
方案2:Grid
css
.container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
方案3:浮动(传统)
css
.column {
float: left;
width: 33.33%;
}
@media (max-width: 600px) {
.column { width: 100%; }
}
九、防抖
javascript
function debounce(fn, delay) {
let timer = null
return function(...args) {
clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, args)
}, delay)
}
}
// 使用示例
const debouncedSearch = debounce((keyword) => {
console.log('搜索:', keyword)
}, 500)
十、数组对象处理:去掉分数最大最小值后取平均值
题目 :数组包含10个对象,每个对象有name(字符串)和scores(分数数组),要求先去掉分数数组中的最大值和最小值,然后计算平均值。
javascript
// 示例数据结构
const students = [
{ name: '张三', scores: [85, 90, 78, 92, 88] },
{ name: '李四', scores: [75, 82, 95, 88, 79] },
// ... 共10个
]
// 处理函数
function processScores(students) {
return students.map(student => {
const scores = student.scores
if (!scores || scores.length <= 2) {
// 如果分数太少,无法去除最大最小值
return { ...student, average: null, originalScores: scores }
}
// 方法1:排序后去掉首尾
const sorted = [...scores].sort((a, b) => a - b)
const trimmed = sorted.slice(1, -1) // 去掉最小和最大
const average = trimmed.reduce((sum, s) => sum + s, 0) / trimmed.length
// 方法2:不排序,找到最大最小值(O(n))
// 适合大数据量场景
return {
...student,
trimmedScores: trimmed,
average: Math.round(average * 100) / 100 // 保留两位小数
}
})
}
// 使用
const result = processScores(students)
console.log(result)
完整示例:
javascript
const students = [
{ name: '张三', scores: [85, 90, 78, 92, 88] },
{ name: '李四', scores: [75, 82, 95, 88, 79] },
{ name: '王五', scores: [90, 85, 80, 95, 100] },
// ... 共10个
]
const processed = students.map(({ name, scores }) => {
const sorted = [...scores].sort((a, b) => a - b)
const trimmed = sorted.slice(1, -1)
const avg = trimmed.reduce((a, b) => a + b, 0) / trimmed.length
return { name, originalScores: scores, average: avg.toFixed(2) }
})
console.log(processed)
// 输出: { name: '张三', originalScores: [...], average: '86.50' }
注意边界:
- 分数数组长度 ≤2 时无法去除最大最小值
- 多个人分数相同时,只去除一个最大值和一个最小值
📚 知识点速查表
| 知识点 | 核心要点 |
|---|---|
| box-sizing | content-box(默认,宽不含padding/border) / border-box(宽含padding/border) |
| 对象判空 | Object.keys(obj).length === 0,需先判null |
| 类型转换 | 3 == true → 3 == 1 → false |
| 小数精度 | 0.1+0.2 !== 0.3,使用Math.round(num*100)/100或Number.EPSILON |
| 单词反转 | str.split(' ').reverse().join(' ') |
| 闭包输出 | 分析作用域,注意IIFE和内部变量作用域 |
| 文字截断 | 单行text-overflow: ellipsis,多行-webkit-line-clamp |
| 三列响应式 | Flex flex:1、Grid auto-fit、浮动+媒体查询 |
| 防抖 | 清除上次timer,延迟执行 |
| 数组处理 | 排序sort → 切片slice → 累加reduce → 平均值 |
📌 最后一句:
秦丝科技这场笔试,是一份"小而全"的基础摸底试卷。从CSS的box-sizing到JS的闭包输出,从浮点数精度到数组对象处理,每一题都在考察前端工程师的基本功是否扎实 。没有偏题怪题,但第6题的闭包和第4题的精度问题,很容易踩坑。基础不牢,地动山摇------笔试过关,靠的不是运气,而是日积月累的代码手感。