← 返回题库
前端核心·真题演练

前端真题 130 题 · HTML & 浏览器基础

行内/块级元素、跨标签页通信、history/hash 路由、DOM 树、BFC、position、图片加载优化、flex、CSS 优先级与层叠上下文等 1-19 题。

HTML浏览器BFCflex层叠上下文

来源:飞书多维表格《前端面试真题,会80%直接进大厂》
说明:以下按原表顺序整理 130 个面试知识点,每题给出详细完整答案,含关键原理、要点、示例代码与手写实现。非技术类题目给出回答思路。适用于前端中高级岗位面试准备。


一、HTML & 浏览器基础

1. 行内元素、块级元素

行内元素(inline):如 spanaimginputlabelemstrongbrcode

  • 与其他行内元素同一行排列
  • 宽度高度由内容决定,设置 width/height 无效imginput 这类替换元素除外);
  • 垂直方向的 margin/padding 不影响布局,水平方向生效;
  • 可以嵌套在块级元素内。

块级元素(block):如 divph1-h6ullitablesectionheader

  • 独占一行,默认宽度撑满父容器;
  • 可设置 width/height、margin/padding(四个方向都生效);
  • 可容纳行内元素与其他块级元素。

行内块级元素(inline-block):兼具两者特性——同行排列、可设宽高、垂直 margin 生效。

面试要点

  • display 可在这几种类型间转换;inline 元素在排版时会产生"换行间隙"问题(因源码换行符)。
  • 现代布局中 flex/grid 会改变子元素的 display 上下文(子项变为 blockified),行内元素的很多特性在 flex 容器内不再适用。

2. 跨标签页通信

标签页间通信的常用方案:

  1. localStorage 事件(同源):在 A 页写入 localStorage,B 页监听 storage 事件。storage 事件只在其他标签页修改时触发,当前页不触发。
// A 页
localStorage.setItem('msg', JSON.stringify({t: Date.now(), data: 'hi'}));
// B 页
window.addEventListener('storage', e => {
  if (e.key === 'msg') console.log('收到:', JSON.parse(e.newValue));
});
  1. BroadcastChannel(推荐,同源):原生 API,双向实时通信。
const bc = new BroadcastChannel('channel');
bc.onmessage = e => console.log(e.data);
bc.postMessage('hello');
  1. postMessage:适用于跨域页面、iframe、window.open 打开的窗口之间。
// A 发
targetWindow.postMessage({type:'greet', data:1}, 'https://target.com');
// B 收
window.addEventListener('message', e => {
  if (e.origin !== 'https://source.com') return; // 务必校验 origin
  console.log(e.data);
});
  1. SharedWorker:多个标签页共享同一个 Worker 线程,通过它中转消息(同源)。
  2. WebSocket / SSE:通过服务器中转,适用于跨标签页但可跨域、跨设备。

要点:同源优先 localStorage 事件或 BroadcastChannel;跨域用 postMessage 且必须校验 e.origin;复杂场景(多页状态同步)可上 SharedWorker。


3. history 和 hash 两种路由

单页应用路由的两种实现,核心是不刷新页面改变 URL 并触发视图更新

Hash 路由

  • URL 形如 https://a.com/#/user?id=1hash 改变不会触发浏览器请求,天然支持刷新。
  • 监听 hashchange 事件切换视图。
  • 缺点:URL 中有 # 不够美观;SEO 不友好。
window.addEventListener('hashchange', () => {
  const path = location.hash.slice(1);
  render(path);
});

History 路由

  • 基于 History API:pushState / replaceState 修改 URL(不刷新),配合 popstate 事件(仅前进后退触发)。
  • URL 干净美观,形如 https://a.com/user/1
  • 缺点:刷新时服务器需把所有路径都重定向回 index.html(否则 404);需要后端配置支持(如 nginx try_files)。
history.pushState({}, '', '/user/1');      // 切页面
window.addEventListener('popstate', () => render(location.pathname));

对比要点

维度hashhistory
URL 美观有 #,一般干净
刷新无感需后端配合
SEO相对好(仍需 SSR/预渲染)
兼容性更好依赖 History API

前端框架(Vue Router / React Router)都有两种 mode,正是对应这两套机制。


