JS BOM完全指南(二):事件轮询、三大尺寸家族与浏览器对象详解

前言

本文是BOM系列的第二篇,深入讲解JavaScript的事件轮询机制 (同步与异步的执行原理)、Location/Navigator/History三大浏览器对象 的详细用法,以及Offset、Client、Scroll三大尺寸家族的完整对比与应用。掌握这些内容,你将彻底理解JS的异步执行机制和页面尺寸测量的核心技术。


一、JS执行机制(事件轮询)

1.1 JS是单线程

JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。

为什么JS是单线程?因为JS最初是用来处理页面中用户的交互以及操作DOM而诞生的。如果同一时间做两件事,这两件事发生冲突就会导致错误。例如:鼠标移入又移出,同时又删除元素,浏览器就不知道应该执行哪个操作。

单线程的问题:所有任务需要排队,前一个任务结束才会执行后一个任务。如果JS执行时间过长,会造成页面渲染不连贯,导致页面加载阻塞。

1.2 同步与异步

为了解决单线程的性能问题,HTML5提出了Web Worker标准,允许JS脚本创建多个线程,于是JS中出现了同步和异步的概念。

概念 说明 生活类比
同步​ 前一个任务结束后再执行后一个任务,执行顺序与排列顺序一致 等水开了(10分钟后),再去切菜、炒菜
异步​ 做一件事时,因为花费时间长,同时可以去处理其他事情 烧水的10分钟内,去切菜、炒菜

1.3 同步任务与异步任务

任务类型 执行方式 执行场所
同步任务​ 在主线程上排队执行,前一个执行完毕才能执行后一个 主线程(执行栈)
异步任务​ 不进入主线程,进入"任务队列",通知主线程后才进入主线程执行 任务队列(消息队列)

常见的异步任务类型:

类型 示例
定时器 setTimeout、setInterval
DOM事件 click、resize、onload
ES6 Promise Promise.then()
Ajax异步请求 XMLHttpRequest、fetch

1.4 JS执行机制(事件轮询 Event Loop)

核心流程:

复制代码
┌──────────────┐
                   │   执行栈      │
                   │  (主线程)     │
                   └──────┬───────┘
                          │
              ┌───────────┴───────────┐
              │                       │
              ▼                       ▼
       ┌──────────────┐     ┌──────────────────┐
       │  同步任务     │     │   异步任务        │
       │  (立即执行)   │     │  (进入异步线程)   │
       └──────────────┘     └────────┬─────────┘
                                     │
                                     ▼
                            ┌──────────────────┐
                            │   任务队列        │
                            │  (等待执行)       │
                            └────────┬─────────┘
                                     │
                          主线程空闲时询问
                                     │
                                     ▼
                            ┌──────────────────┐
                            │   加入执行栈      │
                            └──────────────────┘

执行步骤说明:

步骤 说明
① 执行栈开始顺序执行代码
② 判断任务是同步还是异步:同步立即执行,异步进入异步线程在任务队列等待
③ 执行栈为空(同步任务执行完毕),询问任务队列中是否有异步任务需要执行
④ 有则把任务加入执行栈末尾继续执行,没有则不停发起询问

经典示例:

复制代码
setTimeout(function() {
    console.log("hello1");
}, 1000);

console.log(1);
console.log(2);

setTimeout(function() {
    console.log("hello2");
});

console.log(3);

// 输出顺序:1 → 2 → 3 → hello2 → hello1

分析:

  1. 第一个setTimeout延迟1000ms,进入任务队列等待
  2. 同步任务依次输出1、2、3
  3. 第二个setTimeout延迟0ms(默认),先进入任务队列
  4. 同步任务执行完毕,检查任务队列,先取出第二个setTimeout输出"hello2"
  5. 1000ms后,第一个setTimeout到达时间,输出"hello1"

二、Location对象

2.1 常用属性

location对象管理当前页面的URL信息,可以直接通过location访问(等同于window.location)。

