想象这样一个画面:你坐进一辆新款智能汽车的驾驶舱。正前方是仪表盘,中控台上嵌着GPS导航屏,后视镜上方挂着行车记录仪,副驾储物格里有一本车辆配置说明书。汽车行驶时,GPS告诉你身在何处、即将开往哪里;行车记录仪默默存下你走过的每条路,让你随时可以掉头;配置说明书则记录了车辆的型号与能力。这一切设备组合在一起,构成了你与这台车交互的“驾驶舱生态系统”。 浏览器,其实也是一辆这样的车。你每天打开的网页是车内播放的画面,而承载这一切的“驾驶舱”,在技术圈里有个名字——BOM,浏览器对象模型(Browser Object Model)。本篇文章将带你认识驾驶舱里的每一个关键设备:总控台 window、GPS导航仪 Location、行车记录仪 History、车辆配置单 Navigator,以及它们如何共同支撑起现代前端开发中最火的概念——SPA(单页面应用)的前端路由。学完之后你会发现,前端路由的魔法,不过是“在一条不换路的高速路上不断切换车载屏幕画面”而已。

岗位关联

理解BOM,就是理解浏览器这辆车的驾驶舱。掌握 Location 与 History API,你就能亲手实现前端路由、掌控页面导航,这是从“页面切图仔”走向 Web 架构师的关键一步。

目录
  1. 可视化演示
  2. BOM核心概念
  3. window全局对象
  4. Location导航地址
  5. History历史栈
  6. Navigator环境识别
  7. SPA前端路由
  8. 兼容性与实践
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示BOM对象层次结构、History API历史栈操作与Location URL解析,交互切换三种模式理解浏览器API核心机制。

本节配有交互动效(进入视口后自动加载,约 3KB)。

BOM对象层次结构 紫色为window根对象(BOM核心入口) 浅紫为一级子对象(document/history/location/navigator) 橙色为二级子对象(screen/URL/UserAgent) window = {document, history, location, navigator, screen, ...} BOM以window为根对象,提供浏览器窗口交互接口,包含document、history、location等核心子对象

BOM核心概念

你买下第一辆智能汽车,坐进驾驶舱时,最先看到的是车企印在说明书上的“驾驶舱全景图”:方向盘、仪表盘、后视镜、中控屏各有位置、各司其职。浏览器里的 BOM(Browser Object Model,浏览器对象模型)就是这张全景图。它把浏览器窗口抽象成一组彼此协作的对象:谁是总指挥,谁管当前地址,谁记录历史,谁描述运行环境,全都安排得明明白白。你不需要自己发明体系,只需要学会按图索骥。

DOM 和 BOM 的分工是很多新手的第一道坎。打个比方:DOM 是车载中控屏上正在播放的那部电影——它管的是页面的内容结构;而 BOM 是承载这块屏幕的整个驾驶舱——它管的是浏览器窗口本身。更微妙的是,document 既是 DOM 的根节点,同时也作为 window 的一个属性挂在 BOM 名下。换句话说,电影既属于播放器,也属于整车电气系统。

BOM 的核心是 window 对象。location、history、navigator、screen、document 全都是 window 的子属性。你写的全局变量和全局函数,最终也都会挂载到 window 上。正因如此,前端调试时在控制台敲一个 window.location.href,就等于在问“GPS 屏上当前显示的路段是什么”。不同浏览器对 BOM 的实现细节有差异,这引出了开发中最重要的习惯——先做兼容性检测,再大胆使用新 API。

所以,当你对面试官说“我理解 BOM”时,其实是在说:“我熟悉这个驾驶舱里每一个仪表的位置、读数和操作方法。”

BOM={window,location,history,navigator,screen,document}BOM = \{ \text{window}, \text{location}, \text{history}, \text{navigator}, \text{screen}, \text{document} \}
这个集合公式告诉你,BOM 不是某一个对象,而是一组“驾驶舱设备”的集合。window 是总控台,其余都是它手下的功能模块。
  1. window驾驶舱总控制台,所有子对象的注册中心和全局环境。
    window\text{window}
  2. locationGPS 导航仪,负责当前 URL 的查询、解析与跳转。
    location\text{location}
  3. history行车记录仪,维护浏览历史栈,支持前进、后退与静默入栈。
    history\text{history}
  4. navigator车辆出厂配置单,描述浏览器名称、版本与运行环境。
    navigator\text{navigator}
  5. document中控屏上正在渲染的页面内容,DOM 树的根节点。
    document\text{document}