4. DOM 树

DOM(Document Object Model,文档对象模型) 是浏览器把 HTML 文档解析成的树形结构,每个 HTML 标签对应一个节点对象,节点之间按嵌套关系形成父子、兄弟的层次结构。

  • 节点类型:元素节点、文本节点、属性节点、注释节点等;
  • 文档解析过程:HTML → 字节流 → Tokenizer(分词)→ 构建 DOM 树;同时 CSS 解析生成 CSSOM 树,两者合成 Render Tree(渲染树);
  • 根节点是 document,根元素是 documentElement(即 <html>)。

为什么要理解 DOM 树

  1. DOM 操作是"操作一棵树",增删改查都要符合树结构——appendChildinsertBeforeremoveChildquerySelector 等;
  2. 每次 DOM 修改都可能引起重排/重绘,所以要批量修改、用文档碎片 DocumentFragment、虚拟 DOM 等手段减少操作次数;
  3. DOM 越深越复杂,选择器查找越慢,布局计算越贵——这决定了"减少 DOM 层级/节点数"是前端性能优化的基础手段之一。

面试引申:渲染流程(DOM+CSSOM→RenderTree→Layout→Paint)、defer/async 对 DOM 解析的阻塞、为什么虚拟 DOM 快(批处理 + 最小化真实 DOM 操作)等。


5. bind 的实现

bindcall/apply 的区别:bind 不立即执行,返回一个新函数,并可将 this 永久绑定 + 支持预传参(偏函数)。手写实现要点:

  1. 返回一个新函数;
  2. 新函数调用时,this 指向绑定的对象(若绑定值为 null/undefined,非严格模式回退到全局);
  3. 支持参数合并:bind 时传的参数 + 调用时传的参数;
  4. 支持 new:如果返回的函数被 new 调用,this 应指向新实例,忽略绑定的 this,并继承原函数的 prototype。
Function.prototype.myBind = function (context, ...args) {
  if (typeof this !== 'function') throw new TypeError('not a function');
  const fn = this;
  const bound = function (...rest) {
    // 1) 若被 new 调用,this 是新对象,用新 this 并保留原型链
    // 2) 否则用 context
    // 注:`?? globalThis` 是简化写法。原生 bind 会区分严格模式——
    //     非严格模式下 null/undefined 回退到全局对象;严格模式下则保持 null/undefined 不变。
    return fn.apply(
      this instanceof bound ? this : (context ?? globalThis),
      [...args, ...rest]
    );
  };
  // 让 bound 的原型指向原函数原型,使 instanceof 生效
  bound.prototype = Object.create(fn.prototype);
  return bound;
};

// 验证
function Point(x, y) { this.x = x; this.y = y; }
const BoundPoint = Point.bind(null, 1);
const p = new BoundPoint(2);       // new 时忽略绑定的 this
p.x; // 1

考察点this instanceof bound 判断是否被 new 调用;参数拼接顺序;原型链处理;边界(bind 结果再 bind、丢失绑定 context 为 null)。


6. 实现一个 trim 方法

String.prototype.trim 去除字符串首尾空白(含空格、\t\n\r 等)。注意不要用"两侧去空格"的错误正则。

// 方式一:正则(推荐,写法最贴近原生)
String.prototype.myTrim = function () {
  return this.replace(/^\s+|\s+$/g, '');
};

// 方式二:显式字符表去空白(也可扩展自定义字符)
String.prototype.myTrim2 = function () {
  return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
};

// 方式三:指针扫描(体现对 Unicode 空白理解)
String.prototype.myTrim3 = function () {
  const ws = /\s/;
  let s = 0, e = this.length - 1;
  while (s <= e && ws.test(this[s])) s++;
  while (e >= s && ws.test(this[e])) e--;
  return this.slice(s, e + 1);
};

'  hello world \n\t '.myTrim();  // 'hello world'

考察点\s 匹配哪些空白(空格、制表、换行、回车、\uFEFF BOM 等);两个边界是否都想清楚;是否会用 ^...|...$ 组合。


7. BFC(块级格式化上下文)

