当前位置: 首页 > news >正文

Python Day 33 JavaScript BOM 与 DOM 核心笔记整合

一、函数基础:参数默认值处理

1. 空值合并运算符(??)实现参数默认值

  • 核心作用:仅当参数为 null 或 undefined 时启用默认值(区别于 ||,不会覆盖 0'' 等 falsy 值)
  • 示例代码

    function test(a, b) {// 若 a 是 null/undefined,a 取默认值 10a = a ?? 10;// 建议补充 b 的默认值逻辑,避免返回 NaNb = b ?? 0;return a + b;
    }
    console.log(test()); // 输出 10(a=10,b=0)
    console.log(test(5)); // 输出 5(a=5,b=0)
    console.log(test(null, 8)); // 输出 18(a=10,b=8)
    

二、BOM(浏览器对象模型)核心内容

BOM 是操作浏览器窗口的 API,顶层对象为 window(可省略不写),核心子对象包括 navigatorlocationhistory 等。

1. window 对象(BOM 顶层核心)

(1)全局变量与 window 属性的关联

全局变量 / 函数会自动成为 window 的属性 / 方法,两种访问方式等价:

var globalNum = 20; // 全局变量
console.log(globalNum); // 20(直接访问)
console.log(window.globalNum); // 20(通过 window 访问)function globalFn() {console.log('全局函数');
}
globalFn(); // 全局函数
window.globalFn(); // 全局函数
(2)窗口对话框(均阻塞代码,需用户操作后继续)
对话框类型语法功能与返回值
提示框alert(message)仅展示信息,无返回值(点击 “确认” 后执行后续代码)
确认框confirm(tips)展示 “确定 / 取消”,返回 true(确定)或 false(取消)
输入框prompt(tips, [def])支持用户输入,返回输入内容(确定)或 null(取消),def 为可选默认输入值

  • 示例代码

    // 1. 提示框
    alert('请完成表单填写');// 2. 确认框(删除逻辑示例)
    if (confirm('确定要删除这条数据吗?')) {console.log('数据删除成功');
    } else {console.log('删除操作已取消');
    }// 3. 输入框(获取用户名示例)
    let userName = prompt('请输入您的用户名', '默认用户');
    console.log(userName); // 输入内容或 null
    
(3)定时器任务(延迟 / 轮询)
任务类型创建方法取消方法核心说明
延迟任务setTimeout(fn, delay)clearTimeout(taskId)延迟 delay 毫秒(1 秒 = 1000ms)执行 fn,仅执行 1 次,返回任务 ID
轮询任务setInterval(fn, duration)clearInterval(taskId)每隔 duration 毫秒重复执行 fn,返回任务 ID,需手动取消否则持续执行

  • 示例 1:3 秒后输出内容(延迟任务)

    const delayTask = setTimeout(() => {console.log('3秒后执行');
    }, 3000);
    // (可选)3秒内取消任务:clearTimeout(delayTask);
    
  • 示例 2:10 秒倒计时(轮询任务)

    // 需页面提前添加 <h1>10</h1>
    let count = 10;
    const timer = setInterval(() => {const h1 = document.querySelector('h1');count--;h1.innerText = count;// 倒计时结束,停止轮询if (count <= 0) {clearInterval(timer);h1.innerText = '时间到!';}
    }, 1000);
    