window全局对象

驾驶舱的正中央是一块总控面板。你按下的每个按钮、读取的每个数值,最终都汇集到这块面板上。window 就是浏览器驾驶舱里的总控面板:它既是全局变量的注册中心,也是定时器、弹窗、滚动控制这些基础按钮的所在位置。

在 JavaScript 中,全局作用域下声明 var x = 1 之后,你完全可以在另一个地方写 window.x 取到同一个值。这是因为浏览器把全局变量都挂在了 window 这个属性仓库里。这种设计的代价是:全局变量太多会互相污染,就像中控台上堆满杂物,想找哪个按钮都费劲。因此现代前端开发推崇模块化,尽可能少往 window 上挂东西。

window 还掌管着三类高频操作。第一类是定时器:setTimeout 像是车厢里的“一次性闹钟”,到点提醒一次;setInterval 像是“循环播放”按钮,每隔固定时间重复执行。第二类是弹窗交互:alert、confirm、prompt 是浏览器内置的三块提示牌,适合简单场景,但 UX 粗糙,生产环境要慎用。第三类是窗口控制:通过 window.innerWidth 与 innerHeight 可以读取视口尺寸,用 window.scrollTo 可以控制滚动位置,这就像查看挡风玻璃的尺寸、按下座椅调节键。

总控台虽好,但不要把所有设备都堆在它上面。模块化 + 命名空间,才是老司机的收纳哲学。

var x=1⇒window.x=1\text{var }x = 1 \quad\Rightarrow\quad \text{window}.x = 1
全局变量声明与 window 属性挂载是同一枚硬币的两面。理解了这个等式,你就明白了为什么全局变量过多会带来命名冲突。
  1. 全局变量挂载用 var 声明变量等价于在 window 对象上添加属性。
    var x⇒window.x\text{var } x \Rightarrow \text{window}.x
  2. 定时器调度setTimeout 注册一次性闹钟,到点执行回调函数。
    setTimeout(fn,1000)\text{setTimeout}(fn, 1000)
  3. 弹窗交互alert 弹出模态提示框,阻塞后续脚本直到用户确认。
    alert(message)\text{alert}(\text{message})

Location导航地址

开车去陌生城市,你会把目的地输进导航。导航屏上显示:当前在哪条路、门牌号多少、经由哪条匝道、有没有备注。Location API 就是浏览器这辆车的 GPS 屏,它用一组属性把当前 URL 拆解成清晰的路段信息。比如 https://shop.app/products?page=2#reviews 这串地址,在 location 眼中是一组带标签的零件。

location.protocol 是协议头,相当于 GPS 的“卫星信号通道”(https 还是 http);location.host 是域名加端口,相当于“城市与街道”;location.pathname 是路径,相当于“门牌号”;location.search 是查询字符串,像快递面单上的备注;location.hash 是锚点,相当于楼内的具体房间号。你在控制台分别打印这些属性,就能像看导航数据一样把 URL 拆得明明白白。

Location 的另一个职责是做跳转。location.assign(url) 是规规矩矩开车到新地址,会留下历史记录;location.replace(url) 是走同一条路但把当前出发点从行车记录仪上抹掉,用户按返回键不会回到这里;location.reload() 则是原地重启发动机,重新加载当前页面。最特别的是 hash:修改它不会刷新页面,却会在历史栈里新增一条记录——这个特性让它成为前端路由的基石,后面 SPA 一节你还会见到它。

GPS 屏上每个数字都有意义,Location 的每个属性也一样。会读会改,你的页面导航就成功了一半。

URL=protocol+′//′+host+pathname+search+hash\text{URL} = \text{protocol} + '//' + \text{host} + \text{pathname} + \text{search} + \text{hash}
URL 是一个由六段信息拼接而成的字符串。location 对象把这个字符串拆成了可读可写的零件,方便你精准修改其中任意一段。
  1. protocol协议头(http 或 https),决定数据传输的加密等级。
    protocol\text{protocol}
  2. search查询串,以?开头携带请求参数,服务端可用它识别业务条件。
    search\text{search}
  3. hash锚点片段,以 # 开头,修改时页面不刷新、只留下历史记录。
    hash\text{hash}