BFC(Block Formatting Context):页面上一块独立渲染区域,内部元素的布局不会影响外部,外部也不会影响内部。它是 CSS 视觉格式化模型中的一个概念。

触发 BFC 的条件(常见)

  • overflow 非 visible(hidden/auto/scroll);
  • float 非 none;
  • display: inline-block / table-cell / flex / grid / flow-root
  • position: absolute / fixed
  • 根元素 <html> 本身。

BFC 的特性(作用)

  1. 清除浮动:BFC 会包含内部浮动元素,父元素加 overflow:hidden 可避免高度塌陷;
  2. 阻止外边距合并(margin collapsing):两个相邻元素的外边距会取较大值合并,若把它们放入不同 BFC,则不会合并;
  3. 阻止元素被浮动元素覆盖:BFC 区域不会与浮动元素重叠(可用于实现自适应两栏布局)。
/* 经典用法:父容器触发 BFC 包裹浮动子元素,避免高度塌陷 */
.parent { overflow: hidden; }
.child { float: left; }

面试引申:为什么 overflow:hidden 能清除浮动?因为触发 BFC 后,父元素必须计算包含浮动子元素的整体高度;display:flow-root 是专为无副作用创建 BFC 而生的属性值。


8. position

CSS position 决定元素定位方式,取值与特性如下:

取值脱离文档流参考系特点
static默认值,top/left/right/bottom/z-index 无效
relative自身原位置相对自己原先位置偏移,原占位保留
absolute最近的非 static 定位祖先(否则为包含块/视口)相对定位祖先定位,原占位消失
fixed视口(viewport,除非祖先有 transform 等创建包含块)相对视口固定,滚动不随页面移动
sticky部分最近滚动容器相对定位与 fixed 结合:未到阈值时如 relative,到达后如 fixed 吸附

重点易错

  • absolute 的包含块是最近的非 static 定位祖先;若都没有,则以初始包含块(视口)为参考,并受祖先 transform/perspective/filter 影响(这些属性会创建新的包含块,导致 fixed 失效/相对其定位);
  • relative 偏移后原空间保留,absolute 偏移后原空间不保留;
  • 定位元素(position 非 static)会成为后代 absolute 的定位参考,也是 z-index 生效的前提(static 元素 z-index 无效)。

面试扩展:定位 + z-index 与层叠上下文的关系;absolute 居中技巧(left:0;right:0;margin:auto 或 transform 偏移)。


9. 大量图片加载优化

面试常问"页面有大量图片,如何优化加载与性能",从加载、解码、渲染三层回答:

  1. 懒加载(Lazy Load):首屏只加载可视区图片,滚动进入视口再加载。实现:loading="lazy"(原生)或 IntersectionObserver;占位用低清模糊图或骨架屏。
  2. 图片格式与压缩:优先 WebP/AVIF(体积远小于 JPEG/PNG);开启服务端压缩;使用响应式图片 srcset + sizes 按屏幕分辨率给不同尺寸;图标用 SVG/雪碧图。
  3. CDN 分发:静态图片走 CDN,多地就近加速。
  4. HTTP 层面:开启 HTTP/2 多路复用、Cache-Control 强缓存 + 版本化文件名(hash 命名)减少重复下载。
  5. 预加载关键图:首屏最重要图片用 <link rel="preload" as="image">,防止被 CSS/JS 阻塞发现。
  6. 减少解码开销:避免超大图(按实际展示尺寸下发);content-visibility: auto 跳过屏外渲染;图片设宽高避免布局抖动(CLS)。
  7. 占位与体验:blur-up(先模糊小图再切高清)、渐进式 JPEG。

衡量:用 LCP(最大内容绘制)观察首屏图加载;Lighthouse 的 image 审计项。


10. 实现一个 deepClone 方法

深拷贝要点:处理循环引用各种数据类型(数组、对象、Date、RegExp、Map、Set、Symbol 键、函数、原型、不可枚举属性)。