(4)窗口操作(打开 / 关闭 / 父子关联)
  • window.open(url, target, features):打开新窗口,返回新窗口对象

    • url:新窗口地址(如 https://www.baidu.com
    • target:打开方式(_blank 新窗口,_self 当前窗口)
    • features:窗口属性(如 width=600,height=400,top=100,left=100
  • window.close():关闭当前窗口(或通过新窗口对象关闭指定窗口)

  • window.opener:获取当前窗口的 “打开者”(父窗口)

  • 示例代码

    // 打开新窗口,3秒后自动关闭
    const newWin = window.open('https://www.baidu.com','_blank','width=600,height=400,top=100,left=100'
    );
    setTimeout(() => {newWin.close(); // 关闭新窗口
    }, 3000);
    
(5)其他常用方法 / 属性
方法 / 属性功能
window.btoa(str)Base64 编码(仅支持 Latin-1 字符,中文需先 encodeURI
window.atob(encodedStr)Base64 解码(解码后需 decodeURI 还原中文)
window.scrollX/scrollY获取水平 / 垂直滚动条滚动距离(单位:像素)
window.scrollTo(x, y)滚动到指定位置(如 scrollTo({top: 0, left: 0}) 回到页面顶部)
window.scrollBy(x, y)相对当前位置偏移滚动(如 scrollBy({top: 200}) 向下滚 200 像素)

  • 中文 Base64 编解码示例

    // 中文编码:先转义再编码
    const encoded = btoa(encodeURI('前端笔记'));
    console.log(encoded); // 输出 "5Yqh5ouG5pWw5o2u"// 中文解码:先解码再还原
    const decoded = decodeURI(atob(encoded));
    console.log(decoded); // 输出 "前端笔记"
    

2. navigator 对象(浏览器信息)

用于获取浏览器及设备相关信息,核心属性:

  • navigator.userAgent:获取浏览器 “用户代理” 字符串(用于判断浏览器类型 / 版本)

    console.log(navigator.userAgent);
    // 示例输出:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36
    
  • navigator.cookieEnabled:判断浏览器是否启用 Cookie(返回 true/false

    console.log(navigator.cookieEnabled); // true(若启用 Cookie)
    
  • navigator.geolocation.getCurrentPosition():获取设备地理位置(需用户授权)

    // 成功回调:获取经纬度并解析地址(示例用高德地图API)
    navigator.geolocation.getCurrentPosition((position) => {const lat = position.coords.latitude; // 纬度const lng = position.coords.longitude; // 经度const key = '你的高德地图APIKey'; // 需替换为自己的APIKey// 调用高德地图逆地理编码APIfetch(`https://restapi.amap.com/v3/geocode/regeo?key=${key}&location=${lng},${lat}`).then(res => res.json()).then(data => {if (data.status === '1') {document.querySelector('#addr').innerText = `当前地址:${data.regeocode.formatted_address}`;}});},(error) => {console.log('定位失败:', error.message);}
    );
    // 页面需添加 <div id="addr"></div> 用于展示地址
    

3. location 对象(当前网址操作)

(1)获取网址各组成部分
属性名功能说明
location.protocol获取协议(如 http:https:
location.hostname获取主机 / 域名(如 www.baidu.com127.0.0.1
location.port获取端口号(http 默认 80、https 默认 443,无显式端口则返回空字符串)
location.pathname获取请求路径(如 /index.html/user/profile
location.search获取查询参数(如 ?name=张三&age=20
location.hash获取锚点(如 #section1,用于页面内定位)
location.href获取完整网址(如 https://www.baidu.com/s?wd=js),也可用于跳转

  • 示例代码

    console.log('协议:', location.protocol);
    console.log('域名:', location.hostname);
    console.log('查询参数:', location.search);
    console.log('完整网址:', location.href);
    
(2)页面跳转方法
方法名功能说明
location.href = url跳转到指定 url,会生成历史记录(可通过 history.back() 后退)
location.assign(url)与 href 效果一致,跳转到指定 url 并生成历史记录
location.replace(url)跳转到指定 url不生成历史记录(无法通过 history.back() 后退)

  • 示例代码

    // 1秒后跳转到百度(生成历史记录)
    setTimeout(() => {location.href = 'https://www.baidu.com';// 或 location.assign('https://www.baidu.com');
    }, 1000);// 跳转到谷歌(不生成历史记录)
    // location.replace('https://www.google.com');
    

4. history 对象(历史记录操作)

用于操作浏览器的访问历史(前进 / 后退),核心方法:

方法名功能说明
history.back()后退到上一页(等价于浏览器 “后退” 按钮)
history.forward()前进到下一页(等价于浏览器 “前进” 按钮)
history.go(n)跳转到历史记录中第 n 页(n=1 前进 1 页,n=-1 后退 1 页,n=0 刷新当前页)

  • 示例代码

    // 后退1页
    document.querySelector('#backBtn').addEventListener('click', () => {history.back();
    });// 前进1页
    document.querySelector('#forwardBtn').addEventListener('click', () => {history.forward();
    });// 刷新当前页
    history.go(0);
    

三、DOM(文档对象模型)核心操作

DOM 是操作 HTML 文档结构的 API,核心流程:选中元素 → 操作元素

1. 元素选择器(选中元素的核心方法)

(1)常用选择器对比
选择器方法语法示例返回值类型核心特点
querySelector(cssSelector)document.querySelector('#box')单个元素对象(Element)支持所有 CSS 选择器,返回第一个匹配元素,无匹配则返回 null
querySelectorAll(cssSelector)document.querySelectorAll('.item')NodeList(类数组)支持所有 CSS 选择器,返回所有匹配元素,无匹配则返回空 NodeList
getElementById(id)document.getElementById('username')单个元素对象按 id 选择,id 需页面唯一,性能优,无匹配则返回 null
getElementsByTagName(tag)document.getElementsByTagName('p')HTMLCollection(类数组)按标签名选择,实时更新(DOM 变化时自动同步)
getElementsByClassName(className)document.getElementsByClassName('active')HTMLCollection按 class 选择,实时更新
getElementsByName(name)document.getElementsByName('hobby')NodeList按 name 属性选择(常用于表单元素,如单选框、复选框)

  • 示例代码

    // 1. querySelector(选第一个 .link 类元素)
    const firstLink = document.querySelector('.link');// 2. querySelectorAll(选所有 p 标签下的 a 标签)
    const allLinks = document.querySelectorAll('p a');
    allLinks.forEach(link => {console.log(link.innerText);
    });// 3. getElementById(选 id 为 header 的元素)
    const header = document.getElementById('header');// 4. getElementsByTagName(选所有 div 标签)
    const allDivs = document.getElementsByTagName('div');
    
(2)基础 CSS 选择器参考(配合 querySelector 使用)
选择器类型语法示例功能说明
通用选择器*选所有元素
标签选择器pa选指定标签元素
类选择器.active选 class="active" 的元素
ID 选择器#box选 id="box" 的元素
属性选择器[href][type="text"]选有指定属性或属性值匹配的元素
后代选择器div p选 div 内部所有 p 元素(包括子、孙等后代)
子选择器div > p选 div 直接子元素中的 p(仅一级子元素)
相邻兄弟选择器p + a选 p 元素后紧邻的第一个 a 元素
通用兄弟选择器p ~ a选 p 元素后所有同级的 a 元素
分组选择器p, a, div同时选 p、a、div 元素
伪类选择器a:hoverli:first-child按状态(hover)或位置(first-child)选择
伪元素选择器p::beforediv::after选择元素的伪元素(需配合样式操作,JS 无法直接选中伪元素节点)

2. 元素标签操作(选中元素后的核心操作)

(1)获取标签基础信息
属性名功能说明
tagName获取标签名(全大写,如 PA),Element 节点特有
nodeName获取节点名(标签节点返回全大写标签名,文本节点返回 #text

  • 示例代码

    const pTag = document.querySelector('p');
    console.log('标签名(tagName):', pTag.tagName); // P
    console.log('节点名(nodeName):', pTag.nodeName); // P
    
(2)操作标签体内容(文本 / 超文本)
属性名功能说明特点
innerText获取 / 设置标签体纯文本内容自动过滤 HTML 标签,设置时会将 < 转义为 &lt;(仅显示文本)
textContent与 innerText 类似,获取 / 设置纯文本内容更规范(W3C 标准),会保留空格和换行,兼容性稍好
innerHTML获取 / 设置标签体超文本内容保留 HTML 标签,设置时可解析标签(如设置 <a href="#">链接</a> 会生成链接)
outerHTML获取标签自身 + 内部的超文本内容(设置时会替换整个标签)包含标签本身,如 <p><a>hello</a></p>,慎用设置功能

  • 示例代码(页面需提前添加 <p><a href="">hello</a></p>):

    const pTag = document.querySelector('p');// 1. 获取内容
    console.log('innerText(纯文本):', pTag.innerText); // hello
    console.log('innerHTML(超文本):', pTag.innerHTML); // <a href="">hello</a>
    console.log('outerHTML(自身+内部):', pTag.outerHTML); // <p><a href="">hello</a></p>// 2. 设置内容
    pTag.innerText = '<a href="#">world</a>'; // 页面显示:<a href="#">world</a>(纯文本)
    pTag.innerHTML = '<a href="#">world</a>'; // 页面显示:world(可点击的链接)
    

(3)操作标签特有属性(如表单、链接、图片)

标签特有的属性(如 a 标签 hrefimg 标签 src、表单 value)支持直接通过 . 访问和修改:

一、DOM 交互实战案例

1. 复选框全选 / 反选与选中内容展示

功能需求
  • 实现 “全选” 复选框控制所有子复选框(爱好)的选中状态
  • 子复选框选中状态变化时,同步 “全选” 框状态
  • 实时展示当前选中的爱好名称
核心代码与解析
<!-- HTML 结构 -->
<input type="checkbox" name="" id="all" onchange="checkHobby(this)">全选
<hr>
<input type="checkbox" name="" id="" onchange="ckHobby()">游泳
<input type="checkbox" name="" id="" onchange="ckHobby()">爬山
<input type="checkbox" name="" id="" onchange="ckHobby()">看书
<input type="checkbox" name="" id="" onchange="ckHobby()">听音乐
<p>当前选中的爱好有 :  [<span></span>]</p><script>
// 1. 提前获取所有子复选框(排除全选框)和展示选中内容的 span
let checkList = document.querySelectorAll("input[type=checkbox]:not(#all)");
let spanIs = document.querySelector('p span');// 2. 全选框改变事件:控制所有子复选框
function checkHobby(tag) {const isChecked = tag.checked; // 获取全选框当前状态// 遍历子复选框,同步状态for (let item of checkList) {item.checked = isChecked;}// 调用更新选中内容的逻辑(复用代码)updateSelectedHobbies();
}// 3. 子复选框改变事件:同步全选框状态
function ckHobby() {let isAllChecked = true; // 假设初始为全选状态// 遍历子复选框,判断是否有未选中的for (let item of checkList) {if (!item.checked) {isAllChecked = false;break; // 有一个未选中则跳出循环}}// 同步全选框状态document.querySelector('#all').checked = isAllChecked;// 更新选中内容updateSelectedHobbies();
}// 4. 复用函数:更新当前选中的爱好文本
function updateSelectedHobbies() {const selected = [];for (let item of checkList) {if (item.checked) {// 获取复选框后的文本(需去除空格)selected.push(item.nextSibling.textContent.trim());}}// 拼接选中内容并展示spanIs.innerText = selected.join('、');
}
</script>
关键知识点
  • querySelectorAll("input[type=checkbox]:not(#all)"):使用 CSS 伪类 :not() 排除指定元素
  • nextSibling.textContent:获取元素的下一个兄弟节点(文本节点)的内容
  • 逻辑复用:将 “更新选中内容” 抽取为独立函数,避免代码冗余

2. 星级评分(点击星星高亮)

功能需求
  • 点击某个星星时,该星星及左侧所有星星高亮(红色),右侧星星恢复默认色(米色)
核心代码与解析
<style>span {color: beige; /* 默认色:米色 */cursor: pointer; /* 鼠标悬浮为手型,提示可点击 */font-size: 24px; /* 放大星星,增强交互感 */}
</style><p><span>❤</span><span>❤</span><span>❤</span><span>❤</span><span>❤</span></p><script>// 1. 获取所有星星元素let spanList = document.querySelectorAll('span');// 2. 为每个星星绑定点击事件for (let star of spanList) {star.addEventListener('click', function() {// 步骤1:先将所有星星恢复默认色spanList.forEach(s => s.style.color = 'beige');// 步骤2:高亮当前星星及左侧所有星星for (let s of spanList) {s.style.color = 'red'; // 高亮为红色if (s === this) break; // 遇到当前点击的星星则停止}});}
</script>
关键知识点
  • addEventListener:事件绑定(相比 onclick 更灵活,支持多事件绑定)
  • 遍历逻辑:先 “重置” 所有元素状态,再 “高亮” 目标及左侧元素,逻辑清晰

3. 用户名 / 密码框切换

功能需求
  • 点击按钮实现 “用户名输入框” 与 “密码输入框” 的切换(含文本提示和输入类型切换)
  • 切换时清空输入框内容,避免敏感信息残留
核心代码与解析
<!-- HTML 结构 -->
<p>用户名:<input class="input-box" type="text"></p>
<input type="button" value="切换" class="switch-btn"><script>// 1. 获取按钮、文本提示父容器、输入框let switchBtn = document.querySelector('.switch-btn');let textContainer = document.querySelector('p');let inputBox = document.querySelector('.input-box');// 2. 状态标记:true 表示当前是用户名框,false 表示密码框let isUsername = true;// 3. 按钮点击事件switchBtn.addEventListener('click', function() {inputBox.value = ''; // 切换时清空输入内容if (isUsername) {// 切换为密码框:修改文本提示和输入类型textContainer.firstChild.textContent = "密码:";inputBox.type = 'password';} else {// 切换为用户名框:恢复文本提示和输入类型textContainer.firstChild.textContent = "用户名:";inputBox.type = 'text';}// 切换状态标记isUsername = !isUsername;});
</script>
关键知识点
  • input.type:动态修改输入框类型(text 或 password
  • firstChild.textContent:修改父容器中第一个文本节点的内容(实现 “用户名 / 密码” 提示切换)

4. 2 小时倒计时(含开始 / 暂停 / 继续 / 结束功能)

功能需求
  • 初始显示 02:00:00,点击 “开始” 启动倒计时
  • 支持 “暂停”(停止倒计时但保留剩余时间)、“继续”(恢复倒计时)、“结束”(重置为初始状态)
核心代码与解析
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>2小时倒计时</title><script>// 1. 初始化变量:总秒数(2小时 = 2*60*60 = 7200秒)、定时器ID、倒计时状态let totalSeconds = 2 * 60 * 60;let timerId = null; // 存储定时器ID,用于暂停/结束let isRunning = false; // 标记是否正在倒计时// 2. 格式化时间:将秒数转为 "HH:MM:SS" 格式(补前导0)function formatTime(seconds) {const hours = Math.floor(seconds / 3600).toString().padStart(2, '0');const minutes = Math.floor((seconds % 3600) / 60).toString().padStart(2, '0');const secs = (seconds % 60).toString().padStart(2, '0');return `${hours}:${minutes}:${secs}`;}// 3. 开始倒计时function startCountdown() {if (isRunning) return; // 避免重复启动isRunning = true;timerId = setInterval(() => {totalSeconds--;// 倒计时结束:清除定时器,重置状态if (totalSeconds <= 0) {clearInterval(timerId);isRunning = false;totalSeconds = 2 * 60 * 60; // 重置为初始值}// 更新页面显示document.querySelector('.time').innerText = formatTime(totalSeconds);}, 1000); // 每秒执行一次}// 4. 暂停倒计时function pauseCountdown() {if (!isRunning) return;clearInterval(timerId);isRunning = false;}// 5. 结束倒计时(重置为初始状态)function endCountdown() {clearInterval(timerId);isRunning = false;totalSeconds = 2 * 60 * 60;document.querySelector('.time').innerText = formatTime(totalSeconds);}</script>
</head>
<body><h1 class="time">02:00:00</h1><button onclick="startCountdown()">开始</button><button onclick="pauseCountdown()">暂停</button><button onclick="startCountdown()">继续</button> <!-- 继续复用开始逻辑 --><button onclick="endCountdown()">结束</button>
</body>
</html>
关键知识点
  • padStart(2, '0'):字符串补全方法,确保时间格式为两位数(如 1 → 01
  • 定时器控制:通过 timerId 管理定时器,实现 “暂停”(clearInterval)和 “继续”(重新启动定时器)
  • 状态标记 isRunning:避免重复启动定时器导致倒计时加速

5. 表单验证(用户名 / 密码 / 邮箱)

功能需求
  • 用户名:仅允许字母、数字、下划线
  • 密码:长度 6~20 位
  • 确认密码:与密码一致
  • 邮箱:符合标准邮箱格式(含 @ 和域名后缀)
  • 点击 “提交” 时触发所有校验,有错误则提示并终止提交
核心代码与解析
<!-- HTML 结构 -->
<p>用户名: <input class="username" type="text" placeholder="仅字母、数字、下划线"></p>
<p>密码: <input class="password" type="password" placeholder="6-20位"></p>
<p>确认密码: <input class="repassword" type="password"></p>
<p>邮箱: <input class="email" type="text" placeholder="如 xxx@xxx.com"></p>
<p><input class="submit-btn" type="button" value="提交"></p><script>const submitBtn = document.querySelector('.submit-btn');// 提交按钮点击事件submitBtn.addEventListener('click', function() {// 1. 获取所有输入值(trim() 去除首尾空格)const username = document.querySelector('.username').value.trim();const password = document.querySelector('.password').value;const repassword = document.querySelector('.repassword').value;const email = document.querySelector('.email').value.trim();// 2. 用户名校验:正则 /^[0-9a-zA-Z_]+$/ 匹配字母、数字、下划线if (!username) {alert('用户名不能为空!');return; // 有错误则终止后续校验} else if (!/^[0-9a-zA-Z_]+$/.test(username)) {alert('用户名仅允许字母、数字、下划线!');return;}// 3. 密码长度校验if (password.length < 6 || password.length > 20) {alert('密码长度必须为 6~20 位!');return;}// 4. 确认密码校验if (password !== repassword) {alert('两次输入的密码不一致!');return;}// 5. 邮箱格式校验:标准邮箱正则(含 @、域名后缀)const emailReg = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;if (!emailReg.test(email)) {alert('请输入正确的邮箱格式(如 xxx@xxx.com)!');return;}// 6. 所有校验通过alert('表单验证通过,提交成功!');});
</script>
关键知识点
  • 正则表达式应用:
    • 用户名:/^[0-9a-zA-Z_]+$/^ 开头,$ 结尾,确保整个字符串符合规则)
    • 邮箱:/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/(匹配用户名、@、域名、后缀)
  • 校验逻辑顺序:先校验非空,再校验格式 / 长度,最后校验一致性,逻辑清晰

6. 猜灯谜小游戏(调用第三方 API)

功能需求
  • 页面加载时自动获取灯谜(调用第三方 API)
  • 用户有 3 次答题机会,答错提示剩余次数,3 次答错显示正确答案和解析
  • 答对可切换下一题,支持回车键提交答案
核心代码与解析
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>猜灯谜</title><script>// 全局变量:当前灯谜数据、错误次数、最大错误次数、API密钥let currentRiddle = null;let errorCount = 0;const MAX_ERRORS = 3;const API_KEY = 'd9ea9ddca4f048b487a70dd8e29f3547'; // 第三方API密钥(需自行申请)// 页面加载完成后自动获取灯谜window.onload = fetchRiddle;// 1. 获取灯谜(调用第三方API)function fetchRiddle() {// 重置状态:错误次数、提示文本、输入框状态errorCount = 0;document.getElementById('errorMessage').textContent = '';document.getElementById('answerInput').value = '';document.getElementById('correctAnswer').style.display = 'none';document.getElementById('answerInput').disabled = false;document.getElementById('submitBtn').disabled = false;document.getElementById('nextRiddleBtn').style.display = 'none';// 调用API获取随机灯谜fetch(`https://api.tianapi.com/lantern/random?key=${API_KEY}`).then(response => response.json()).then(data => {currentRiddle = data.newslist[0]; // 提取灯谜数据document.getElementById('riddleQuestion').textContent = currentRiddle.question;}).catch(error => {alert('获取灯谜失败,请刷新页面重试!');console.error('API请求错误:', error);});}// 2. 验证答案function checkAnswer() {if (!currentRiddle) return; // 无灯谜数据时不执行const userAnswer = document.getElementById('answerInput').value.trim();const correctAnswer = currentRiddle.answer.trim();// 答案不区分大小写if (userAnswer.toLowerCase() === correctAnswer.toLowerCase()) {document.getElementById('errorMessage').textContent = '恭喜你,答对了!';document.getElementById('errorMessage').style.color = 'green';document.getElementById('nextRiddleBtn').style.display = 'inline-block';} else {errorCount++;// 提示剩余次数document.getElementById('errorMessage').textContent = `答错了!还有${MAX_ERRORS - errorCount}次机会`;document.getElementById('errorMessage').style.color = 'red';// 3次答错:显示正确答案和解析,禁用输入框if (errorCount >= MAX_ERRORS) {document.getElementById('correctAnswer').innerHTML = `<p>正确答案:${currentRiddle.answer}</p><p>解析:${currentRiddle.explanation || '暂无解析'}</p>`;document.getElementById('correctAnswer').style.display = 'block';document.getElementById('nextRiddleBtn').style.display = 'inline-block';document.getElementById('answerInput').disabled = true;document.getElementById('submitBtn').disabled = true;}}}// 3. 下一题(复用获取灯谜逻辑)function nextRiddle() {fetchRiddle();}// 4. 回车键提交答案function handleKeyPress(event) {if (event.key === 'Enter') {checkAnswer();}}</script>
</head>
<body><h1>猜灯谜</h1><div><h3 id="riddleQuestion">加载中...</h3><p>请输入答案:</p><input type="text" id="answerInput" onkeypress="handleKeyPress(event)"><button id="submitBtn" onclick="checkAnswer()">提交答案</button><button id="nextRiddleBtn" onclick="nextRiddle()" style="display: none;">下一个灯谜</button><p id="errorMessage"></p><div id="correctAnswer" style="display: none; margin-top: 10px; padding: 10px; border: 1px solid #ccc;"></div></div><p>提示:您有3次答题机会,3次错误后将显示正确答案和解析</p>
</body>
</html>
关键知识点
  • fetch API:用于调用第三方 API 获取数据,需处理异步逻辑(then/catch
  • 状态管理:通过全局变量 currentRiddleerrorCount 管理游戏状态
  • 键盘事件:onkeypress 监听回车键,提升用户体验

二、正则表达式实战

1. 提取字符串中的所有数字并求和

需求:从混合字符串中提取所有连续数字,转为数字后求和
const str = '46546asdafa87gfg654dg';
// 1. 正则 /\d+/g:匹配1个及以上连续数字,g表示全局匹配
const numArr = str.match(/\d+/g); 
// 2. 求和(先判断是否有匹配结果,避免报错)
const sum = numArr ? numArr.reduce((total, numStr) => total + parseInt(numStr), 0) : 0;
console.log(sum); // 46546 + 87 + 654 = 47287

2. 将字符串中的所有数字替换为 **

需求:将字符串中每个数字字符替换为 **

const str = '1sdad45s64ad';
// 正则 /\d/g:匹配单个数字,g表示全局替换
const newStr = str.replaceAll(/\d/g, '**'); 
console.log(newStr); // **sdad****s****ad

3. 手机号中间 4 位脱敏

需求:将手机号 15455367889 处理为 154****7889

const phone = '15455367889';
// 正则分组:(\d{3}) 匹配前3位,(\d{4}) 匹配中间4位,(\d{4}) 匹配后4位
const desensitizedPhone = phone.replace(/(\d{3})(\d{4})(\d{4})/, '$1****$3'); 
console.log(desensitizedPhone); // 154****7889

4. 邮箱用户名脱敏

需求:将邮箱 16544987465654@qq.com 处理为 1***@qq.com

const email = '16544987465654@qq.com';
// 正则分组:(.) 匹配第一个字符,.* 匹配中间任意字符,(@.*$) 匹配@及后面所有内容
const desensitizedEmail = email.replace(/(.).*(@.*$)/, '$1***$2'); 
console.log(desensitizedEmail); // 1***@qq.com

5. 提取文件路径中的文件名和扩展名

需求:从路径 /home/user/docs/report.pdf 中提取文件名 report 和扩展名 .pdf

const path = '/home/user/docs/report.pdf';
// 正则解析:
// ([^/]*?):匹配最后一个/后的非/字符(非贪婪,避免包含.)
// (\.[^.]*)$:匹配.及后面非.字符(直到字符串结束,即扩展名)
const matchResult = path.match(/([^/]*?)(\.[^.]*)$/);
if (matchResult) {const fileName = matchResult[1]; // reportconst extName = matchResult[2]; // .pdfconsole.log(`文件名:${fileName},扩展名:${extName}`);
}

6. 查找字符串中最长的字母单词

需求:从混合字符串中提取所有字母组成的单词,找到最长的一个

const str = '11465sdada55dasdwasd789522adsadada';
// 正则 /[a-z]+/ig:匹配所有字母(不区分大小写),g全局匹配
const words = str.match(/[a-z]+/ig); 
// 找到最长单词(reduce 遍历比较长度)
const longestWord = words.reduce((max, word) => word.length > max.length ? word : max, '');
console.log(longestWord); // adsadada(长度8)

7. 驼峰命名转脊柱命名

需求:将 fontSizeKiss 转为 font-size-kiss

const camelCase = 'fontSizeKiss';
// 正则 /[A-Z]/g:匹配大写字母,替换为 -+小写字母
const kebabCase = camelCase.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`);
console.log(kebabCase); // font-size-kiss

三、数组与对象实战

1. 多数组交叉组合

需求:将 [1, 2] 和 [3, 4] 组合为 [[1,3], [1,4], [2,3], [2,4]]

function combineArrays(...arrays) {// 边界处理:无输入数组返回空数组if (arrays.length === 0) return [];// 初始值:将第一个数组的每个元素转为数组(如 [1,2] → [[1], [2]])let result = arrays[0].map(item => [item]);// 遍历后续数组,与现有结果交叉组合for (let i = 1; i < arrays.length; i++) {const temp = [];// 现有结果中的每个组合,与当前数组的每个元素拼接result.forEach(combo => {arrays[i].forEach(item => {temp.push([...combo, item]); // 展开现有组合,添加新元素});});result = temp; // 更新结果}return result;
}// 测试
console.log(combineArrays([1,2], [3,4])); // [[1,3],[1,4],[2,3],[2,4]]
console.log(combineArrays([1], [2,3], [4])); // [[1,2,4], [1,3,4]]

2. 员工数据多维度处理

需求:基于员工数组,实现筛选、排序、求和、分组等操作

const employees = [{ name: 'Alice', age: 30, department: 'Engineering' },{ name: 'Bob', age: 25, department: 'Marketing' },{ name: 'Charlie', age: 35, department: 'Engineering' },{ name: 'David', age: 40, department: 'Sales' }
];// 1. 获取 Engineering 部门员工
const engineeringEmp = employees.filter(emp => emp.department === 'Engineering');// 2. 按年龄升序排序(注意:sort 会修改原数组,需先复制)
const sortedByAge = [...employees].sort((a, b) => a.age - b.age);// 3. 计算所有员工年龄和
const totalAge = employees.reduce((sum, emp) => sum + emp.age, 0);// 4. 统计各部门人数
const deptCount = employees.reduce((acc, emp) => {acc[emp.department] = (acc[emp.department] || 0) + 1;return acc;
}, {});// 5. 按部门分组(值为员工数组)
const deptGroup = employees.reduce((acc, emp) => {if (!acc[emp.department]) acc[emp.department] = [];acc[emp.department].push(emp);return acc;
}, {});// 6. 查找年龄最大的员工
const oldestEmp = employees.reduce((oldest, current) => current.age > oldest.age ? current : oldest, employees[0]
);// 输出结果
console.log('1. Engineering部门员工:', engineeringEmp);
console.log('2. 按年龄升序:', sortedByAge);
console.log('3. 总年龄:', totalAge);
console.log('4. 各部门人数:', deptCount);
console.log('5. 按部门分组:', deptGroup);
console.log('6. 年龄最大员工:', oldestEmp);

3. 实现可遍历的自定义对象(for...of 支持)

需求:自定义对象支持 push/unshift/pop/shift 等数组方法,并可通过 for...of 遍历

let obj = {values: [], // 存储数据的内部数组// 1. 向尾部添加元素push(...val) {this.values.push(...val);return this.values.length;},// 2. 向头部添加元素unshift(...val) {this.values.unshift(...val);return this.values.length;},// 3. 移除尾部元素pop() {return this.values.pop();},// 4. 移除头部元素shift() {return this.values.shift();},// 5. 获取指定索引元素get(index) {return index >= 0 && index < this.values.length ? this.values[index] : undefined;},// 6. 修改指定索引元素set(index, value) {if (index >= 0 && index < this.values.length) {this.values[index] = value;}},// 7. 转为字符串toString() {return `[${this.values.join(', ')}]`;},// 8. 实现迭代器(支持 for...of 遍历)[Symbol.iterator]() {let index = 0;const values = this.values;return {next() {return index < values.length ? { value: values[index++], done: false } : { done: true };}};}
};// 测试
obj.push(1, 2, 3);
for (let val of obj) {console.log(val); // 1, 2, 3
}

四、解构赋值实战

1. 数组混合解构(提取部门员工)

需求:从部门数组中提取 HR 部门第一个员工、IT 部门最后一个员工

const departments = [{ name: 'HR', employees: ['Alice', 'Bob', 'Charlie'] },{ name: 'IT', employees: ['Dave', 'Eve', 'Frank'] }
];// 解构:HR 部门取第一个员工,IT 部门取第三个(最后一个)员工
const [{ employees: [firstHREmployee] }, { employees: [, , lastITEmployee] }] = departments;
console.log(firstHREmployee); // Alice
console.log(lastITEmployee); // Frank

2. 对象复杂解构(提取配置项)

需求:从配置对象中提取服务器地址(重命名)、超时时间(默认值)、重试次数(默认值)

javascript

运行

const config = {server: { host: 'localhost', port: 8080 },timeout: 0,retries: undefined
};// 解构:重命名 host/port,timeout 为0时用默认值5000,retries 未定义时用默认值3
const {server: { host: serverHost, port: serverPort },timeout: timeout = 5000,retries: retries = 3
} = config;console.log(serverHost); // localhost
console.log(serverPort); // 8080
console.log(timeout); // 0(原值为0,不触发默认值)
console.log(retries); // 3(原值为undefined,触发默认值)

3. 解构函数返回值

需求:从函数返回的嵌套对象中提取 id、name、country

function getUserProfile() {return {id: 123,info: {name: 'Sarah',location: { city: 'New York', country: 'USA' }}};
}// 嵌套解构:提取 id、info.name、info.location.country
const {id: userId,info: {name: userName,location: { country: userCountry }}
} = getUserProfile();console.log(userId); // 123
console.log(userName); // Sarah
console.log(userCountry); // USA

五、工具函数实战

1. 随机生成 RGB 颜色

需求:编写函数 randomColor,返回 rgb(r, g, b) 格式的随机颜色(r/g/b 范围 0~255)

function randomColor() {// Math.random() 生成 0~1 随机数,*256 后取整(0~255)const r = Math.floor(Math.random() * 256);const g = Math.floor(Math.random() * 256);const b = Math.floor(Math.random() * 256);return `rgb(${r}, ${g}, ${b})`;
}// 测试:生成随机颜色并设置页面背景
document.body.style.backgroundColor = randomColor();

2. 查找所有三位数水仙花数

需求:水仙花数是指一个 3 位数,其各位数字的立方和等于该数本身(如 153 = 1³ + 5³ + 3³)

const narcissisticNumbers = [];
// 遍历所有三位数(100~999)
for (let i = 100; i <= 999; i++) {const hundreds = Math.floor(i / 100); // 百位const tens = Math.floor((i % 100) / 10); // 十位const units = i % 10; // 个位// 判断是否为水仙花数if (Math.pow(hundreds, 3) + Math.pow(tens, 3) + Math.pow(units, 3) === i) {narcissisticNumbers.push(i);}
}
console.log('三位数水仙花数:', narcissisticNumbers); // [153, 370, 371, 407]

3. 数字转中文大写(12 位以内)

需求:将数字 1234 转为 壹仟贰佰叁拾肆,处理中间含 0 的情况(如 1001 → 壹仟零壹

function numberToChinese(numStr) {// 边界校验:1~12位数字if (!/^\d{1,12}$/.test(numStr)) return "请输入1-12位数字";// 中文数字和单位(对应 个、十、百、千、万、十万、百万、千万、亿、十亿、百亿、千亿)const digits = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];const units = ['', '拾', '佰', '仟', '万', '拾', '佰', '仟', '亿', '拾', '佰', '仟'];let result = '';// 遍历数字的每一位,拼接“数字+单位”for (let i = 0; i < numStr.length; i++) {const digit = parseInt(numStr[i]);const unitIndex = numStr.length - 1 - i; // 计算当前位的单位索引result += digits[digit] + units[unitIndex];}// 处理冗余的“零”(关键步骤)result = result.replace(/零[拾佰仟]/g, '零') // 去掉“零十”“零百”等.replace(/零+/g, '零') // 多个零合并为一个.replace(/零(万|亿)/g, '$1') // 去掉“零万”“零亿”中的零.replace(/零$/, ''); // 去掉末尾的零// 特殊情况:结果为空时返回“零”return result || '零';
}// 测试
console.log(numberToChinese('1234')); // 壹仟贰佰叁拾肆
console.log(numberToChinese('1001')); // 壹仟零壹
console.log(numberToChinese('105')); // 壹佰零伍
console.log(numberToChinese('0')); // 零

4. BOM 实战:自动填充当前网址信息

需求:页面加载时,将当前网址的协议、域名、端口、请求地址、锚点自动填充到对应输入框

<!-- HTML 结构 -->
协议 : <input class="protocol" type="text"><br>
域名 : <input class="hostname" type="text"><br>
端口号: <input class="port" type="text"><br>
请求地址 : <input class="pathname" type="text"><br>
锚点 : <input class="hash" type="text"><br><script>
// 页面加载完成后填充
window.onload = function() {// 从 location 对象中获取各部分信息document.querySelector('.protocol').value = location.protocol.slice(0, -1); // 去掉末尾的冒号(如 http: → http)document.querySelector('.hostname').value = location.hostname;document.querySelector('.port').value = location.port || '80(http默认)/443(https默认)'; // 无端口时提示默认值document.querySelector('.pathname').value = location.pathname;document.querySelector('.hash').value = location.hash.slice(1); // 去掉开头的#(如 #section1 → section1)
};
</script>

5. 扩展 RegExp 原型:实现指定次数替换

需求:给 RegExp 类添加 sub 方法,支持替换指定次数的匹配内容(默认替换所有)

RegExp.prototype.sub = function(string, newStr, count = -1) {const regex = this;let match;let result = string;let replaceCount = 0;// 情况1:count=-1(默认),替换所有if (count === -1) {// 若原正则无 g 标志,添加 g 后替换const globalRegex = new RegExp(regex.source, regex.flags.includes('g') ? regex.flags : regex.flags + 'g');return result.replace(globalRegex, newStr);}// 情况2:count>0,替换指定次数// 若原正则有 g 标志,先去掉(避免全局匹配导致计数问题)const nonGlobalRegex = new RegExp(regex.source, regex.flags.replace('g', ''));while ((match = nonGlobalRegex.exec(result)) !== null && replaceCount < count) {// 替换当前匹配项result = result.slice(0, match.index) + newStr + result.slice(match.index + match[0].length);// 更新下一次匹配的起始位置(避免重复匹配同一位置)nonGlobalRegex.lastIndex = match.index + newStr.length;replaceCount++;}return result;
};// 测试
const str = 'xyz12345adsda1113';
const regex = /\d+/;
console.log(regex.sub(str, '*****', 2)); // xyz*****adsda*****3(替换前2个数字组合)
console.log(regex.sub(str, '*****')); // xyz*****adsda*****(替换所有数字组合)
    http://www.dtcms.com/a/349593.html

    相关文章:

  • 【数学建模】如何总结数学建模中的层次分析法最好
  • 通过Fiddler肆意修改接口返回数据进行测试
  • EXCEL自动调整列宽适应A4 A3 A2
  • OpenCV计算机视觉实战(21)——模板匹配详解
  • 将盾CDN:高防CDN和游戏盾有什么区别?
  • 宋红康 JVM 笔记 Day07|本地方法接口、本地方法栈
  • More Effective C++ 条款08:理解各种不同意义的new和delete
  • Genymotion 虚拟机如何安装 APK?(ARM 插件安装教程)
  • (操作系统)死锁是什么 必要条件 解决方式
  • 5分钟发布技术博客:cpolar简化Docsify远程协作流程
  • 《 nmcli网络管理学习》
  • [新启航]医疗器械深孔加工:新启航激光频率梳攻克 130mm 深度,实现 2μm 精度测量
  • Windows Server 2019 DateCenter搭建 FTP 服务器
  • MOLEX莫仕/莫莱克斯借助PCIe发展,引领数据中心的未来
  • 从Java全栈到前端框架的深度探索
  • gte2_common的作用
  • 数据集成平台-Kafka实时同步Doris能力演示
  • Appium学习笔记
  • 如何判断投手甲的认知比投手乙高?
  • “华生科技杯”2025年全国青少年龙舟锦标赛在海宁举行
  • 暴雨蓝色预警发布:我国多地将迎强降雨,局地伴有强对流天气 疾风气象大模型
  • 《李沐读论文》系列笔记:论文读写与研究方法【更新中】
  • 【机器学习】(11) --回归树算法
  • 【机器学习基础】朴素贝叶斯算法详解:从原理到实战
  • 机器学习-朴素贝叶斯
  • 机器学习采样方法深度详解:过采样、下采样与混合采样(附完整代码、可视化与多场景实战)
  • 机器学习:贝叶斯派
  • 【Linux | 网络】多路转接IO之poll
  • 编写Linux下usb设备驱动方法:usb设备驱动实现流程
  • AI-调查研究-60-机器人 机械臂技术发展趋势详解:工业、服务与DIY三大阵营全解析