History历史栈

行车记录仪默默记下你走过的每个路口,你可以回放某一段,也可以随时掉头回到某个位置。History API 就是浏览器这辆车的记录仪,它维护着一个“历史栈”——一个你或许听过但有点怕的术语。别担心,它就像自助餐厅里的一摞盘子:新盘子总是放到最上面,要拿只能从最上面取,后进先出。

传统页面跳转就像换了一条路:每点一个链接,浏览器都要重新加载整条路的路况信息,页面白屏、状态丢失,体验生硬。History API 给出了更优雅的方案:history.pushState(state, title, url) 可以把一条新记录“无声无息”地压入栈顶,同时更新地址栏的 URL,但页面完全不刷新。这就像在导航仪上切换到一条新路线预览,车本身还在原地,没有重新启动。

history.replaceState 则替换当前栈顶的记录而不新增,适合“修正”当前状态而不是留下新足迹的场景。那监听什么呢?当用户点击浏览器的前进/后退按钮时,浏览器会触发 popstate 事件,并把当初 pushState 时存入的状态对象(state)交还给你。有趣的是:直接调用 pushState 或 replaceState 并不会触发 popstate,只有用户主动前进后退才会触发。这与“按下记录仪的播放键不会自动倒带,手动拨动进度条才会”是同一个道理。

记录仪不会说谎,历史栈里每一条都真实可查。理解它的读写规则,你就能精准操控浏览器的“前进后退记忆”。

history.pushState(state,title,url)⇒URL更新,页面不刷新\text{history.pushState}(\text{state}, \text{title}, \text{url}) \Rightarrow \text{URL更新,页面不刷新}
pushState 是前端路由的核心操作:它把地址栏变成了可以自由书写的地址,却不打扰页面的运行状态,让“换屏不换路”成为可能。
  1. state任意 JavaScript 对象,相当于记录仪上的“书签标签”,popstate 时原样返回。
    state∈{JS对象}\text{state} \in \{\text{JS对象}\}
  2. title页面标题参数,目前多数浏览器会忽略它,但仍建议传入。
    title\text{title}
  3. url新地址,必须是同源 URL,否则浏览器会抛出安全异常。
    url∈同源URL\text{url} \in \text{同源URL}

Navigator环境识别

每辆车的副驾储物格里都有一本随车手册,记录着发动机型号、排量、出厂年份、支持的功能列表。Navigator 就是浏览器的随车手册——一个描述“我是谁、我在什么环境里运行”的配置单对象。

navigator.userAgent 是一个长字符串,包含浏览器名称、版本号、内核信息,江湖人称“浏览器身份证”。过去前端靠解析它来判断“用户用的是不是 Chrome”,但这条路的坑很多:Chrome 的 UA 里就藏着 Mozilla、Safari 等字样,直接匹配容易翻车。更现代的思路是特性检测:不问他叫什么名字,直接问他能不能做某件事。比如判断浏览器是否支持 History API,写 'pushState' in history 比检测 UA 可靠得多——就像判断一辆车能不能跑山路,与其看车标,不如直接试试它的悬挂系统。

Navigator 还提供几个高频实用信息:navigator.platform 返回操作系统平台(Win32、MacIntel 等),navigator.onLine 告诉你当前网络是否在线,navigator.language 返回浏览器界面语言。在开发国际化站点或离线功能时,这些字段就像随车手册里的环境适配建议,缺一不可。

先看配置单再开车,先查 navigator 再做功能。这条准则能让你的代码在五花八门的浏览器里都走对路。

navigator.userAgent⇒"浏览器身份证"\text{navigator.userAgent} \Rightarrow \text{"浏览器身份证"}
userAgent 是浏览器主动报出的身份字符串,适合做兼容性兜底和数据分析,但不适合作为唯一的判断依据。
  1. userAgent一个长字符串,内含浏览器名、版本与内核信息。
    navigator.userAgent\text{navigator.userAgent}
  2. onLine返回布尔值,表示浏览器当前是否处于联网状态。
    navigator.onLine∈{true,false}\text{navigator.onLine} \in \{true, false\}
  3. language返回浏览器首选语言标签,常用于国际化的默认语言选择。
    navigator.language\text{navigator.language}