function deepClone(target, map = new WeakMap()) {
  if (target === null || typeof target !== 'object') return target; // 原始值/函数
  if (map.has(target)) return map.get(target);       // 循环引用
  const ctor = target.constructor;

  // 特殊类型:时间/正则/Map/Set
  if (target instanceof Date) return new Date(target.getTime());
  if (target instanceof RegExp) {
    const r = new RegExp(target.source, target.flags);
    r.lastIndex = target.lastIndex;
    return r;
  }
  if (target instanceof Map) {
    const m = new Map();
    map.set(target, m);
    target.forEach((v, k) => m.set(deepClone(k, map), deepClone(v, map)));
    return m;
  }
  if (target instanceof Set) {
    const s = new Set();
    map.set(target, s);
    target.forEach(v => s.add(deepClone(v, map)));
    return s;
  }

  // 数组与普通对象
  const clone = Array.isArray(target) ? [] : Object.create(Object.getPrototypeOf(target));
  map.set(target, clone);

  // 自有可枚举 Symbol 键 + 字符串键(含不可枚举用 Reflect.ownKeys 可全拷)
  for (const key of Reflect.ownKeys(target)) {
    const desc = Object.getOwnPropertyDescriptor(target, key);
    if (desc && 'value' in desc) {
      desc.value = deepClone(target[key], map);
      Object.defineProperty(clone, key, desc);
    } else if (desc) {
      Object.defineProperty(clone, key, desc); // getter/setter 拷贝
    }
  }
  return clone;
}

考察点

  • 循环引用必须处理(否则爆栈),用 WeakMap 记录已克隆对象;
  • 区分"浅拷贝"(Object.assign/展开符只拷一层);
  • 实际项目中可提醒:structuredClone 是浏览器原生深拷贝(但丢失函数、不支持 Symbol key、原型变普通对象);业务常用 JSON.parse(JSON.stringify())局限(丢 undefined/函数/Symbol、Date 变字符串、循环引用报错)也要能说清。

11. 斐波那契数列

斐波那契定义:F(0)=0, F(1)=1, F(n)=F(n-1)+F(n-2)

递归(朴素,指数级,会爆栈/超时)

const fib = n => n <= 1 ? n : fib(n-1) + fib(n-2);

优化方案

  1. 记忆化(memo)——把重复子问题缓存:
function fib(n, memo = {}) {
  if (n <= 1) return n;
  if (memo[n] != null) return memo[n];
  return (memo[n] = fib(n-1, memo) + fib(n-2, memo));
}
  1. 动态规划 / 滚动迭代(O(n) 时间、O(1) 空间,最优)
function fib(n) {
  if (n <= 1) return n;
  let a = 0, b = 1;
  for (let i = 2; i <= n; i++) [a, b] = [b, a + b];
  return b;
}
  1. 矩阵快速幂:O(log n),适合超大 n(体现进阶能力,可简单提及)。

考察点:能指出朴素递归的重复计算问题,并给出迭代解法;能回答"时间复杂度/空间复杂度"。


12. 最近看的书

非技术问题,考学习态度与沉淀。回答套路:

  • 说 1-2 本真实在读且与岗位相关的书,比泛泛而谈更好;
  • 技术书 + 非技术书各一本,展示广度;
  • 讲"读完最大的收获/改变了我什么",不要只报书名。

示例(前端方向):

《JavaScript 高级程序设计(第4版)》正在二刷,重点补 ES6+ 与浏览器原理部分;同时在看《代码整洁之道》,最大的改变是写代码时会更在意函数职责单一和命名,重构了一些历史模块。平时也刷《你不知道的 JavaScript》里的 this/闭包章节。

注意:别编造没读过的书,面试官常追问细节(某章讲了什么、你的实践)。


13. flex 布局

Flex(弹性盒)是一维布局模型,核心概念:容器(flex container)+ 项目(flex item),主轴(main axis)与交叉轴(cross axis)。

容器属性

  • display: flex | inline-flex
  • flex-direction: row | row-reverse | column | column-reverse(主轴方向)
  • flex-wrap: nowrap | wrap | wrap-reverse(是否换行)
  • flex-flow(direction + wrap 简写)
  • justify-content(主轴对齐):flex-start/end、center、space-between、space-around、space-evenly
  • align-items(交叉轴单行对齐):stretch、flex-start/end、center、baseline
  • align-content(交叉轴多行对齐,需换行才生效)

