第三部分:赋予网页灵魂 —— JavaScript(下)
目录
- 7 DOM 操作:控制网页的"智能面板
- 7.1 小例子:点击按钮时改变段落文字,根据用户输入改变图片
- 7.2 练习:实现一个简单的 Tab 切换效果
- 8 事件处理:响应用户的"指令"
- 8.1 小例子:实现点击按钮弹出提示框,监听输入框内容变化
- 8.2 练习:制作一个可拖拽的方块
- 9 异步编程:处理耗时任务的"预约"机制
- 9.1 小例子:使用 setTimeout 模拟异步,Promise 封装,async/await 调用
- 9.2 练习
- 10 与服务器通信:Fetch API - 获取外部数据的"遥控器
- 10.1 小例子:从 JSONPlaceholder (一个免费的假 API 服务) 获取用户数据并展示
- 10.2 练习:创建一个简单的天气查询应用
- 11 本地存储:浏览器的"小抽屉"
- 11.1 小例子:实现一个记住用户偏好(如主题颜色)的功能
- 11.2 练习
- 12 ES6+ 新特性选讲:现代 JavaScript 的便捷写法
- 12.1 小例子:演示各种新特性的用法
- 12.2 练习
- 阶段项目 3:打造你的第一个交互式应用
- 结语
7 DOM 操作:控制网页的"智能面板
DOM 是浏览器将 HTML 文档解析成的一个树状结构模型,JavaScript 可以通过操作这个 DOM 树来动态地修改网页的内容、结构和样式。就像智能家居的中控面板,你可以通过它控制家里的所有设备(HTML 元素)。
获取元素: 是 DOM 操作的第一步,找到你要控制的那个"设备"。
- getElementById: 通过 ID 获取唯一元素 (最快)。
- getElementsByTagName/ClassName: 获取符合条件的元素集合 。
- querySelector: 使用 CSS 选择器语法获取第一个匹配的元素 (非常强大常用)。
- querySelectorAll: 使用 CSS 选择器语法获取所有匹配的元素集合 。
- 修改内容: innerHTML (解析 HTML 标签), innerText (只获取/设置可见文本,受 CSS 影响), textContent (获取/设置所有文本内容,包括隐藏的,性能较好)。注意 innerHTML 的安全风险 (可能导致 XSS 攻击),如果只是设置文本,优先使用 textContent。
- 修改属性: 直接用点号访问标准属性 (img.src = ‘new.jpg’),或用 setAttribute/getAttribute 处理标准和自定义属性。
- 修改样式:
- element.style.propertyName: 直接修改内联样式 (如 el.style.color = ‘red’),propertyName 是 CSS 属性的驼峰命名 (如 backgroundColor)。适合少量动态修改。
- element.classList: 推荐的方式!通过添加/删除预定义的 CSS 类来改变样式,实现样式与行为分离。add(), remove(), toggle() (切换), contains() (检查)。
- 创建/插入/删除: 动态地添加或移除页面元素。createElement 创建新标签,createTextNode 创建文本节点,appendChild 在末尾添加子节点,insertBefore 在指定子节点前插入,removeChild 删除子节点。
7.1 小例子:点击按钮时改变段落文字,根据用户输入改变图片
HTML (dom.html):
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>DOM 操作示例</title><style>.highlight { background-color: yellow; font-weight: bold; }.hidden { display: none; }</style>
</head>
<body><h1 id="main-title">DOM 操作</h1><p id="text-to-change">这是初始文本。</p><button id="change-text-btn">改变文本</button><button id="toggle-style-btn">切换样式</button><hr><img id="my-image" src="https://placehold.co/200x100/007bff/ffffff?text=Image+1" alt="占位图" width="200"><br><input type="text" id="image-url-input" placeholder="输入新图片 URL"><button id="change-image-btn">改变图片</button><hr><div id="container"><p>第一个子元素</p></div><button id="add-element-btn">添加元素</button><button id="remove-element-btn">移除最后一个元素</button><script src="dom_script.js"></script>
</body>
</html>
JavaScript (dom_script.js):
// --- 获取元素 ---
const titleElement = document.getElementById('main-title');
const textElement = document.getElementById('text-to-change');
const changeTextBtn = document.getElementById('change-text-btn');
const toggleStyleBtn = document.getElementById('toggle-style-btn');
const myImage = document.getElementById('my-image');
const imageUrlInput = document.getElementById('image-url-input');
const changeImageBtn = document.getElementById('change-image-btn');
const container = document.getElementById('container');
const addBtn = document.getElementById('add-element-btn');
const removeBtn = document.getElementById('remove-element-btn');// --- 修改内容和样式 ---
changeTextBtn.onclick = function() { // 简单的事件处理 (后面会学 addEventListener)textElement.textContent = "文本已被 JavaScript 改变!";
};toggleStyleBtn.onclick = function() {// 使用 classList.toggle() 切换类textElement.classList.toggle('highlight');// 或者直接修改 style (不推荐大量使用)// if (textElement.style.color === 'red') {// textElement.style.color = 'black';// } else {// textElement.style.color = 'red';// }
};// --- 修改属性 ---
changeImageBtn.onclick = function() {const newImageUrl = imageUrlInput.value; // 获取输入框的值if (newImageUrl) { // 检查是否有输入myImage.src = newImageUrl;myImage.alt = "用户提供的新图片"; // 同时更新 alt 文本imageUrlInput.value = ''; // 清空输入框} else {alert("请输入图片 URL!");}
};
// 图片加载失败处理
myImage.onerror = function() {console.error("图片加载失败:", myImage.src);myImage.src = 'https://placehold.co/200x100/cccccc/ffffff?text=Image+Error'; // 替换为错误提示图myImage.alt = "图片加载失败";
};// --- 创建和插入/删除节点 ---
addBtn.onclick = function() {// 创建一个新的 <p> 元素const newParagraph = document.createElement('p');// 创建文本节点const newText = document.createTextNode(`这是新添加的元素 #${container.children.length + 1}`);// 将文本节点添加到 p 元素中newParagraph.appendChild(newText);// 将新的 p 元素添加到 container 的末尾container.appendChild(newParagraph);
};removeBtn.onclick = function() {const lastElement = container.lastElementChild; // 获取最后一个子元素if (lastElement) { // 确保有子元素可移除container.removeChild(lastElement);} else {console.log("容器内没有元素可以移除了。");}
};
运行后的效果
7.2 练习:实现一个简单的 Tab 切换效果
- HTML 结构:包含几个 Tab 按钮和一个内容区域,内容区域包含几个 div,每个 div 对应一个 Tab 的内容,初始时只有一个 div 显示,其他隐藏。
- JavaScript 逻辑:点击某个 Tab 按钮时,将所有内容 div 隐藏,然后只显示与被点击 Tab 对应的那个 div。同时,可以给当前激活的 Tab 按钮添加一个特殊的 CSS 类(例如 active)来高亮显示。
8 事件处理:响应用户的"指令"
事件处理让 JavaScript 能够响应用户的各种操作(点击、移动鼠标、敲击键盘、提交表单等)或浏览器的状态变化(页面加载完成、窗口大小改变等)。就像给智能家居系统添加传感器和响应规则:“当检测到有人按下门铃 (事件),就播放提示音 (处理函数)”。
- 事件监听器 (addEventListener): 推荐的绑定事件的方式。可以为一个事件绑定多个处理函数,更灵活。
- type: 事件类型字符串 (如 ‘click’, ‘mouseover’)。
- listener: 事件触发时要执行的函数(事件处理函数/回调函数)。
- options: 可选参数对象,如 { capture: true } (使用捕获阶段), { once: true } (只触发一次)。
- 事件对象 (event): 当事件触发时,浏览器会自动创建一个包含事件信息的对象,并将其作为参数传递给事件处理函数。你可以通过它获取鼠标位置 (event.clientX, event.clientY)、按下的键 (event.key)、目标元素 (event.target) 等信息。
- event.preventDefault(): 阻止事件的默认行为。例如,阻止链接 (
<a>
) 的跳转,阻止表单 (<form>
) 的默认提交。 - event.stopPropagation(): 阻止事件在 DOM 树中继续传播(通常是阻止冒泡)。
- 事件委托 (Event Delegation): 一个重要的性能优化技巧。将事件监听器添加到父元素上,利用事件冒泡来处理所有子元素的事件。特别适用于列表项很多或动态添加/删除子元素的情况。
8.1 小例子:实现点击按钮弹出提示框,监听输入框内容变化
HTML (event.html):
<!DOCTYPE html>
<html lang="zh-CN">
<head> <meta charset="UTF-8"> <title>事件处理</title> </head>
<body><button id="alert-btn">点我弹框</button><hr><label for="my-input">输入内容:</label><input type="text" id="my-input"><p>你输入了: <span id="output-span"></span></p><hr><a id="prevent-link" href="https://example.com">阻止默认跳转的链接</a><hr><div id="parent-div" style="padding: 20px; border: 1px solid blue;">父元素<button id="child-btn" style="margin-left: 10px;">子元素按钮</button></div><script src="event_script.js"></script>
</body>
</html>
JavaScript (event_script.js):
const alertBtn = document.getElementById('alert-btn');
const myInput = document.getElementById('my-input');
const outputSpan = document.getElementById('output-span');
const preventLink = document.getElementById('prevent-link');
const parentDiv = document.getElementById('parent-div');
const childBtn = document.getElementById('child-btn');// --- 基本事件监听 ---
alertBtn.addEventListener('click', function(event) {// event 对象包含了事件信息console.log('事件类型:', event.type);console.log('目标元素:', event.target);alert('按钮被点击了!');
});// 监听输入框的 'input' 事件 (内容实时变化时触发)
myInput.addEventListener('input', function(event) {outputSpan.textContent = event.target.value; // 将输入内容显示到 span
});// --- 阻止默认行为 ---
preventLink.addEventListener('click', function(event) {event.preventDefault(); // 阻止链接跳转alert('链接的默认跳转行为已被阻止!');
});// --- 阻止事件传播 (冒泡) ---
parentDiv.addEventListener('click', function(event) {console.log('父元素 Div 被点击了!');
});childBtn.addEventListener('click', function(event) {console.log('子元素 Button 被点击了!');event.stopPropagation(); // 阻止事件冒泡到父元素alert('子按钮点击,事件传播已阻止。');
});// --- 页面加载事件 ---
// 当整个页面及所有资源 (图片、样式表等) 加载完成时触发
window.addEventListener('load', function() {console.log('页面及所有资源加载完成!');
});// 当 HTML 文档被完全加载和解析完成之后触发,无需等待样式表、图像等完全加载
document.addEventListener('DOMContentLoaded', function() {console.log('DOM 完全加载并解析完成!(通常在这里执行需要操作 DOM 的代码)');// 可以在这里安全地获取和操作 DOM 元素alertBtn.style.backgroundColor = 'lightblue';
});
执行后的效果
8.2 练习:制作一个可拖拽的方块
- HTML: 创建一个 div 作为可拖拽的方块。
- CSS: 给方块设置 position: absolute; 或 relative; 使其可以定位。
- JavaScript:
- 监听方块的 mousedown 事件。
- 在 mousedown 处理函数中,记录鼠标初始位置和方块初始位置,并添加对 document 的 mousemove 和 mouseup 事件监听。
- 在 mousemove 处理函数中,计算鼠标移动的距离,并更新方块的 style.left 和 style.top 实现拖动。
- 在 mouseup 处理函数中,移除对 document 的 mousemove 和 mouseup 事件监听,停止拖动。
9 异步编程:处理耗时任务的"预约"机制
JavaScript 默认是单线程的,意味着一次只能做一件事。同步代码按顺序执行,如果遇到耗时操作(如网络请求、大量计算),就会阻塞后续代码的执行,导致页面卡顿。异步编程就是解决这个问题的机制,它允许耗时任务在后台执行,完成后再通知主线程(通过回调、Promise 或 async/await),就像你去餐厅点餐(发起异步任务),然后可以先玩手机(执行其他代码),等餐好了(任务完成)服务员再叫你(执行回调)。
- 回调函数: 最早的异步处理方式。将一个函数作为参数传递给异步函数,异步任务完成后调用该回调函数。缺点是如果多个异步任务有依赖关系,容易形成层层嵌套的"回调地狱 (Callback Hell)",代码难以阅读和维护。
- Promise: ES6 引入,用于更好地管理异步操作。Promise 对象代表一个尚未完成但最终会完成(或失败)的操作。它有三种状态:pending (进行中)、fulfilled (已成功)、rejected (已失败)。
- .then(onFulfilled, onRejected): 指定成功和失败状态的回调函数。可以链式调用 .then() 处理连续的异步操作。
- .catch(onRejected): 专门用于捕获错误(等同于 .then(null, onRejected))。
- .finally(onFinally): 无论成功或失败都会执行的回调。
- Promise.all([p1, p2, …]): 等待所有 Promise 都成功后才成功,返回包含所有结果的数组;有一个失败则整体失败。
- Promise.race([p1, p2, …]): 只要有一个 Promise 成功或失败,就立刻采用它的结果。
- async/await: ES2017 引入,是基于 Promise 的语法糖,让异步代码看起来更像同步代码,更易读写。
- async function: 在函数声明前加 async 关键字,表示该函数内部可以使用 await。async 函数总是返回一个 Promise。
- await expression: 只能用在 async 函数内部,用于等待一个 Promise 完成,并返回其解决(fulfilled)的值。如果 Promise 被拒绝(rejected),await 会抛出错误,可以用 try…catch 捕获。
9.1 小例子:使用 setTimeout 模拟异步,Promise 封装,async/await 调用
console.log("同步代码 1");// 1. 使用 setTimeout 模拟异步 (回调函数)
setTimeout(function() {console.log("异步操作 1 (setTimeout) 完成");// 如果有后续依赖操作,需要嵌套在这里... Callback HellsetTimeout(function() {console.log("异步操作 2 (嵌套) 完成");}, 500);
}, 1000); // 1000毫秒 = 1秒 后执行console.log("同步代码 2");// 2. 使用 Promise 封装异步操作
function simulateAsync(message, delay) {return new Promise((resolve, reject) => {// 模拟成功或失败const success = Math.random() > 0.3; // 70% 概率成功setTimeout(() => {if (success) {console.log(`异步操作 (${message}) 完成`);resolve(`结果: ${message} 成功`); // 成功时调用 resolve} else {console.error(`异步操作 (${message}) 失败`);reject(new Error(`错误: ${message} 失败`)); // 失败时调用 reject}}, delay);});
}// 使用 Promise 处理链式异步
simulateAsync("任务A", 1500).then(resultA => {console.log(resultA);return simulateAsync("任务B", 500); // 返回新的 Promise 实现链式调用}).then(resultB => {console.log(resultB);console.log("Promise 链执行完毕");}).catch(error => { // 捕获链中任何一个环节的错误console.error("Promise 链中发生错误:", error.message);}).finally(() => {console.log("Promise 链最终清理工作");});// 3. 使用 async/await 简化 Promise 调用
async function runAsyncTaskSequence() {console.log("Async/Await: 开始执行异步序列");try {const result1 = await simulateAsync("任务X", 800);console.log("Async/Await:", result1);const result2 = await simulateAsync("任务Y", 600);console.log("Async/Await:", result2);console.log("Async/Await: 序列执行完毕");return "序列成功完成"; // async 函数返回 Promise} catch (error) {console.error("Async/Await: 序列中发生错误:", error.message);throw error; // 可以选择重新抛出错误} finally {console.log("Async/Await: 序列最终清理");}
}// 调用 async 函数
runAsyncTaskSequence().then(finalResult => console.log("Async 函数最终结果:", finalResult)).catch(finalError => console.error("Async 函数最终捕获错误:", finalError.message));console.log("同步代码 3 (可能在异步完成前执行)");
运行后的效果
9.2 练习
创建三个模拟异步任务的函数(使用 setTimeout 和 Promise),让它们按顺序执行(任务 2 在任务 1 成功后执行,任务 3 在任务 2 成功后执行),并使用 async/await 来实现。
10 与服务器通信:Fetch API - 获取外部数据的"遥控器
现代网页很少是完全静态的,它们需要从服务器获取最新数据(如新闻、商品列表)或向服务器提交用户数据(如表单信息)。Fetch API 是浏览器内置的、基于 Promise 的现代网络请求接口,就像一个可以远程控制服务器(获取信息或发送指令)的遥控器。
- 基本用法: fetch(url) 默认发送 GET 请求。它返回一个 Promise,该 Promise resolve 为一个 Response 对象。
- Response 对象: 代表服务器的响应。你需要调用它的方法(如 .json() 或 .text())来再次返回一个 Promise,这个 Promise 最终 resolve 为实际的数据。
- 处理响应: 检查 response.ok (布尔值,表示 HTTP 状态码是否在 200-299 之间) 或 response.status 来判断请求是否成功。
- 发送数据 (POST 等): 需要在 fetch 的第二个参数 options 对象中指定 method, headers, body。
- method: ‘POST’, ‘PUT’, ‘DELETE’ 等。
- headers: 通常需要设置 ‘Content-Type’ 来告诉服务器你发送的数据格式 (如 ‘application/json’)。使用 new Headers() 创建。
- body: 要发送的数据。如果是 JSON,需要用 JSON.stringify() 转换成字符串。
10.1 小例子:从 JSONPlaceholder (一个免费的假 API 服务) 获取用户数据并展示
HTML (fetch.html):
<!DOCTYPE html>
<html lang="zh-CN">
<head> <meta charset="UTF-8"> <title>Fetch API 示例</title> </head>
<body><h1>用户列表</h1><button id="load-users-btn">加载用户数据</button><ul id="user-list"></ul><p id="loading-status"></p><script src="fetch_script.js"></script>
</body>
</html>
JavaScript (fetch_script.js):
const loadBtn = document.getElementById('load-users-btn');
const userList = document.getElementById('user-list');
const statusP = document.getElementById('loading-status');loadBtn.addEventListener('click', fetchUsers);async function fetchUsers() {statusP.textContent = '正在加载...';userList.innerHTML = ''; // 清空列表loadBtn.disabled = true; // 禁用按钮防止重复点击try {// 发送 GET 请求const response = await fetch('https://jsonplaceholder.typicode.com/users?_limit=5'); // 获取前 5 个用户console.log('Response Status:', response.status); // 打印状态码console.log('Response OK?', response.ok); // 打印是否成功if (!response.ok) {// 如果 HTTP 状态码不是 2xx,抛出错误throw new Error(`HTTP 错误! 状态码: ${response.status}`);}// 解析 JSON 数据 (response.json() 返回 Promise)const users = await response.json();statusP.textContent = '加载成功!';// 将用户数据渲染到页面users.forEach(user => {const listItem = document.createElement('li');listItem.textContent = `ID: ${user.id}, 姓名: ${user.name}, 邮箱: ${user.email}`;userList.appendChild(listItem);});} catch (error) {// 捕获网络错误或解析错误console.error('获取用户数据失败:', error);statusP.textContent = `加载失败: ${error.message}`;} finally {loadBtn.disabled = false; // 无论成功失败,重新启用按钮}
}// 示例:发送 POST 请求 (需要后端支持)
async function postDataExample() {const dataToSend = { title: 'foo', body: 'bar', userId: 1 };try {const response = await fetch('https://jsonplaceholder.typicode.com/posts', {method: 'POST',headers: {'Content-Type': 'application/json; charset=UTF-8',},body: JSON.stringify(dataToSend), // 将 JS 对象转为 JSON 字符串});if (!response.ok) {throw new Error(`HTTP error! status: ${response.status}`);}const result = await response.json(); // 解析返回的 JSONconsole.log('POST 请求成功,服务器返回:', result);} catch (error) {console.error('POST 请求失败:', error);}
}
// postDataExample(); // 可以取消注释来尝试发送 POST 请求
运行后的效果
10.2 练习:创建一个简单的天气查询应用
- HTML: 提供一个输入框让用户输入城市名,一个查询按钮,一个区域显示天气结果。
- JavaScript:
- 给按钮添加点击事件监听。
- 在事件处理函数中,获取用户输入的城市名。
- 使用 Fetch API 调用一个免费的天气 API (如 OpenWeatherMap - 可能需要注册获取 API Key,或者找一些无需 Key 的公共天气 API)。
- 将城市名作为参数拼接到 API URL 中。
- 处理 API 返回的 JSON 数据,提取需要的天气信息(如温度、天气描述、湿度等)。
- 将天气信息显示在页面指定区域。
- 添加适当的加载中和错误处理提示。
11 本地存储:浏览器的"小抽屉"
有时我们需要在用户关闭浏览器后仍然记住一些信息(如用户偏好、登录状态、未完成的表单),或者在单个会话期间临时存储数据。Web Storage API 提供了两个简单的机制:
- localStorage: 持久化存储。数据会一直保留,除非用户手动清除浏览器缓存或代码主动删除。同源(协议、域名、端口相同)的所有页面共享 localStorage 数据。容量通常为 5-10MB。
- sessionStorage: 会话级存储。数据只在当前浏览器标签页的生命周期内有效。关闭标签页或浏览器后,数据会被清除。不同标签页即使同源,sessionStorage 也不共享。容量与 localStorage 类似。
注意: 两者都只能存储字符串类型。存储对象或数组时,需要先用 JSON.stringify() 转换成 JSON 字符串,读取时再用 JSON.parse() 解析回来。它们是同步操作,不适合存储大量数据。
11.1 小例子:实现一个记住用户偏好(如主题颜色)的功能
HTML (storage.html):
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>本地存储示例</title><style>body.dark-theme { background-color: #333; color: white; }body.light-theme { background-color: #fff; color: black; }</style>
</head>
<body class="light-theme"> <h1>主题切换</h1><p>当前主题: <span id="theme-name">亮色</span></p><button id="toggle-theme-btn">切换主题</button><script src="storage_script.js"></script>
</body>
</html>
JavaScript (storage_script.js):
const toggleBtn = document.getElementById('toggle-theme-btn');
const themeNameSpan = document.getElementById('theme-name');
const body = document.body;// --- 应用存储的主题 ---
function applyTheme(theme) {if (theme === 'dark') {body.classList.remove('light-theme');body.classList.add('dark-theme');themeNameSpan.textContent = '暗色';} else {body.classList.remove('dark-theme');body.classList.add('light-theme');themeNameSpan.textContent = '亮色';}
}// --- 页面加载时读取 localStorage ---
// 使用 getItem 获取存储的主题,如果没有则默认为 'light'
let currentTheme = localStorage.getItem('theme') || 'light';
applyTheme(currentTheme);// --- 切换主题并存储 ---
toggleBtn.addEventListener('click', function() {// 切换主题逻辑if (currentTheme === 'light') {currentTheme = 'dark';} else {currentTheme = 'light';}// 应用新主题applyTheme(currentTheme);// 将新主题存储到 localStoragelocalStorage.setItem('theme', currentTheme);console.log(`主题已保存到 localStorage: ${currentTheme}`);// 示例:使用 sessionStorage (会话结束后清除)sessionStorage.setItem('lastAction', '切换主题');console.log('上次操作已存入 sessionStorage:', sessionStorage.getItem('lastAction'));
});// 清除示例 (通常不需要随便清除)
// localStorage.removeItem('theme');
// localStorage.clear(); // 清除所有 localStorage 数据
// sessionStorage.clear();
运行后的效果
11.2 练习
将你之前或现在实现的待办事项列表应用进行改进,将任务列表数据存储到 localStorage 中。
- 当添加、删除、修改任务状态时,更新 localStorage 中的数据(记得将数组转换为 JSON 字符串)。
- 当页面加载时,从 localStorage 读取数据(如果存在),解析 JSON 字符串,并根据这些数据重新渲染待办事项列表,这样即使用户刷新页面,任务也不会丢失。
12 ES6+ 新特性选讲:现代 JavaScript 的便捷写法
ECMAScript (ES) 是 JavaScript 的标准规范。ES6 (也叫 ES2015) 是一个重要的里程碑,引入了大量新特性,让 JavaScript 写起来更方便、更强大。后续版本 (ES7/ES2016, ES8/ES2017, …) 也在不断添加新功能。了解并使用这些新特性可以提高开发效率和代码质量。
- 模板字符串: 使用反引号 ` 定义字符串,可以方便地嵌入变量 (${variable}) 和换行。
- 解构赋值: 从数组或对象中提取值并赋给变量,语法更简洁。
- 展开运算符: 将数组或对象"展开"成独立的元素或属性,常用于合并数组/对象、传递函数参数。
- 剩余参数: 在函数定义时,将多余的参数收集到一个数组中。
- 模块化: 将代码分割成独立的模块(文件),通过 export 导出需要暴露的部分,通过 import 导入其他模块的功能。这是构建大型应用的基础,有助于代码组织和复用。(浏览器原生支持需要
<script type="module">
,或者通过 Webpack/Rollup 等构建工具处理)。
12.1 小例子:演示各种新特性的用法
// 1. 模板字符串
const userName = "Alice";
const userAge = 28;
const greeting = `你好,我是 ${userName},
我今年 ${userAge} 岁了。`; // 支持换行和变量嵌入
console.log(greeting);// 2. 解构赋值
// 数组解构
const numbers = [10, 20, 30];
const [first, second] = numbers;
console.log(first, second); // 输出: 10 20// 对象解构 (变量名需与属性名一致,或使用别名)
const person = { name: "Bob", city: "New York" };
const { name, city: personCity } = person; // city 重命名为 personCity
console.log(name, personCity); // 输出: Bob New York// 3. 展开运算符
// 合并数组
const arr1 = [1, 2];
const arr2 = [3, 4];
const combinedArr = [...arr1, 0, ...arr2, 5];
console.log(combinedArr); // 输出: [1, 2, 0, 3, 4, 5]// 合并对象 (ES2018)
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 }; // 相同属性会被后面的覆盖
const combinedObj = { ...obj1, ...obj2, d: 5 };
console.log(combinedObj); // 输出: { a: 1, b: 3, c: 4, d: 5 }// 函数参数传递
function sumNumbers(x, y, z) { return x + y + z; }
const numsToSum = [1, 2, 3];
console.log(sumNumbers(...numsToSum)); // 输出: 6// 4. 剩余参数
function logArgs(firstArg, ...restArgs) { // restArgs 是一个包含剩余参数的数组console.log("第一个参数:", firstArg);console.log("剩余参数:", restArgs);
}
logArgs('a', 'b', 'c', 'd');
// 输出:
// 第一个参数: a
// 剩余参数: [ 'b', 'c', 'd' ]// 5. 模块化 (基本概念 - 需要特定环境或构建工具)
// --- utils.js ---
// export const PI = 3.14;
// export function double(n) { return n * 2; }// --- main.js ---
// import { PI, double } from './utils.js';
// console.log(PI);
// console.log(double(5));
运行后的效果
12.2 练习
尝试使用 ES6+ 的语法(如箭头函数、模板字符串、解构赋值、let/const)来重构你之前编写的一些练习代码,体会语法的简洁性。
阶段项目 3:打造你的第一个交互式应用
理论学习完毕,是时候动手实践,将 HTML, CSS, JavaScript 结合起来,创建一个真正能与用户交互的前端应用了!
选择一个项目进行开发:
-
待办事项列表 (Todo List) 应用:
- 核心功能: 添加新任务、将任务标记为已完成/未完成、删除任务、将任务列表存储到 localStorage 实现数据持久化。
- 涉及技术: DOM 操作(创建列表项、修改样式)、事件处理(按钮点击、复选框状态改变)、数组操作、本地存储。
- 挑战: 添加编辑任务功能、按完成状态过滤任务、设置任务优先级。
-
电影/图书搜索应用:
- 核心功能: 提供输入框让用户搜索电影或图书名称,调用公共 API (如 The Movie Database (TMDb), Open Library API) 获取数据,将搜索结果(如海报、标题、简介)展示在页面上。
- 涉及技术: DOM 操作(展示结果列表)、事件处理(搜索按钮点击、回车键搜索)、Fetch API、处理 JSON 数据、异步编程 (async/await)。
- 挑战: 实现分页加载更多结果、点击结果项显示更详细信息、处理 API 请求错误。
-
简易计算器:
- 核心功能: 实现一个基本的计算器界面,能够进行加、减、乘、除运算。
- 涉及技术: DOM 操作(获取按钮点击、更新显示屏)、事件处理(数字和运算符按钮点击)、基本的算术逻辑处理、状态管理(记录当前数字、运算符、运算结果)。
- 挑战: 实现更复杂的运算(如百分比、平方根)、处理连续运算、处理错误输入(如除以零)。
要求:
- 项目包含清晰的 HTML 结构、合适的 CSS 样式和功能性的 JavaScript 代码。
- JavaScript 代码应组织良好(例如,使用函数封装不同功能),并添加必要的注释。
- 优先使用 addEventListener 绑定事件。
- 优先使用 let/const 声明变量。
- 考虑代码健壮性,进行适当的错误处理(如 API 请求失败、用户输入无效)。
- (可选)尝试使用 ES6+ 新特性让代码更简洁。
这个阶段项目将是你综合运用前端三剑客(HTML, CSS, JS)能力的最好锻炼。放手去做吧!
结语
恭喜你,完成了 JavaScript 客户端部分的学习!你已经掌握了编程的基础语法、流程控制、数据结构(数组和对象),学会了如何通过 DOM 和事件让网页动起来、响应用户操作,了解了如何通过异步编程和 Fetch API 与服务器沟通,甚至接触了本地存储和现代 JS 特性。
现在,你的"房子"不仅有了结构和外观,还通上了"电",拥有了"智能系统",能够完成各种任务了!
JavaScript 的世界远不止于此,还有更多高级概念(如面向对象编程深入、设计模式、函数式编程、更复杂的 this 指向、性能优化等)和庞大的生态系统(各种框架、库、工具)等待你去探索。但你现在打下的基础,足以让你开始构建功能丰富的前端应用,并为后续学习 Node.js (后端) 和 React/Vue/Angular (前端框架) 做好准备。
最重要的仍然是:持续练习,不断实践! 尝试去实现你自己的想法,或者参与到开源项目中,在实践中巩固和深化你的 JavaScript 技能。
接下来,我们将继续学习javascript的强化版typescript
相关文章:
第三部分:赋予网页灵魂 —— JavaScript(下)
目录 7 DOM 操作:控制网页的"智能面板7.1 小例子:点击按钮时改变段落文字,根据用户输入改变图片7.2 练习:实现一个简单的 Tab 切换效果 8 事件处理:响应用户的"指令"8.1 小例子:实现点击按钮…...
(02)Redis 的订阅发布Pub/Sub
我们为了自己实现一个MQ功能,就要深入底层挖掘现有开源产品的实现过程。 Redis 发布订阅底层结构解析 Redis 不存储消息,仅作为“实时中转”;只有订阅者在线时才能收到消息;消息是广播给所有订阅此频道的客户端。 1. 核心数据结…...
JavaScript性能优化实战:从基础到高级的全面指南
作为前端开发者,掌握JavaScript性能优化是提升用户体验和职业竞争力的关键。本文将系统性地介绍JavaScript性能优化的各个方面,从基础概念到高级技巧,帮助你编写更高效的代码。 一、JavaScript性能优化基础概念 1.1 什么是JavaScript性能优…...
CertiK创始人顾荣辉出席Unchained Summit,探讨Web3.0安全与合规路径
4月28日,CertiK联合创始人、哥伦比亚大学教授顾荣辉出席迪拜Unchained Summit峰会并发表主题演讲,探讨Web3.0在创新与安全间的平衡,引发网易科技、中国财经时报、腾讯网、新浪财经等多家知名媒体的关注和报道。 作为迪拜最重要的峰会之一&am…...
企业出海降本:如何将应用从 AWS EC2 快速无缝迁移至DigitalOcean Droplet
企业出海已经成为目前最热门的趋势。然而不论你是做跨境电商,还是短剧出海,或处于最热门的AI 赛道,你都需要使用海外的云主机或GPU云服务。海外一线的云服务平台尽管覆盖区域广泛,但是往往费用成本较高。所以降本始终是企业出海关…...
java练习2
package a01_第一次练习.a02_计算输入天数;import java.text.ParseException; import java.time.Duration; import java.time.LocalDateTime; import java.util.Scanner;public class Test {public static void main(String[] args) throws ParseException {//当前时间LocalDat…...
PDM是什么?PDM有什么用?怎么选PDM?2025制造PDM/PLM系统盘点(4000字)
(文章来自CRDE PDM研究中心) 摘要 PDM是制造企业产品数据管理的核心工具,并逐渐发展出了PLM、云PLM等新形态,在功能增加的同时成本也有大幅降低,已经成为企业研发管理的最优选择,并逐渐被有产品创新需求的…...
TiDB 可观测性最佳实践
TiDB 介绍 TiDB,由 PingCAP 公司自主研发的开源分布式关系型数据库,是一款创新的 HTAP 数据库产品,它融合了在线事务处理(OLTP)和在线分析处理(OLAP)的能力,支持水平扩容和缩容&…...
8.idea创建maven项目(使用Log4j日志记录框架+Log4j 介绍)
8.idea创建maven项目(使用Log4j日志记录框架Log4j 介绍) 在 IntelliJ IDEA 的 Maven 项目中引入了 Log4j,并配置了日志同时输出到控制台和文件。 Log4j 提供了灵活的日志配置选项,可以根据项目需求调整日志级别、输出目标和格式。 1. 创建 Maven 项目 …...
Linux 命名管道+日志
一、命名管道 1.1 进程通信的前提:先让不同进程看到同一份资源; 1.2 如何确保两个进程打开的是同一个文件:同路径下同一文件名; 1.3 命名管道:通过路径文件名确保这个份资源的唯一性; 1.4 接口…...
c/c++之信号处理<signal.h>
该库提供了一组用于处理信号的函数和宏。信号是由操作系统或程序本身生成的一种异步事件,用于通知某些事件的发生,例如非法操作、用户中断等。 信号 信号是进程之间通信的重要方式。信号是一种异步通知机制,由操作系统或其他进程发送给当前进…...
基于PyTorch的图像分类特征提取与模型训练文档
概述 本代码实现了一个基于PyTorch的图像特征提取与分类模型训练流程。核心功能包括: 使用预训练ResNet18模型进行图像特征提取 将提取的特征保存为标准化格式 基于提取的特征训练分类模型 代码结构详解 1. 库导入 import torch import torch.nn as nn import…...
DDoS vs CC攻击:哪种对服务器威胁更大?
引言 DDoS(分布式拒绝服务)与CC(Challenge Collapsar)攻击是两种常见的网络攻击手段,均会导致服务器资源耗尽、服务中断。但它们的攻击原理、防御难度及危害程度存在显著差异。本文将从技术原理、攻击效果、防御成本等…...
Weiss Robotics的WPG与WSG系列紧凑型机器人夹爪,精准、灵活、高效
在自动化和智能制造领域,Weiss Robotics 以其创新的智能抓取系统而受到广泛认可。本文将重点介绍 Weiss Robotics 的两大产品系列:WPG 系列和 WSG 系列。这些产品系列凭借其先进的技术特性,为各行各业的自动化需求提供了高效、灵活的解决方案…...
引力透镜效应添加光线弯曲程度可视化层的MATLAB代码
物理实现要点: 雅可比矩阵计算 通过数值梯度计算偏转场的空间导数: 放大率μ反映像的亮度增强倍数 动态范围处理 使用对数压缩μ值范围:μ_vis log10(1μ),避免高放大率区域饱和 多物理量联合显示 红圈标注爱因…...
OpenCV 图形API(71)图像与通道拼接函数-----从图像(GMat)中裁剪出一个矩形区域的操作函数 crop()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 裁剪一个2D矩阵。 该函数根据给定的 cv::Rect 裁剪矩阵。 输出矩阵必须与输入矩阵具有相同的深度,大小由给定的矩形大小指定。 注意…...
tarjan缩点+强联通分量
【模板】缩点https://www.luogu.com.cn/problem/P3387 首先我们要理解这道题为什么要用缩点 题目说的是有向图,如果无环的话就可以用DP来解决了 由于可以走重复的点,所以一个环上的点可以看成是一个点,它的点权就等于该环上所有点的点权之…...
tornado_登录页面(案例)
目录 1.基础知识编辑 2.脚手架(模版) 3.登录流程图(processon) 4.登录表单 4.1后(返回值)任何值:username/password (4.1.1)app.py (4.1.2ÿ…...
Educational Codeforces Round 178 (Rated for Div. 2)
ABC 略 D n个数互质,即n个数都是质数,预处理前4e5个质数,原排列从大到小排序求前缀和,质数求前缀和,从后往前循环第一个前者前缀和能大于等于后者就是留下的i个数。 #include<bits/stdc.h> #define int long …...
从Transformer原理角度来看,prompt设置输出字数限制会生效的原因
从Transformer原理角度来看,prompt设置输出字数限制会生效的原因 1. 生成过程的控制 Transformer在生成文本时是一个自回归的过程,它从起始标记开始,逐个生成下一个单词或标记。在这个过程中,模型会根据已经生成的文本和自身的参数来预测下一个可能的标记的概率分布。当设…...
WEB漏洞--CSRF及SSRF案例
CSRF案例 原理 检测是否存在CSRF(跨站请求伪造)漏洞 1. 手动测试 构造恶意请求:创建一个恶意网页或电子邮件,包含指向目标网站的恶意请求(如更改密码或发送消息的URL)。诱使用户访问该恶意请求,…...
Android开发——实现一个计算器
目录 代码讲解 activity_calculator.xmld代码讲解 1. 根布局(LinearLayout) 2. 显示区域(TextView) 3. 按钮区域(GridLayout) 4. 清除和删除按钮 5. 数字和操作符按钮 6. 其他行的按钮 7. 最后一行…...
23G显存可以跑多大尺寸的Qwen3?
随着阿里Qwen3系列大模型的发布,开发者们对如何在有限显存下部署不同尺寸的模型尤为关注。本文基于Qwen3的技术特性和实际测试数据,探讨在23G显存环境下可运行的模型选择及优化策略。 不过由于咱财力有限,只有一张A100,还不是空的,目前只有23G的显存。 那么这23G显存能跑…...
网络通讯【QTcpServer、QTcpSocket、QAbstractSocket】
目录 QTcpServer class简单描述成员函数和信号 QTcpSocket Class详细描述成员函数和信号 QAbstractSocket Class详细描述成员函数和信号成员函数说明文档 QT实现服务器和客户端通讯服务器端:通讯流程原代码 客户端通信流程原代码 QTcpServer class header: #includ…...
std::functional 类是干什么用的?
author: hjjdebug date: 2025年 04月 29日 星期二 15:54:53 CST description: std::functional 类是干什么用的? 文章目录 1.functional 对象的概念2.functional 对象存在的意义2.1 为什么要把接口再封一层?2.2 c中函数参数可以不用回调函数, 而改用func…...
人事管理系统6
模糊查询 DepartmentMapper.xml : <select id"findDepartmentListByName" resultMap"BaseResultMap"> select <include refid"Base_Column_List"/> from department where dname like %${dname}% /*where dname like con…...
链表的中间节点
这题需要用到快慢指针的思想,快指针叫fast,慢指针是slow,快指针每次往后移两个节点,slow只移动一个节点,这样子fast的速度是slow的两遍,当fast遍历完链表,slow才遍历一半,正好就在中…...
【学习学学】城市群与都市圈是什么?怎么
城市群与都市圈是什么?怎么发展 这些年城市群,都市圈的概念被逐渐提了出来,也是未来我国即将重点发展的对象之一。 因此,身边有一些朋友在问,城市群与都市圈有什么区别?哪个对城市发展影响更大?…...
【深度学习与大模型基础】第14章-分类任务与经典分类算法
Part 1:什么是分类任务? 1.1 分类就是“贴标签” 想象你有一堆水果,有苹果🍎、橘子🍊、香蕉🍌,你的任务是让机器学会自动判断一个新水果属于哪一类——这就是分类(Classification&…...
第十五章:预训练大语言模型
目录 15.1 数据准备 15.1.1 数据预处理 15.1.2 数据调度 15.2 模型架构 15.2.1 主流架构 一、编码器架构(Encoder-only,以 BERT 为代表) 核心特点: 代表模型:BERT、RoBERTa、ALBERT 典型应用: 二…...
万象生鲜配送系统代码2025年4月29日更新日志
亲爱的用户:万象生鲜配送系统始终致力于为您提供更优质、高效的服务体验。经过我们技术团队的不懈努力,万象生鲜配送系统在 2025 年 4 月迎来了一次重大更新。本次更新涵盖了多个方面,包括功能新增、性能优化以及问题修复,旨在进一…...
Mac 创建QT按钮以及一些操作
在创建QT项目好 后我们打开mainwindow.cpp,下面所示的代码都是在这个cpp文件里面因为它是窗口的入口函数 #include "mainwindow.h" #include "ui_mainwindow.h" #include<QPushButton>//按钮的头文件MainWindow::MainWindow(QWidget *pa…...
C++学习之shell高级和正则表达式
目录 1.正则表达式 2.C中使用正则 3.复习 4.sort命令 5.uniq命令 6.wc命令 7.grep命令 8.find命令 9.xargs命令 10.sed命令 11.awk命令 12.crontab 1.正则表达式 1 管道 使用| 将多个命令拼接在一起 原理,就是将前一个命令的标准输出作为后一个…...
SpringBoot获取用户信息常见问题(密码屏蔽、驼峰命名和下划线命名的自动转换)
文章目录 一、不返回password字段二、返回的createTime和updateTime为空原因解决:开启驼峰命名和下划线命名的自动转换 一、不返回password字段 在字段上面添加JsonIgnore注解即可 JsonIgnore // 在把对象序列化成json字符串时,忽略该字段 private Str…...
优化PCB Via Stub系列(2) – 运用U-Turn Via设计破解阻抗匹配困境,改善信号完整性
在PCB设计中,往往透过制程改善如背钻、盲孔或埋孔,来消除不必要的Via stub,可是多出来的制造成本会压低产品的毛利,可是又有什么办法可以不透过制程改善以缩小Via stub带来的SI困扰呢? 本周我们来讲从Layout布局的角度…...
飞鸟游戏模拟器 1.0.3 | 完全免费无广告,内置大量经典童年游戏,重温美好回忆
飞鸟游戏模拟器是一款专为安卓用户设计的免费游戏模拟器,内置了大量经典的童年游戏。该模拟器拥有丰富的游戏资源,目前已有约20,000款游戏,包括多种类型如冒险、动作、角色扮演等。用户可以直接搜索查找想要玩的游戏进行下载并启动。游戏库中…...
钓鱼网页散播银狐木马,远控后门威胁终端安全
在当今网络环境下,许多人都有通过搜索引擎下载应用程序的习惯,虽然这种方式简单又迅速,但这也可能被不法分子所利用,通过设置钓鱼网站来欺骗用户。这些钓鱼网站可能会通过各种方式吸引用户点击,从而进行病毒的传播&…...
一文读懂 JavaScript 中的深浅拷贝
在 JavaScript 编程里,深浅拷贝是处理数据时极为关键的概念。理解它们的差异,能帮我们规避许多数据操作上的 “陷阱”。今天,咱们就借助简单的 “abc” 相关示例,深入探索深浅拷贝的奥秘,并且通过在浏览器控制台输出结…...
5G技术在工业4.0中的应用:连接未来,驱动智能制造
5G技术在工业4.0中的应用:连接未来,驱动智能制造 引言 工业4.0,作为第四次工业革命的核心,已经在全球范围内掀起了智能制造的浪潮。它不仅包括了自动化生产、智能物流、云计算和大数据的应用,更是融合了互联网、物联网…...
驱动开发硬核特训 · Day 25 (附加篇):从设备树到驱动——深入理解Linux时钟子系统的实战链路
一、前言 在嵌入式Linux开发中,无论是CPU、外设控制器,还是简单的GPIO扩展器,大多数硬件模块都离不开时钟信号的支撑。 时钟子系统(Clock Subsystem),作为Linux内核中基础设施的一部分,为设备…...
数据结构---单链表的增删查改
前言: 经过了几个月的漫长岁月,回头时年迈的小编发现,数据结构的内容还没有写博客,于是小编赶紧停下手头的活动,补上博客以洗清身上的罪孽 目录 前言 概念: 单链表的结构 我们设定一个哨兵位头节点给链…...
【codeforces 2104D,E】欧拉筛,字符串上dp
【codeforces 2104D,E】欧拉筛,字符串上dp Problem - D - Codeforces 题意: 给定一个长度为 n n n的数组 a 1 , a 2 , . . . , a n a_1, a_2, ... , a_n a1,a2,...,an,其中 2 ≤ a i ≤ 1 0 9 2 \leq a_i \leq 10^9 2≤…...
UEC++第15天|番茄插件、实现跳跃、实现背景运动
这是flyBird的第二天,做了一些简单的功能,明天继续更新 vs的番茄插件 在visual stdudio里使用可以帮助代码补全,这一篇博客写的不错,大家可以参考一下。VS2019 安装番茄助手(Visual Assist x 插件)攻略_vs…...
论文笔记-基于多层感知器(MLP)的多变量桥式起重机自适应安全制动与距离预测
《IET Cyber-Systems and Robotics》出版山东大学 Tenglong Zhang 和 Guoliang Liu 团队的研究成果,文章题为“Adaptive Safe Braking and Distance Prediction for Overhead Cranes With Multivariation Using MLP”。 摘要 桥式起重机的紧急制动及其制动距离预测是…...
[论文阅读]Adversarial Semantic Collisions
Adversarial Semantic Collisions Adversarial Semantic Collisions - ACL Anthology Proceedings of the 2020 Conference on Empirical Methods in Natural Language Processing (EMNLP) 对抗样本是相似的输入但是产生不同的模型输出,而语义冲突是对抗样本的逆…...
Redis Sentinel 和 Redis Cluster 各自的原理、优缺点及适用场景是什么?
我们来详细分析下 Redis Sentinel (哨兵) 和 Redis Cluster (集群) 这两种方案的原理和使用场景。 Redis Sentinel (哨兵) 原理: Sentinel 本身是一个或一组独立于 Redis 数据节点的进程。它的核心职责是监控一个 Redis 主从复制 (Master-Slave) 架构。多个 Sentinel 进程协同…...
面向人工智能、量子科技、人形机器人等产业,山东启动制造业创新中心培育认定
从山东省工业和信息化厅了解到,2025年山东省制造业创新中心培育和认定已启动,重点面向全省传统优势产业、新兴产业及未来产业等领域,鼓励具备条件的龙头企业牵头创建省制造业创新中心。 今年,山东重点面向全省冶金、化工、轻工、…...
无锡哲讯科技:SAP财务系统——赋能企业智慧财务管理
数字化时代,财务管理的新挑战 在全球化竞争和数字经济快速发展的背景下,企业财务管理正面临前所未有的挑战。传统的财务核算方式效率低下、数据孤岛严重、决策滞后,难以满足现代企业高效运营的需求。如何实现财务数据的实时整合࿱…...
Linux命令使用记录(自用)
阿里开源镜像站:https://developer.aliyun.com/mirror/?spma2c6h.13651102.0.0.6c2a1b11I9pmUD&serviceTypemirror&tag top命令 top [选项] -p 只显示某个进程的信息 -d 设置刷新时间,默认是5s -c 显示产生进程的完整命令,默认是进程…...
Spring Security 的 CSRF 防护机制
CSRF:跨站请求伪造(Cross-Site Request Forgery) Spring Security 中的 .csrf() 是用来开启或配置这种保护机制,防止恶意网站“冒充用户”向你的网站发起请求。 一、CSRF 攻击原理简要 CSRF 的典型攻击场景如下: 用…...