SPA前端路由

想象你在一条高架路上匀速行驶,道路本身没有变化,但导航屏上的目的地画面不断切换——这就是单页面应用(SPA)的体验。SPA 的灵魂是“不刷新页面,就能换一块屏幕内容”,而实现这个魔术的,正是前端路由,它实际上就是在“导航与记录仪”之间做协同编排。

前端路由有两大主流模式。第一种是 hash 模式:URL 写成 https://site.app/#/about,把路由路径放在 # 之后。因为修改 hash 不会刷新页面又会在历史栈留下记录,天然契合 SPA。浏览器还提供了 hashchange 事件,只要 hash 一变,页面就能收到通知并重新渲染。第二种是 History 模式:借助 pushState 把 URL 改成没有 # 的干净路径(比如 /about),看起来和传统网站一模一样,但对服务器有额外要求——用户刷新 /about 时,服务器必须把请求回退到 index.html,否则会得到 404。

以 hash 路由为例,实现思路非常朴素:监听 hashchange 事件,取出 location.hash 中的路径,查路由表,渲染对应组件。Vue Router、React Router 的底层逻辑本质上就是这句话的工程化放大。History 模式则依赖 popstate 事件和 state 对象来传递路由参数,让“前进后退”时组件状态也能恢复回来。

掌握了这两套模式,你就像同时学会了“导航界面切换”和“仪表盘布局切换”两种手法,遇到任何路由需求都能从容应对。

newURL=pathname.replace(旧路径,新路径)\text{newURL} = \text{pathname.replace}(\text{旧路径}, \text{新路径})
路由切换的公式可以压缩成一句话:把地址中的路径换成新值,再交给 History 或 hash 机制去落地,让页面“换屏不换路”。
  1. pathname从 location 拿到当前路径,作为替换的原材料。
    location.pathname\text{location.pathname}
  2. replace用字符串替换方法把旧路径替换成目标路径。
    pathname.replace(old,new)\text{pathname.replace}(old, new)
  3. pushState把新 URL 压入历史栈,最终更新地址栏。
    history.pushState(state,′′,newURL)\text{history.pushState}(state, '', newURL)

兼容性与实践

同样是驾驶舱,特斯拉和五菱宏光的按钮布局完全不同。浏览器市场也是如此:Chrome、Firefox、Safari、Edge 各有各的怪脾气。BOM 相关的 API 兼容性差异,处理不好就会让你的页面在某类设备上“熄火”。老司机的做法是提前做好三件事:特性检测、降级方案、安全边界。

第一,特性检测优先。在使用 pushState 前,先检查 'pushState' in history;需要监听 hash 变化时,先确认浏览器支持 hashchange 事件。与其问“你是不是 Chrome”,不如问“你能不能做到这件事”。第二,降级方案兜底。如果浏览器不支持 History 模式,就自动退回 hash 模式;实在不行,就退回传统的整页刷新。这就像山路太陡时,自动从两驱切换成四驱。第三,守好安全边界。pushState 只允许同源 URL,跨域会抛异常;跳转时警惕恶意 URL 的钓鱼陷阱;从 URL 读取的参数要校验后再使用,防止注入攻击。

性能优化方面也有讲究:监听 scroll、resize 这类高频事件时要加节流(throttle),路由懒加载则能让首屏只渲染必要的组件,不至于一启动就把所有页面都载入内存。这些细节就像给驾驶舱做定期保养,短时间看不出差别,跑长途就见真章。

能驯服不同车型的车手,才是真正的老司机。兼容性不是束缚,而是让你在更广阔的路况下都能安全抵达。