项目属性

  • flex-grow:放大比例(默认 0);flex-shrink:缩小比例(默认 1);flex-basis:初始主轴尺寸
  • flex 简写:flex: 1 = 1 1 0%flex: none = 0 0 autoflex: auto = 1 1 auto
  • order:排序;align-self:覆盖容器的 align-items

经典场景

  • 水平垂直居中:容器 display:flex; justify-content:center; align-items:center
  • 等分:子项 flex:1
  • 圣杯/左右固定中间自适应:中间 flex:1

易错点flex-basis:0auto 的区别;默认 flex-shrink:1 导致内容挤压;flex 子项 min-width:auto 导致溢出(需 min-width:0);flex 容器内子项 margin:auto 可做复杂居中。


14. CSS 优先级

CSS 层叠(cascade)判断优先级规则,从高到低:

  1. !important(比普通声明高,同 !important 再比权重);
  2. 内联样式 style(权重 1000);
  3. 选择器权重
    • ID 选择器 = (1,0,0)
    • 类/属性/伪类(.cls、attr、:hover)= (0,1,0)
    • 元素/伪元素(div、::before)= (0,0,1)
    • 通配符 * 与组合器不占权重
  4. 后写的同权重声明覆盖先写的;
  5. :is()/:where() 特例::where 权重恒为 0,:not()/:is() 取参数中最高权重。
/* 权重计算示例 */
#nav .item:hover    /* (1,2,0) */
div .item          /* (0,1,1) */
#nav div.item      /* (1,1,1) */
*                  /* (0,0,0) */

判断技巧:四元组 (inline, id, class, element) 从左往右比较,先出现更大值的胜出。同级比"出现顺序"(后声明的赢)。

要点:权重相同时源码顺序决定;!important 尽量少用(破坏可维护性);用 BEM/类选择器把权重控制在可预期范围。


15. 双飞翼/圣杯布局

目的:左右两栏固定宽度,中间栏自适应且优先渲染(对 SEO/首屏有利)。核心:中间栏放最前 + 三栏浮动/弹性。

圣杯布局(float + 负 margin + padding)

<div class="container">
  <div class="middle">中间</div>
  <div class="left">左</div>
  <div class="right">右</div>
</div>
.container { padding: 0 200px; }            /* 给左右留位 */
.middle { float: left; width: 100%; }        /* 中间占满 */
.left { float: left; width: 200px; margin-left: -100%; position: relative; left: -200px; }
.right { float: left; width: 200px; margin-left: -200px; position: relative; right: -200px; }

缺点:依赖 position 偏移、对宽度敏感。

双飞翼布局(float + 负 margin + 内层 margin)

<div class="middle"><div class="middle-inner">中间</div></div>
<div class="left">左</div>
<div class="right">右</div>
.middle { float: left; width: 100%; }
.middle-inner { margin: 0 200px; }           /* 内容腾出两侧空间,无需外层 padding */
.left { float: left; width: 200px; margin-left: -100%; }
.right { float: left; width: 200px; margin-left: -200px; }

现代方案(推荐回答)

  • Flex:中间 flex:1
  • Grid:grid-template-columns: 200px 1fr 200px

考察点:说出负 margin 让固定栏"爬上"100% 宽度行的原理;中间栏 HTML 在最前(content first);能给出 flex/grid 更简单的解法。


16. CSS3 新特性

选择器、样式、布局、动画、特效等维度作答:

  1. 新增选择器:属性选择器 [attr^=] [attr$=] [attr*=]、伪类 :nth-child(n) :not() :empty :root :target :enabled/:disabled :checked:first-of-type 等;
  2. 边框与背景border-radiusbox-shadowtext-shadowbackground-size/clip/origin、多重背景、渐变 linear-gradient/radial-gradient
  3. 文字@font-face 自定义字体、text-overflowword-breakwhite-space
  4. 颜色与单位rgba/hslarem/vh/vw/vmin/vmax/calc()
  5. 布局flex(一维弹性)、grid(二维网格)、多列 columnsbox-sizing: border-box
  6. 动画与过渡transition@keyframes + animationtransform(translate/rotate/scale/skew,含 3D:perspective、transform-style);
  7. 特效filter(模糊/灰度)、mix-blend-modeclip-path
  8. 响应式:媒体查询 @media@supports
  9. 新特性语义化/其他calc、自定义属性 --varwill-change(动画优化)、content-visibility