属性 说明 示例
location.href 返回当前加载页面的完整URL https://example.com/page?id=1#top
location.hash 返回URL中的hash(#号后内容) #top
location.host 返回服务器名称和端口号 example.com:8080
location.hostname 返回不带端口号的服务器名称 example.com
location.pathname 返回URL中的目录和文件名 /page/index.html
location.port 返回URL中指定的端口号 8080
location.protocol 返回页面使用的协议 https:
location.search 返回URL的查询字符串(?开头) ?id=1&name=test

2.2 常用方法

方法 功能 是否生成历史记录
location.assign(url) 跳转到新的页面(重定向) ✅ 有后退按钮
location.replace(url) 重新定向URL,替换当前页面 ❌ 无后退按钮
location.reload() 重新加载当前页面 ---
location.reload(true) 强制刷新(跳过缓存) ---
复制代码
// 跳转到百度
location.assign("https://www.baidu.com");

// 替换当前页面(不能后退)
location.replace("https://www.baidu.com");

// 刷新页面
location.reload();

// 强制刷新(跳过缓存)
location.reload(true);

三、Navigator对象

3.1 常用属性

navigator对象提供浏览器自身的信息。

复制代码
console.log(navigator.appName);     // Netscape(网景公司)
console.log(navigator.userAgent);   // 浏览器完整信息

3.2 浏览器类型判断

复制代码
if (/Chrome/i.test(navigator.userAgent)) {
    alert("谷歌浏览器");
} else if (/firefox/i.test(navigator.userAgent)) {
    alert("火狐浏览器");
} else if (/msie/i.test(navigator.userAgent)) {
    alert("IE浏览器");
} else if ("ActiveXObject" in window) {
    alert("你是IE(IE11特殊检测)");
} else {
    alert("其他浏览器");
}

四、History对象

history对象可以用来操作浏览器向前或者向后。

复制代码
// 获取历史记录数量
console.log(history.length);

// 后退一页
history.back();

// 前进一页
history.forward();

// 向前跳转一个页面
history.go(1);

// 向后跳转一个页面
history.go(-1);
方法 功能 等效操作
back() 后退一页 浏览器"后退"按钮
forward() 前进一页 浏览器"前进"按钮
go(n) 跳转n页(正数前进,负数后退) go(1) = forward()

五、Offset属性

5.1 offset概述

使用offset系列相关属性可以动态地得到该元素的位置(偏移) 和大小。

特点:

  • 获得元素距离带有定位祖先元素的位置
  • 获得元素自身的大小(宽度和高度)
  • 返回的数值都不带单位

5.2 offset常用属性

属性 作用
element.offsetParent 返回作为该元素带有定位的父级元素,如果父级没有定位则返回body
element.offsetTop 返回元素最左上角点相对于有定位父元素上方的偏移量
element.offsetLeft 返回元素最左上角点相对于有定位父元素左方的偏移量
element.offsetWidth 返回自身包括padding、边框、内容区的宽度(数值不带单位)
element.offsetHeight 返回自身包括padding、边框、内容区的高度(数值不带单位)
复制代码
var box = document.querySelector(".box");
var box1 = document.querySelector(".box1");

// 获取该元素带有定位的祖先级元素
console.log(box1.offsetParent);

// 返回元素相对于定位祖先元素上方的偏移量
console.log(box1.offsetTop);

// 返回元素相对于定位祖先元素左方的偏移量
console.log(box1.offsetLeft);

// 返回自身的宽度(包括内容区、边框、padding)
console.log(box.offsetWidth);

// 返回自身的高度(包括内容区、边框、padding)
console.log(box.offsetHeight);

六、Client属性

6.1 client概述

使用client系列的相关属性来获取元素可视区的相关信息,可以动态地得到该元素的边框大小、元素大小等。

6.2 client常用属性

属性 作用
element.clientTop 返回元素上边框的大小
element.clientLeft 返回元素左边框的大小
element.clientWidth 返回自身包括padding、内容区宽度,不含边框(数值不带单位)
element.clientHeight 返回自身包括padding、内容区高度,不含边框(数值不带单位)
复制代码
var box = document.querySelector(".box");

// 返回元素上边框大小
console.log(box.clientTop);   // 10

// 返回元素左边框大小
console.log(box.clientLeft);  // 10

// 返回自身的宽度,包括padding、内容区,不含边框
console.log(box.clientWidth); // 140

// 返回自身的高度,包括padding、内容区,不含边框
console.log(box.clientHeight); // 140

七、Scroll属性

7.1 scroll概述

利用scroll系列的相关属性可以动态地得到该元素的大小 和滚动距离。

7.2 scroll常用属性

属性 作用
element.scrollTop 返回被卷去的上侧距离(数值不带单位)
element.scrollLeft 返回被卷去的左侧距离(数值不带单位)
element.scrollWidth 返回自身实际的宽度(实际内容的宽度+内边距),不含边框
element.scrollHeight 返回自身实际的高度(实际内容的高度+内边距),不含边框
复制代码
var box = document.querySelector(".box1");

// 返回自身实际的宽度(实际内容的宽度),不含边框
console.log("scrollWidth", box.scrollWidth);

// 返回自身实际的高度(实际内容的高度),不含边框
console.log("scrollHeight", box.scrollHeight);

// 返回被卷去的上侧距离
console.log("scrollTop", box.scrollTop);

// 返回被卷去的左侧距离
console.log("scrollLeft", box.scrollLeft);

7.3 滚动事件监听

复制代码
box.addEventListener("scroll", function() {
    console.log("scrollTop", box.scrollTop);
    console.log("scrollLeft", box.scrollLeft);
});

八、三大尺寸家族对比总结

8.1 核心对比表

属性系列 包含内容 应用场景
offset​ 内容 + padding + 边框 获取元素整体尺寸和位置
client​ 内容 + padding(不含边框) 获取元素可视区域尺寸
scroll​ 实际内容尺寸(含溢出部分) 检测滚动相关属性

8.2 offset与style的区别

对比项 offset style
样式来源​ 任意样式表(行内、内部、外部) 仅行内样式表
返回值类型​ 数值(不带单位) 字符串(带单位,如"100px")
包含内容​ offsetWidth包含padding+border+width style.width不包含padding和border
读写权限​ 只读(只能获取,不能赋值) 可读写(可以获取也可以赋值)
适用场景​ 获取元素大小和位置 给元素更改样式值

8.3 三大系列属性速查

属性 包含边框 包含padding 包含内容 说明
offsetWidth/Height ✅ ✅ ✅ 元素整体尺寸
clientWidth/Height ❌ ✅ ✅ 可视区域尺寸
scrollWidth/Height ❌ ✅ ✅(含溢出) 实际内容尺寸
offsetTop/Left --- --- --- 相对于定位父级的偏移
clientTop/Left 边框本身 --- --- 边框宽度
scrollTop/Left --- --- --- 被卷去的距离

总结

本文系统梳理了JavaScript BOM第二部分和JS执行机制的完整知识体系:

主题 核心要点
JS执行机制​ 单线程、同步/异步、事件轮询(Event Loop)、任务队列
Location对象​ href/hash/host/search属性、assign/replace/reload方法
Navigator对象​ userAgent检测浏览器类型
History对象​ back/forward/go操作历史记录
Offset系列​ 元素整体尺寸(含边框)+ 相对定位父级的偏移
Client系列​ 可视区域尺寸(不含边框)+ 边框大小
Scroll系列​ 实际内容尺寸 + 被卷去的距离 + 滚动事件监听

掌握这些内容,你就具备了操控浏览器行为和测量页面元素尺寸的完整能力。

相关推荐
Sarvartha9 小时前
final 关键字
java·开发语言
程序员Sunday10 小时前
JavaScript 事件循环面试题,宏任务与微任务怎么执行|Sunday面试指南
开发语言·javascript·面试·校招·事件循环·程序员sunday
zhangzeyuaaa11 小时前
Ruby 多线程、GVL(GIL)与 Mutex 完全指南
开发语言·前端·ruby
默_笙11 小时前
🌊 向量库和 ES 都查不出"关系",我只好给奶茶建了一张人脉网
前端·javascript
谢亮_vipxieliang12 小时前
用 PHP 构建轻量级 REST API:从路由到鉴权的完整实践
开发语言·后端·php
思无邪6612 小时前
用 AI 做 JS 逆向:从抓包到复现的完整方法论
开发语言·javascript·人工智能
H.莓飛12 小时前
【数据结构】二叉树_OJ题
linux·开发语言·数据结构·算法
福兮说13 小时前
浏览器里把 iPhone 的 HEIC 转成 JPG:Chrome 解不开、转完大了七成、拍摄时间全丢,六个坑实测
前端·javascript·图像处理·chrome·ios·iphone·heic
零基础12313 小时前
Ubuntu 常用命令汇总
linux·运维·开发语言
外收内放13 小时前
Python基础语法练习题(57-58)
开发语言·python