可用性=特性检测×降级方案×安全校验\text{可用性} = \text{特性检测} \times \text{降级方案} \times \text{安全校验}
真正的兼容性不是“支持所有浏览器”,而是“在能力不同的浏览器上,都能给出可用的体验”。三者相乘,一项为零,整体归零。
  1. 特性检测调用前先判断 API 是否存在,而不是依赖浏览器名判断。
    ’pushState’ in history\text{'pushState' in history}
  2. 降级方案在新 API 不可用时,回退到 hash 模式或整页刷新。
    fallback(hash,reload)\text{fallback}(hash, reload)
  3. 安全校验保持同源限制,对 URL 参数进行合法性检查后再使用。
    同源+参数校验\text{同源} + \text{参数校验}

AI 应用场景

场景一:大型后台管理系统的 SPA 路由

阶段:前端架构与交互设计

解决的问题:传统多页面应用每次跳转都整页刷新,白屏闪烁、状态丢失、体验割裂。借助 History API 和 Location API,可以构建无刷新的单页应用,让“订单列表”到“订单详情”的切换像切换导航画面一样顺滑。

具体案例:某电商企业后台,运营人员从“订单管理”点击某条订单记录时,URL 从 /orders 平滑变为 /orders/102345,页面右侧抽屉滑出订单详情,左侧列表保持滚动位置不变,历史栈支持用户按返回键回到列表且筛选条件不丢失。

场景二:数据可视化大屏的 URL 状态同步

阶段:数据呈现与状态持久化

解决的问题:大屏页面上有大量筛选条件(时间范围、城市、指标),每次调整后刷新或分享链接都会丢失状态。利用 location.hash 把筛选参数编码进 URL,可以让任意同事打开同一链接时自动恢复大屏状态。

具体案例:某智慧城市指挥中心大屏,用户选择“2024-11-11 至 2024-11-18”和“上海”后,地址栏变成 #date=20241111-20241118&city=shanghai。将该链接发给同事,对方打开即看到完全相同的图表配置,无需手动重复筛选。

场景三:电商网站的浏览轨迹回溯

阶段:用户行为分析与流失挽回

解决的问题:用户在未登录状态下浏览了多个商品页,登录后如何快速恢复刚才的浏览轨迹?借助 History API 的状态管理和 history 栈结构,可以在登录前后无缝衔接用户的“行车记录”。

具体案例:某跨境电商网站,游客连续浏览了 3 个商品后触发登录弹窗。完成登录后,页面利用之前记录的状态对象,自动把用户带回刚才停留的第三个商品页,并弹出“继续浏览”引导条,带动转化率提升约 12%。

Python 演示

python49 行
from urllib.parse import urlparse  class BrowserHistory:    def __init__(self):        self.entries = ["/home"]        self.index = 0     def push(self, new_path):        self.entries = self.entries[: self.index + 1]        self.entries.append(new_path)        self.index += 1     def back(self):        if self.index > 0:            self.index -= 1        return self.entries[self.index]     def forward(self):        if self.index < len(self.entries) - 1:            self.index += 1        return self.entries[self.index]     def current(self):        return self.entries[self.index]  def parse_location(full_url):    parts = urlparse(full_url)    return {        "protocol": parts.scheme,        "host": parts.netloc,        "pathname": parts.path,        "search": parts.query,        "hash": parts.fragment,    }  if __name__ == "__main__":    bh = BrowserHistory()    bh.push("/about")    bh.push("/contact")    print("current:", bh.current())    print("back ->", bh.back())    print("forward ->", bh.forward())     loc = parse_location("https://store.app/products?page=2#reviews")    for key, value in loc.items():        print(key, ":", value)
目的:用 Python 模拟浏览器 History 栈的前进/后退行为,并模拟 Location API 对 URL 的结构化解析,帮助理解前端 BOM 的核心逻辑。
  1. self.entries = self.entries[: self.index + 1]新入栈时先截断当前索引之后的记录,模拟浏览器“跳到新路线后旧的前进记录作废”的行为。
  2. bh.push("/about")相当于前端调用 history.pushState,把新路径压入栈顶。
  3. bh.back()相当于 history.back(),返回上一页;越界时停留在栈底。
  4. parse_location("https://store.app/products?page=2#reviews")模拟 location 对象把完整 URL 拆解成 protocol、host、pathname 等部件。
  5. parts.fragment解析出的片段就是前端 location.hash 对应的内容,体现 hash 在 URL 中的独立地位。