作答技巧:挑 5-6 类展开并举例说明"在项目中怎么用",比背目录好。


17. CSS 样式隔离

解决多团队/微前端/组件库下样式互相污染的问题,方案从简单到完整:

  1. BEM 命名规范.block__element--modifier,靠命名约定隔离(约定层,无运行时成本);
  2. CSS Modules:构建时给类名加 hash(.title_2x3ab_1),局部作用域,引用用 import styles from './x.module.css'
  3. CSS-in-JS(styled-components / emotion):样式绑定组件,自动生成唯一类名,支持动态主题;
  4. scoped(Vue)<style scoped> 编译时给选择器加 [data-v-xxx] 属性,实现"伪局部"(注意子组件根节点会受影响、动态内容需 );
  5. Shadow DOM(Web Components):浏览器原生隔离——样式与 DOM 都被封在 shadow root 内,真正物理隔离;
  6. 原子化 CSS(Tailwind/Unocss):类名本身就是工具,配合预检(preflight)减少冲突面。

微前端场景加分:子应用样式污染父应用 → 子应用用 CSS Modules + 容器前缀 + 动态样式挂载/卸载;沙箱隔离样式(shadow DOM 或 iframe)。

面试要点:能对比各方案隔离粒度(约定/编译/运行时/浏览器级);说出 scoped 的原理(属性选择器);CSS Modules 与 scoped 本质都靠"改名+改名后选择器加约束"。


18. CSS 性能优化

加载、渲染、书写习惯三层面回答:

  1. 减少体积与请求:压缩(cssnano)、移除未使用样式(PurgeCSS/Tailwind)、合并请求、HTTP/2 下多小文件反而不必合并;
  2. 关键 CSS 内联:首屏关键样式 <style> 内联,避免阻塞渲染;非关键 CSS 异步加载(media="print"rel="preload" as="style" 加载后切换 rel);
  3. 减少重排重绘
    • 避免频繁读写布局属性(读一帧攒批、requestAnimationFrame 里写);
    • transform/opacity 做动画(走合成层,不触发 layout/paint);
    • 减少 DOM 层级,降低样式匹配成本(后代选择器越深越贵,.a .b .c {} 尽量收敛为类);
    • 批量改 DOM(DocumentFragment);
    • will-change 提前告诉浏览器要动画(谨慎用,会占内存);
  4. 选择器优化:避免通配/超长后代链;类选择器优先;
  5. 其他:开启 gzip/br、图片等走 CDN、content-visibility:auto 跳过屏外渲染。

回答框架:可以按 Lighthouse 的 Performance 面板维度(FCP/LCP/CLS)组织:样式影响首屏 → 关键 CSS;动画卡顿 → 合成层优化;页面跳动 → 图片字体预留空间。


19. 层叠上下文

层叠上下文(Stacking Context):元素在 Z 轴上的"分组",一组元素按层叠顺序(stacking order)排列,组内比较、组间比较按组的整体。

触发条件(常见):

  • 根元素 <html>
  • position + z-index(非 auto);
  • opacity < 1transformfilterbackdrop-filterperspectiveclip-pathmaskmix-blend-modecontainwill-change 指定上述属性;
  • flex/grid 子项且 z-index 非 auto;
  • isolation: isolate

层叠顺序(从底到顶)

  1. 背景与边框
  2. 负 z-index 子元素
  3. 块级元素(非定位)
  4. 浮动元素
  5. 行内元素
  6. z-index:auto / 0 的定位元素
  7. 正 z-index 子元素

常见坑

  • 父元素 opacity < 1 会创建层叠上下文,导致子元素 z-index 无法"越级"(z-index 只在同一上下文内比较);
  • z-index 只对定位元素(或 flex/grid 子项)生效;
  • 判断两个元素谁上谁下:找到共同层叠上下文祖先,在其中比较各自的层叠顺序。

面试加分:层叠上下文与 z-index 的"父子限制"关系;transform 也创建上下文导致 fixed 定位异常的关联。