常见误区

误区很多初学者写 `history.pushState(null, '', '/about')` 后,发现地址栏变了但页面没变化,于是疑惑:“怎么页面没刷新?是不是我写错了?”还有人以为调用它会自动加载新页面。

正解pushState 只做两件事:把一条新记录压入历史栈、更新地址栏 URL。它明确不会触发页面刷新,也不会加载新文档。这正是它在 SPA 中被当作路由核心的原因——我们能“假装”到了新页面,再自己动手渲染组件。如果要真正加载新页面,请改用 location.href 或 location.assign。

为何常见:人们容易把“地址栏变化”和“页面加载”本能地绑定在一起,因为传统网站中这两件事总是同时发生,而 History API 刻意拆开了它们。

误区“往地址栏后面加一个 `#xxx`,页面是不是会重新加载?那 hash 路由岂不是每切换一次就白屏一次?”

正解修改 hash 只会触发 hashchange 事件并在历史栈中新增一条记录,浏览器不会重新加载文档。从 #/home 切到 #/about,页面始终是同一份 HTML,组件切换完全由 JavaScript 控制。这套机制专为“不刷新页面改状态”设计,是 hash 路由成立的前提。

为何常见:人们看到 URL 变化就默认会刷新,忽略了 hash 是浏览器专门预留的“文档内部定位”片段,它天然不触发网络请求。

误区“BOM 不就是那个全局 `window` 吗?我学完了 window 就相当于学完了 BOM。”

正解window 是 BOM 的顶层对象,但 BOM 是一个对象集合,还包括 location、history、navigator、screen 等。它们分工明确:location 管地址、history 管历史、navigator 管环境。只懂 window 就像只认识方向盘却不认识油门刹车,驾驶舱还远未掌握。

为何常见:因为日常写代码时 window. 可以省略,alert()、setTimeout 这些函数看起来都像“全局的”,于是 window 的光环遮蔽了其他 BOM 成员的存在感。

误区“我 pushState 之后写个 `window.addEventListener('popstate', handler)`,为什么 handler 没执行?明明 URL 都变了。”

正解popstate 只在用户点击浏览器前进/后退按钮、或调用 history.back() / forward() / go() 时触发。pushState 和 replaceState 不会触发它,它们只是静默修改历史栈。因此,在 SPA 中你需要手动调用渲染函数让组件与 URL 同步,依赖 popstate 只会让你错过首次导航。

为何常见:初学者直觉上认为“URL 变化就是状态变化,状态变化就该触发事件”,忽略了 popstate 这个事件命名的本意是“用户主动移动历史位置”。

误区“SPA 里我 pushState 了好几条记录,但一按 F5,回到首页,之前压入的路由是不是全没了?”

正解刷新页面后,浏览器会保留当前页面对应的历史记录,但之前通过 pushState 压入的同文档历史记录会被清空,当前 URL 变成新文档在历史栈中的第一条。也就是说,记录仪的“磁带”在重启后被重置了。正因如此,History 模式路由在刷新时必须靠服务器回退到 index.html,再由前端代码根据当前 URL 恢复出对应的页面状态。

为何常见:人们容易把 JS 内存中的状态和浏览器历史栈混为一谈,以为刷新只是“重新执行脚本”,忽略了刷新本质上是重新加载了一个文档上下文。

面试题

Q1. BOM 的全称是什么?

单选题 · easy

  • A.Browser Object Model← 正确答案
  • B.Browser Operation Method
  • C.Basic Object Module
  • D.Binary Operation Module

答案:A。BOM 是 Browser Object Model,即浏览器对象模型,是浏览器窗口及其子对象(location、history、navigator 等)的统称。它和 DOM(文档对象模型)共同构成了前端操作浏览器的两大接口体系。

Q2. 下列哪个是 BOM 的顶层对象?

单选题 · easy

  • A.window 窗口对象← 正确答案
  • B.document 文档对象
  • C.navigator 浏览器对象
  • D.location 地址对象

答案:A。window 是 BOM 的顶层/全局对象,location、history、navigator、document 等都是 window 的子属性。document 虽然是 DOM 树的根,但在 BOM 的视角下它也是挂在 window 下的一个属性。

Q3. history.pushState() 第三个参数的作用是什么?

单选题 · mid

  • A.设置新 URL 并更新地址栏← 正确答案
  • B.触发页面发生刷新
  • C.定义新页面的标题
  • D.保存页面的状态对象

答案:A。pushState 的第三个参数是新 URL,会被写入地址栏而不会触发页面刷新。第一个参数 state 用于保存状态对象,第二个参数 title 目前多数浏览器会忽略。选项 B 是典型误解,pushState 不会刷新页面。

Q4. location 对象中表示 URL 中"#"后面内容的属性是哪个?

单选题 · mid

  • A.hash 锚点属性← 正确答案
  • B.host 域名属性
  • C.search 查询属性
  • D.port 端口属性

答案:A。location.hash 返回 URL 中 # 之后的部分。host 是域名加端口,search 是 ? 之后的查询参数,port 是端口号。hash 的特殊性在于修改它不会刷新页面,因此成为前端 hash 路由的基础。

Q5. 下列哪个操作可以让 URL 变化但页面不刷新?

单选题 · mid

  • A.调用 location.reload()
  • B.使用 history.pushState()← 正确答案
  • C.调用 location.assign()
  • D.点击 target=_blank 链接

答案:B。history.pushState() 只更新历史栈和地址栏 URL,不触发页面刷新。location.reload() 会重新加载当前页,location.assign() 会加载新文档,点击 target=_blank 会打开新标签页,三者都会发生文档加载。

Q6. popstate 事件在什么情况下会触发?

单选题 · hard

  • A.调用 pushState 方法后立即
  • B.调用 replaceState 方法后
  • C.用户点击前进后退时← 正确答案
  • D.修改 location.href 属性后

答案:C。popstate 只在用户点击浏览器前进/后退按钮,或调用 history.back() / forward() / go() 时触发。pushState、replaceState 不会触发它,修改 location.href 会触发整页导航,也不是 popstate 的职责范围。

Q7. 下列哪些是 navigator 对象提供的信息?

多选题 · mid

  • A.userAgent 浏览器标识← 正确答案
  • B.onLine 在线状态← 正确答案
  • C.language 界面语言← 正确答案
  • D.href 当前地址

答案:ABC。navigator.userAgent 是浏览器标识字符串,navigator.onLine 表示网络在线状态,navigator.language 表示浏览器首选语言。href 是 location 对象的属性,不属于 navigator,选项 D 混淆了对象归属。

Q8. 下列哪些关于 hash 路由的说法是正确的?

多选题 · mid

  • A.修改 hash 不会刷新页面← 正确答案
  • B.修改 hash 会生成历史记录← 正确答案
  • C.页面会触发 hashchange 事件← 正确答案
  • D.必须配置服务器端支持

答案:ABC。修改 hash 不刷新页面、会在历史栈中留痕、并触发 hashchange 事件,这正是 hash 路由的原理。hash 路由不需要服务器做额外配置,因为 # 后面的内容不会发送到服务器;需要服务器配置的是 History 模式路由。

Q9. 下列关于 history.replaceState 的说法,哪些是对的?

多选题 · hard

  • A.会替换当前栈顶记录← 正确答案
  • B.历史栈的长度不变← 正确答案
  • C.页面不会发生刷新← 正确答案
  • D.会触发 popstate 事件

答案:ABC。replaceState 用新状态替换当前栈顶记录,不新增记录,因此历史栈长度不变;它只更新 URL 和状态,不刷新页面。和 pushState 一样,replaceState 不会触发 popstate 事件,所以选项 D 错误。

Q10. History 模式路由下用户停留在 /about 并刷新,会发生什么?

单选题 · extreme

  • A.向服务器请求该路径,未配置回退会返回 404← 正确答案
  • B.浏览器自动重定向到首页
  • C.不再请求服务器直接由前端渲染
  • D.浏览器强制改用 hash 模式

答案:A。History 模式下 URL 是干净的 /about,刷新时浏览器会向服务器发起真实请求。若服务器未将未知路径回退到 index.html,就会返回 404。这正是 History 模式与 hash 模式的本质差异:hash 模式不会把 # 后内容发给服务器。