JavaScript 基础入门与核心语法详解
JavaScript 基础入门教程涵盖语言特点、脚本分类、DOM 操作、事件处理、数组与对象方法以及客户端数据存储。内容包含 HelloWorld、弹窗交互、表单校验、时钟显示等实战案例,详细讲解 ES5/ES6 函数定义、闭包、变量声明及类型转换,并提供 Cookie、SessionStorage 和 LocalStorage 的封装与应用示例,适合初学者系统掌握前端开发核心技能。

JavaScript 基础入门教程涵盖语言特点、脚本分类、DOM 操作、事件处理、数组与对象方法以及客户端数据存储。内容包含 HelloWorld、弹窗交互、表单校验、时钟显示等实战案例,详细讲解 ES5/ES6 函数定义、闭包、变量声明及类型转换,并提供 Cookie、SessionStorage 和 LocalStorage 的封装与应用示例,适合初学者系统掌握前端开发核心技能。

心法:本章使用 Maven 父子结构项目进行练习。
练习项目结构如下:
|_ v2-9-web-javascript |_ 8080test
武技:搭建练习项目结构。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>基础入门</title>
</head>
<body>
<p>Hello JavaScript!</p>
</body>
</html>

心法:JavaScript6,简称 JS6,是一门用于与浏览器及其内容等几乎所有方面的交互的编程语言,在 1995 年由 NetScape 公司在网景导航者浏览器上首次设计实现而成,原名 LiveScript,后因与 Sun 合作更名为 JavaScript 并被纳入 ECMA-262 标准(ECMA 是欧洲计算机制造联合会,旨在建立统一的电脑操作格式标准),故 JS 也可被称为 ES。
JS 语言特点如下:
| 语言特点 | 描述 |
|---|---|
| 解释型 | 程序的运行过程中逐行进行解释,不需要编译 |
| 面向对象 | JS 不仅能使用现有的对象,还可以创建新的对象 |
| 弱类型 | JS 拥有数据类型但不需要声明强调 |
| 事件驱动 | JS 采用事件驱动进行编程,可以对用户的操作做出实时的响应 |
| 跨平台性 | JS 不依赖于操作系统,仅依赖于浏览器,浏览器兼容性良好 |
JS 组成结构:JS 除了 ECMA 核心引擎之外,还分为 DOM 和 BOM 两种结构:
| 对比项 | DOM 文档对象模型 | BOM 浏览器对象模型 |
|---|---|---|
| 赋予 JS 什么能力 | 操作 DOM 树的能力 | 操作浏览器的能力 |
| 示例 | 得到元素,删除元素等 | 弹窗,关窗,剪切板等 |
| 浏览器兼容性 | 部分兼容 | 完全不兼容 |
| 关键字 | document,不能省略 | window,可省略 |
JS 脚本分类:根据 JS 代码位置分类如下:
| JS 分类 | 优先级 | 使用方式 | 特点 |
|---|---|---|---|
| 内嵌 | 最高 | 直接在某标签上使用 onclick 等属性挂载 JS 事件 | 代码耦合度极高,仅临时调试使用,正式开发不推荐 |
| 内部 | 中等 | 在页体末尾的 <script></script> 中开发多个 <script></script> 区域会按编写顺序依次执行 | 脚本与页面绑定,适合学习或单页面小场景 |
| 外部 | 最低 | 在页体末尾使用 <script src="JS 文件路径"> 引入 | 开发场景下推荐使用 |
武技:测试 JS 脚本分类。
<!-- 内嵌 JS 脚本 --><button type="button" onclick="alert('内嵌 JS 脚本')">测试内嵌 JS 脚本</button><!-- 外部 JS 脚本文件 --><script src="../script/基础 - 脚本分类.js"></script><!-- 内部 JS 脚本 --><script>alert('内部:页面加载完毕!');</script>
alert('外部:页面加载完毕!');
心法:【案例 01】- 编写 JS 版的 HelloWorld。
案例需求:点击【打印】按钮,在浏览器的控制台输出'hello world'以及当前浏览器的相关信息。
案例知识:
| 案例相关知识点 | 简述 | 说明 |
|---|---|---|
onload = () => {} | 加载函数 | 在页面全部元素(包括 DOM,图片,视频等)加载完毕后自动执行 多个加载函数会发生覆盖,尽量不要这么干 |
console.log('', '') | 输出语句 | 多值输出使用逗号分隔即可,浏览器控制台表现为空格分隔 |
document.querySelector('C3 选择器') | 单抓元素 | 从 DOM 树中根据 CSS 选择器抓取单个元素 返回值一律使用 let 接收 抓取失败(如元素不存在)时返回 null |
元素.onclick = () => {} | 点击事件 | 为元素挂载点击事件 |
武技:开发 view/入门案例/HelloWorld.html 页面,并编写【案例 01】代码。
结构:
<button id="btn" type="button">打印</button>
脚本:
// 加载函数:页面加载完毕后执行
onload = () => {
// 抓取 DOM 元素
let btn = document.querySelector('#btn');
// 挂载点击事件
btn.onclick = () => {
// 打印内容在浏览器 F12 可查,多值输出使用逗号分隔即可
console.log('hello', 'world');
// 打印浏览器信息
console.log(window.navigator.userAgent);
};
};
效果图:
心法:【案例 02】- 页面相关操作。
案例需求:
案例知识:
| 案例相关知识点 | 简述 | 说明 |
|---|---|---|
open('页面 URL') | 当前窗口 | 在当前窗口展示指定的 URL 页面 |
open('页面 URL', '_blank') | 新窗口指定页面 | 开启一个新窗口,展示指定的 URL 页面,返回新窗口对象 |
open('about:blank', '_blank') | 新窗口空白页面 | 开启一个新窗口,展示空白的页面,返回新窗口对象 |
document.write('HTML 内容') | 文档写入 HTML | 在整个文档上写入指定的 HTML 内容 |
location.href = '' | 跳转新页面 | 支持网络 URI 地址或本地资源地址 |
close() | 关闭新窗口 | 仅支持关闭被 open() 打开的页面关闭最后一个页面时浏览器也会退出 |
top.close() | 关闭父窗口 | 在子页面中关闭父页面 |
武技:开发 view/入门案例/页面相关操作.html 页面,并编写【案例 02】代码。
结构:
<button id="openBtn" type="button">打开新页面</button>
<button id="jumpBtn" type="button">跳转新页面</button>
脚本:
onload = () => {
// 抓取 DOM 元素
let openBtn = document.querySelector('#openBtn');
let jumpBtn = document.querySelector('#jumpBtn');
// 挂点击事件:开启新窗口并写入 HTML 内容
openBtn.onclick = () => {
// 打开一个空白窗口,_blank 表示在新页面打开,返回新页面对象
let newWindow = open('about:blank', '_blank');
// 在新页面上编写 `<h1>HelloWorld</h1>` 内容
newWindow.document.write('<h1>HelloWorld</h1>');
};
// 挂点击事件:跳入百度页面
jumpBtn.onclick = () => {
location.href = 'https://www.baidu.com';
};
};
效果图:
心法:【案例 03】- 三种小弹窗。
案例需求:
案例知识:
| 案例相关知识点 | 简述 | 说明 |
|---|---|---|
alert('弹窗内容') | 普通弹窗 | 仅包含一个确定按钮 |
confrim('弹窗内容') | 确认弹窗 | 包含确定按钮和取消按钮 点击确定:返回 true 点击取消:返回 false |
prompt('弹窗内容', '默认值') | 输入弹窗 | 包含确定按钮,取消按钮和一个输入框 点击确定:返回用户输入的内容(无任何输入时返回空串) 点击取消:返回 null |
武技:开发 view/入门案例/三种小型弹窗.html 页面,并编写【案例 03】代码。
结构:
<button id="alertBtn" type="button">普通小弹窗</button>
<button id="confirmBtn" type="button">确认小弹窗</button>
<button id="promptBtn" type="button">输入小弹窗</button>
脚本:
onload = () => {
// 抓取 DOM 元素
let alertBtn = document.querySelector('#alertBtn');
let confirmBtn = document.querySelector('#confirmBtn');
let promptBtn = document.querySelector('#promptBtn');
// 挂点击事件
alertBtn.onclick = () => {
alert('你好');
};
confirmBtn.onclick = () => {
let result = confirm('即将删除,确定吗?');
console.log('响应:', result);
};
promptBtn.onclick = () => {
let result = prompt('请输入密码阅读');
console.log('响应:', result);
};
};
效果图:
心法:【案例 04】- 记住密码提示
案例需求:
案例知识:
| 案例相关知识点 | 简述 | 说明 |
|---|---|---|
元素.innerText = '' | 文本内容 | 设置元素的纯文本内容 |
元素.innerHTML = '' | HTML 内容 | 设置元素的 HTML 文本内容 |
元素.style['样式'] = '' | 元素样式 | 设置元素的 CSS 样式 |
元素.onmouseover = () => {} | 移入事件 | 当鼠标移入该元素时触发 |
元素.onmouseout = () => {} | 移出事件 | 当鼠标移出该元素时触发 |
武技:开发 view/入门案例/鼠标经过提示.html 页面,并编写【案例 04】代码。
结构:
<input id="cbx" type="checkbox"/><label for="cbx">记住密码</label>
<mark id="tips"></mark>
脚本:
onload = () => {
// 抓取 DOM 元素
let cbx = document.querySelector('#cbx');
let tips = document.querySelector('#tips');
// 开发敏感提示的内容,并进行初始隐藏
tips.innerHTML = '<strong>请不要在公共场合勾选此项!</strong>';
tips.style['display'] = 'none';
// 鼠标移入 cbx 的时候触发
cbx.onmouseover = () => tips.style['display'] = 'inline';
// 鼠标移出 cbx 的时候触发
cbx.onmouseout = () => tips.style['display'] = 'none';
};
效果图:
心法:【案例 05】- 鼠标经过高亮。
案例需求:
案例知识:
| 案例相关知识点 | 说明 |
|---|---|
document.querySelectorAll('C3 选择器') | 从 DOM 树 中,根据 CSS 选择器抓取元素集合 元素不存在时返回 null |
元素.querySelectorAll('C3 选择器') | 从 指定元素内部 根据 CSS 选择器抓取元素集合 元素不存在时返回 null |
武技:开发 view/入门案例/鼠标经过高亮.html 页面,并编写【案例 05】代码。
结构:
<ul id="list01">
<li class="item">在我心上用力的开一枪</li>
<li class="item">让一切归零在这声巨响</li>
<li class="item">如果爱是说什么都不能忘</li>
<li class="item">我不挣扎</li>
<li class="item">也许我也没差</li>
</ul>
<ul id="list02">
<li class="item">在我心上用力的开一枪</li>
<li class="item">让一切归零在这声巨响</li>
<li class="item">如果爱是说什么都不能忘</li>
<li class="item">我不挣扎</li>
<li class="item">也许我也没差</li>
</ul>
脚本:
onload = () => {
// 抓取 DOM 元素
let list01 = document.querySelector('#list01');
let items = list01.querySelectorAll('.item');
// 遍历 DOM 集合
for (let i = 0, j = items.length; i < j; i++) {
// 对每一个 li 元素挂载鼠标移入事件和鼠标移出事件
items[i].onmouseover = () => items[i].style['background-color'] = 'red';
items[i].onmouseout = () => items[i].style['background-color'] = '';
}
};
效果图:
心法:【案例 06】- 全选/反选功能。
案例需求:
案例知识:
| 案例相关知识点 | 简述 | 说明 |
|---|---|---|
元素.checked | 确认选中 | 元素被选中时返回 true,否则返回 false |
武技:开发 view/入门案例/全选反选按钮.html 页面,并编写【案例 06】代码。
结构:
<input id="allCbx" type="checkbox"><label for="allCbx">全选</label>
<input id="revCbx" type="checkbox"><label for="revCbx">反选</label>
<hr>
<input id="cbx01" name="user" type="checkbox"><label for="cbx01">赵四</label>
<input id="cbx02" name="user" type="checkbox"><label for="cbx02">刘能</label>
<input id="cbx03" name="user" type="checkbox"><label for="cbx03">广坤</label>
<input id="cbx04" name="user" type="checkbox"><label for="cbx04">王云</label>
<input id="cbx05" name="user" type="checkbox"><label for="cbx05">飞机</label>
脚本:
onload = () => {
// 抓取 DOM 元素
let users = document.querySelectorAll('input[name="user"]');
let allCbx = document.querySelector('#all-cbx');
let revCbx = document.querySelector('#rev-cbx');
// 点击全选多选框,可以将所有内容选中,再次点击可以取消全选
allCbx.onclick = () => {
for (let i = 0, j = users.length; i < j; i++) {
users[i].checked = allCbx.checked;
}
};
// 点击反选多选框,可以将所有内容反选,再次点击再次反选
revCbx.onclick = () => {
for (let i = 0, j = users.length; i < j; i++) {
users[i].checked = !users[i].checked;
}
};
};
效果图:
心法:【案例 07】- 批量删除提示。
案例需求:当点击【批量删除】按钮时,模拟删除全部被选中的用户:
案例知识:
| 案例相关知识点 | 简述 | 说明 |
|---|---|---|
return | 结束函数 | 该关键字用于结束并退出其所在的函数 |
${xx} | 变量插值 | ES6 支持使用反引号作为字符串符号,且在反引号中可以使用 ${xx} 进行变量插值 |
武技:开发 view/入门案例/批量删除提示.html 页面,并编写【案例 07】代码。
结构:
<button id="deleteBtn" type="button">批量删除</button>
<hr>
<input id="cbx01" name="user" type="checkbox"><label for="cbx01">赵四</label>
<input id="cbx02" name="user" type="checkbox"><label for="cbx02">刘能</label>
<input id="cbx03" name="user" type="checkbox"><label for="cbx03">广坤</label>
<input id="cbx04" name="user" type="checkbox"><label for="cbx04">王云</label>
<input id="cbx05" name="user" type="checkbox"><label for="cbx05">飞机</label>
脚本:
onload = () => {
// 抓 DOM 元素
let deleteBtn = document.querySelector('#deleteBtn');
// 挂点击事件
deleteBtn.onclick = () => {
// 抓取所有被选中的用户
let checkedUsers = document.querySelectorAll('input[name="user"]:checked');
// 空删保护
if (checkedUsers.length <= 0) {
alert('删除失败:您至少要选择一项!');
return;
}
// 误删保护
if (confirm(`您将要删除${checkedUsers.length}个用户,确认吗?`)) {
alert('删除成功!');
}
};
};
效果图:
心法:【案例 08】- 判断账号存在。
案例需求:当账号框丧失焦点时,判断账号是否已存在:
案例知识:
| 案例相关知识点 | 简述 | 说明 |
|---|---|---|
元素.onblur = () => {} | 失焦事件 | 当元素丧失焦点时触发 |
值 1 === 值 2 | 全等比较 | 全等比较不会自动转换数据类型进行比较 |
武技:开发 view/入门案例/账号失焦判断.html 页面,并编写【案例 08】代码。
结构:
<label for="ipt">账号:</label>
<input id="ipt">
<span id="tips"></span>
脚本:
onload = () => {
// 获取 DOM 元素
let tips = document.querySelector('#tips');
let usernameIpt = document.querySelector('#username-ipt');
// 挂载失焦事件
usernameIpt.onblur = () => {
// 空值保护
if (usernameIpt.value === '') {
tips.style['display'] = 'none';
return;
}
// 判断当前输入框中的账号是否为 admin
let isAdmin = 'admin' === usernameIpt.value;
// 展示提示文字
tips.style['display'] = 'inline-block';
// 若账号为 admin,则提示'该账号不可使用',否则提示'该账号可以使用'
tips.innerText = isAdmin ? '该账号不可使用' : '该账号可以使用';
// 若账号为 admin,则提示文字呈红色,否则呈现绿色
tips.style['color'] = isAdmin ? 'red' : 'green';
};
};
效果图:
心法:【案例 09】- 表单空值校验(普通按钮版)。
案例需求:输入账号和密码后,使用普通按钮进行表单提交:
案例知识:
| 案例相关知识点 | 简述 | 说明 |
|---|---|---|
元素.focus() | 获得焦点事件 | 使元素立刻获取焦点,一般元素类型为 <input>, <select>, <textarea> 等 |
表单元素.submit() | 表单提交函数 | 提交数据,一般元素类型为 <form> 表单 |
武技:开发 view/入门案例/普通按钮判空.html 页面,并编写【案例 09】代码。
结构:
<form id="loginForm" action="https://www.baidu.com" autocomplete="off">
<label for="usernameIpt">账号:</label>
<input id="usernameIpt" type="text" name="username"/>
<label for="passwordIpt">密码:</label>
<input id="passwordIpt" type="password" name="password"/>
<button type="button" id="loginBtn">登录</button>
</form>
脚本:
onload = () => {
// 抓取 DOM 元素
let usernameIpt = document.querySelector('#usernameIpt');
let passwordIpt = document.querySelector('#passwordIpt');
let loginForm = document.querySelector('#loginForm');
let loginBtn = document.querySelector('#loginBtn');
// 手动设置焦点
usernameIpt.focus();
// 为登录按钮挂点击事件
loginBtn.onclick = () => {
// 判断账号是否为空
if ('' === usernameIpt.value || null === usernameIpt.value) {
alert('账号不能为空!');
usernameIpt.focus();
return;
}
// 判断密码是否为空
if ('' === passwordIpt.value || null === passwordIpt.value) {
alert('密码不能为空!');
passwordIpt.focus();
return;
}
// 手动提交表单
loginForm.submit();
};
};
效果图:
心法:【案例 10】- 表单空值校验(提交按钮版)。
案例需求:输入账号和密码后,使用提交按钮进行表单提交:
案例知识:
| 案例相关知识点 | 简述 | 说明 |
|---|---|---|
表单元素.onsubmit = () => {} | 表单提交事件 | 当表单被提交时触发 |
return false | 阻止默认行为 | 阻止浏览器的默认行为,如提交表单,事件冒泡等 |
武技:开发 view/入门案例/提交按钮判空.html 页面,并编写【案例 10】代码。
结构:
<form id="loginForm" action="https://www.baidu.com" autocomplete="off">
<label for="usernameIpt">账号:</label>
<input id="usernameIpt" type="text" name="username"/>
<label for="passwordIpt">密码:</label>
<input id="passwordIpt" type="password" name="password"/>
<button type="submit" id="loginBtn">登录</button>
</form>
脚本:
onload = () => {
// 抓取 DOM 元素
let usernameIpt = document.querySelector('#usernameIpt');
let passwordIpt = document.querySelector('#passwordIpt');
let loginForm = document.querySelector('#loginForm');
// 手动设置焦点
usernameIpt.focus();
// 挂提交事件
loginForm.onsubmit = () => {
// 判断账号是否为空
if ('' === usernameIpt.value || null === usernameIpt.value) {
alert("账号不能为空!");
usernameIpt.focus();
// 阻止浏览器提交表单的这个默认行为
return false;
}
// 判断密码是否为空
if ('' === passwordIpt.value || null === passwordIpt.value) {
alert("密码不能为空!");
passwordIpt.focus();
// 阻止浏览器提交表单的这个默认行为
return false;
}
};
};
效果图:
心法:【案例 11】- 数码时钟。
案例需求:页面加载完毕的 3 秒后,显示一个实时的动态数码时钟,要求包括年,月,日,时,分,秒六大时间元素。
案例知识:
| 案例相关知识点 | 简述 | 说明 |
|---|---|---|
setInterval(任务函数名称,毫秒数) | 开启周期定时器 | 每隔 t 毫秒重复执行一次任务函数 返回数字类型的定时器编号 |
clearInterval(定时器编号) | 关闭周期定时器 | 通过定时器编号来关闭周期定时器 |
setTimeout(任务函数名称,毫秒数) | 开启延迟定时器 | 在 t 毫秒后仅执行一次任务函数 返回数字类型的定时器编号 |
clearTimeout(定时器编号) | 关闭延迟定时器 | 通过定时器编号来关闭延迟定时器 |
let now = new Date() | 获取本地时间 | let yy = now.getFullYear():获取年份let mm = now.getMonth() + 1:获取月份let dd = now.getDate():获取日let wk = now.getDay():获取星期let hh = now.getHours():获取时let mi = now.getMinutes():获取分let ss = now.getSeconds():获取秒 |
武技:开发 view/入门案例/显示数码时钟.html 页面,并编写【案例 11】代码。
结构:
<section id="clock" class="clock">将在 3 秒后显示数码时钟</section>
样式:
.clock {
width: 550px; /* 宽度 */
margin: 20px auto; /* 自居中 */
text-align: center; /* 内容居中 */
border: 5px inset black; /* 边框 */
padding: 20px; /* 内边距 */
color: red; /* 前景色 */
font-size: 50px; /* 字号 */
}
脚本:
// 用于接收定时器编号
let timer01;
onload = () => {
// 抓 DOM 元素
let clock = document.querySelector('#clock');
// 开启延迟定时器:在 3 秒后显示数码时钟
setTimeout(() => {
// 周期定时器先关后开,防止多开
clearInterval(timer01);
// 周期定时器任务:修改时钟的显示内容
function task() {
let now = new Date();
let yy = now.getFullYear();
let mm = now.getMonth() + 1 < 10 ? '0' + (now.getMonth() + 1) : now.getMonth() + 1;
let dd = now.getDate() < 10 ? '0' + now.getDate() : now.getDate();
let wk = '零一二三四五六日'.charAt(now.getDay());
let hh = now.getHours() < 10 ? '0' + now.getHours() : now.getHours();
let mi = now.getMinutes() < 10 ? '0' + now.getMinutes() : now.getMinutes();
let ss = now.getSeconds() < 10 ? '0' + now.getSeconds() : now.getSeconds();
clock.innerHTML = `${yy}年${mm}月${dd}日 星期${wk}${hh}:${mi}:${ss}`;
}
// 手动调用一次,解决第 1 秒钟的空挡问题
task();
// 开启周期定时器
timer01 = setInterval(task, 1000);
}, 3000);
};
效果图:
心法:【案例 12】- 鼠标跟踪。
案例需求:当鼠标移动时,会有一个圆形的容器跟随移动。
案例知识:
| 案例相关知识点 | 简述 | 说明 |
|---|---|---|
ev | 键鼠对象 | 每个函数的首位参数都可以用来表示鼠标或键盘对象,命名建议 ev:ev.clientX:获取鼠标相对于视口原点的 X 坐标,不带单位ev.clientY:获取鼠标相对于视口原点的 Y 坐标,不带单位ev.pageX:获取鼠标相对于文档原点的 X 坐标,不带单位ev.pageY:获取鼠标相对于文档原点的 Y 坐标,不带单位ev.screenX:获取鼠标相对于屏幕原点的 X 坐标,不带单位ev.screenY:获取鼠标相对于屏幕原点的 Y 坐标,不带单位 |
document.onmousemove = (ev) => {} | 鼠标移动事件 | 当鼠标在文档上移动时触发 |
元素.offsetWidth | 元素宽度数值 | 获取元素当前的宽度,不带单位,适用于计算 |
元素.offsetHeight | 元素高度数值 | 获取元素当前的高度,不带单位,适用于计算 |
武技:开发 view/入门案例/鼠标跟踪效果.html 页面,并编写【案例 12】代码。
结构:
<!--荧光笔-->
<div id="pen" class="pen"></div>
样式:
.pen {
width: 50px; /* 宽度 */
height: 50px; /* 高度 */
background: yellow; /* 背景色 */
border-radius: 50%; /* 圆角 */
position: absolute; /* 绝对定位 */
}
脚本:
onload = () => {
// 抓取 DOM 元素
let pen = document.querySelector('#pen');
// 当鼠标在整个文档上移动时触发
document.onmousemove = ev => {
// 荧光笔的左距离 = 鼠标横坐标 - 荧光笔的宽度 / 2
pen.style['left'] = ev.clientX - pen.offsetWidth / 2 + 'px';
// 荧光笔的上距离 = 鼠标纵坐标 - 荧光笔的高度 / 2
pen.style['top'] = ev.clientY - pen.offsetHeight / 2 + 'px';
};
};
效果图:
心法:【案例 13】- 贪食蛇移动。
案例需求:初始布局是一只拥有 1 个头关节 + 5 个身关节的贪食蛇,当按下某个【方向键】时,贪食蛇可以按照对应的方向进行移动,每次移动一个关节的位置且不允许超出指定的边界范围。
案例知识:
| 案例相关知识点 | 简述 | 说明 |
|---|---|---|
ev.key | 获取键盘描述 | 获取键盘对应的文字描述字符串 |
武技:开发 view/入门案例/贪食蛇移动版.html 页面,并编写【案例 13】代码。
结构:
<section id="board" class="board">
<div class="snake"></div>
<div class="snake"></div>
<div class="snake"></div>
<div class="snake"></div>
<div class="snake"></div>
<div class="snake"></div>
</section>
样式:
.board {
width: 500px; /* 宽度 */
height: 500px; /* 高度 */
outline: 10px solid red; /* 轮廓 */
position: relative; /* 相对定位 */
margin: 20px auto; /* 自居中 */
}
.snake {
width: 50px; /* 宽度 */
height: 50px; /* 高度 */
border-radius: 10px; /* 圆角 */
background-color: green; /* 背景色 */
position: absolute; /* 绝对定位 */
}
.snake:nth-child(1) {
background-color: black; /* 背景色 */
z-index: 1; /* Z 轴 */
}
脚本:
/* 面板宽度,面板高度,蛇关节宽度,蛇关节高度 */
let boardWidth, boardHeight, snakeWidth, snakeHeight;
onload = () => {
// 抓取 DOM 元素
let snakes = document.querySelectorAll(".snake");
let board = document.querySelector('#board');
// 赋值面板宽度,面板高度,蛇关节宽度,蛇关节高度
boardWidth = board.offsetWidth;
boardHeight = board.offsetHeight;
snakeWidth = snakes[0].offsetWidth;
snakeHeight = snakes[0].offsetHeight;
// 当用户按下某个按键时触发
document.onkeydown = ev => {
// 蛇身先移动
snakeBodyMove(snakes);
// 蛇头后移动
snakeHeadMove(ev, snakes[0]);
};
};
/* 蛇身移动:后面的蛇身追赶前面的蛇身 */
function snakeBodyMove(snakes) {
for (let i = snakes.length - 1; i > 0; i--) {
snakes[i].style['left'] = snakes[i - 1].style['left'];
snakes[i].style['top'] = snakes[i - 1].style['top'];
}
}
/* 蛇头移动:移动方向由键盘决定 */
function snakeHeadMove(ev, snakeHead) {
// 获取蛇头的当前左距离和当前右距离
let left = snakeHead.offsetLeft;
let top = snakeHead.offsetTop;
// 对应用户按键移动蛇头:每次移动前进行边界保护,防止出界
switch (ev.key) {
case 'ArrowLeft':
if (left <= 0) return;
snakeHead.style['left'] = left - snakeWidth + 'px';
break;
case 'ArrowUp':
if (top <= 0) return;
snakeHead.style['top'] = top - snakeHeight + 'px';
break;
case 'ArrowRight':
if (left >= boardWidth - snakeWidth) return;
snakeHead.style['left'] = left + snakeWidth + 'px';
break;
case 'ArrowDown':
if (top >= boardHeight - snakeHeight) return;
snakeHead.style['top'] = top + snakeHeight + 'px';
break;
}
}
效果图:
心法:【案例 14】- 阻止事件冒泡。
案例需求:当点击【事件冒泡】按钮时,自身的点击事件和父元素的点击事件被触发,但要求爷元素的点击事件不被触发。
案例知识:
| 案例相关知识点 | 简述 | 说明 |
|---|---|---|
ev.cancelBubble = true | 阻止事件冒泡 | 在子元素的事件中阻止向上触发父级元素的相同事件的冒泡效果 |
武技:开发 view/入门案例/阻止事件冒泡.html 页面,并编写【案例 14】代码。
结构:
<body id="body" class="body">
<section id="sec" class="sec">
<button id="btn" class="btn" type="button">事件冒泡</button>
</section>
</body>
样式:
.body {
border: 5px solid red; /* 边框 */
}
.sec {
margin: 100px auto; /* 外边距 */
width: 60%; /* 宽度 */
height: 100px; /* 高度 */
border: 5px solid green; /* 边框 */
text-align: center; /* 内容居中 */
}
.btn {
margin-top: 35px; /* 外边距 */
}
脚本:
onload = () => {
// 抓 DOM 元素
let bubbleBody = document.querySelector("#body");
let bubbleSec = document.querySelector("#sec");
let bubbleBtn = document.querySelector("#btn");
// 分别挂载点击事件
bubbleBody.onclick = () => { console.log("body..."); }
bubbleSec.onclick = ev => {
console.log("section...");
// 阻止事件冒泡
ev.cancelBubble = true;
};
bubbleBtn.onclick = () => { console.log("button..."); }
};
效果图:
心法:在整个运行过程中,可以 发生变化的量叫变量,不能 发生变化的量叫常量。
声明常量的方式如下:
| 声明赋值代码 | 中文 | 基本规则 |
|---|---|---|
const a = 10 | 常量 | 1. 声明时必须立即赋值(即声明和赋值必须在同一行) 2. 基本类型赋值后值不可修改,引用类型赋值后仅不可修改引用地址 3. 无预解析 |
声明变量的方式如下:
| 声明赋值代码 | 版本 | 默认值 | 是否会被预解析 | 重复声明 | 规范性 |
|---|---|---|---|---|---|
var a = 10 | ES5 | undefined | 完全预解析(变量提升) | 允许 | 不规范,易出错,不推荐 |
let a = 10 | ES6+ | undefined | 无预解析 | 禁止 | 更严谨,推荐 |
预解析:预解析的本质是'变量提升',是 JS 引擎执行代码前的预处理逻辑,仅针对 var 变量和函数声明,JS 代码中的 var 变量,函数的内置参数,函数内的另一个函数声明以及函数内的 var 变量都会被预解析,优先级从高到低,其具体流程分为两步,如下:
武技:开发 view/基础 - 变量常量.html 页面,并测试 JS 变量的预解析效果。
结构:
<button id="varBtn" type="button">测试 var 变量是否会预解析</button>
<button id="letBtn" type="button">测试 let 变量是否会预解析</button>
脚本:
onload = () => {
// 抓 DOM 元素
let varBtn = document.querySelector('#varBtn');
let letBtn = document.querySelector('#letBtn');
// 测试 var 变量是否会预解析,以及是否可以重复命名
varBtn.onclick = () => {
console.log('money', money);
{
// var 允许重复声明,覆盖值
var money = 100;
var money = 200;
}
};
// 预解析:将声明放在所在函数的首行,赋值保持原位
/* varBtn.onclick = () => { var money; console.log('money', money); { money = 100; money = 200; } } */
// 测试 let 变量是否会预解析,以及是否可以重复命名
letBtn.onclick = () => {
console.log('money', money);
{
let money = 100;
// let money = 200;
}
};
};
效果图:
心法:JS 是弱类型语言(变量的类型由赋值的'值'决定,而非声明时指定,且类型可动态改变),不需要强调类型,但不代表没有类型,可以使用
typeof关键字测得值的本质类型。
常用数据类型如下:
| 数据类型 | 中文 | 核心特征 & 示例 | 特殊说明 |
|---|---|---|---|
| number | 数字 | 包含整数和浮点数 包含特殊值 NaN 包含特殊值 Infinity 和 -Infinity | 1. NaN 是特殊数字(非数字) 2. NaN !== NaN 3. Infinity 表示无穷大 |
| string | 字符串 | 支持单,双,反三种引号 | 反引号可配${}快速插值 |
| boolean | 布尔 | 仅含 true 和 false,可直接作为判断条件 | 0,空串,null 和 undefined 视为假,其余为真 |
| function | 函数 | 包括 ES5 中的普通函数 包括 ES6 中的箭头函数 包括 JS 中的内置函数 | 函数可作为其它函数的参数或返回值 |
| object | 对象 | 包括普通 JSON 对象 包括数组对象 包括从页面端抓取到的 DOM 元素均可视为对象类型 | null 会被检测为 object 类型,属于历史 BUG |
| undefined | 未定义 | 包括仅声明但未赋值的变量 包括不存在的对象属性 包括无返回值的函数 | 布尔判断为假 |
非数字函数isNaN():用于判断非数字(NaN,即 Not a Number),但 NaN 不一定等于 NaN。
武技:开发 view/基础 - 数据类型.html 页面,并测试 JS 变量的数据类型。
结构:
<button id="btn" type="button">按钮</button>
脚本:
onload = () => {
let result;
// undefined
console.log(result, typeof result);
// number
result = 1;
console.log(result, typeof result, isNaN(result) ? '非数字' : '数字');
// string
result = 'abc';
result = "abc";
result = `abc`;
console.log(result, typeof result, isNaN(result) ? '非数字' : '数字');
// boolean
result = true;
console.log(result, typeof result);
// function
result = function () {};
console.log(result, typeof result);
result = () => {};
console.log(result, typeof result);
// object
result = document.querySelector('#btn');
console.log(result, typeof result);
// null 也属于 object
result = null;
console.log(result, typeof result);
// 真假规范
let a = 0;
console.log('a = 0', a ? '真' : '假');
let b = 1;
console.log('b = 1', b ? '真' : '假');
let c = '';
console.log('c = \'\'', c ? '真' : '假');
let d = 'hello';
console.log('d = \'hello\'', d ? '真' : '假');
let e = null;
console.log('e = null', e ? '真' : '假');
let f = undefined;
console.log('f = undefined', f ? '真' : '假');
};
效果图:

心法:JS 类型转换分为 显示转换(开发者主动用函数来控制类型转换) 和 隐式转换 (JS 引擎在运算或判断时自动触发的类型转换)两种。
常用显示转换如下:
| 转换方法 | 核心规则 | 特殊说明 |
|---|---|---|
parseInt(str) | 1. 逐字符转整数,遇非数字停止 2. 忽略前导空格 | parseInt('12abc34') → 12parseInt('abc1234') → NaNparseInt('12.99') → 12parseInt(' 12') → 12 |
parseFloat(str) | 1. 逐字符转浮点数,遇非数字停止 2. 忽略前导空格 3. 仅识别一个小数点 | parseFloat('3.14a15') → 3.14parseFloat('a3.1415') → NaNparseFloat('12.34.56') → 12.34 |
Number(val) | 整体转换为数字(而非逐字符) | Number('123') → 123Number('123abc')→ NaNNumber(true) → 1Number(null) → 0Number(undefined) → NaN |
数字.toFixed(n) | 四舍五入保留 n 位小数 返回 字符串类型 | (1.234).toFixed(2) → '1.23'(1).toFixed(2) → '1.00' |
String(val) | 整体转换为字符串 | String(123)→ '123'String(null) → 'null'String(undefined) → "undefined" |
Boolean(val) | 转换为布尔值 假值列表:0 / '' / null / undefined / NaN / false | Boolean(0) → falseBoolean([]) → trueBoolean({}) → trueBoolean('0') → true |
常用隐示转换如下:
| 场景 | 转换规则 | |
|---|---|---|
值 1 == 值 2 | 1. 类型相同直接比较 2. null/undefined 互相相等(仅等于自身) 3. 其他类型先转数字再比较 | 10 == '10' → true0 == false → true'' == 0 → truenull == undefined → trueNaN == NaN → false |
字符串 1 - 字符串 2 | 双方先转数字,再执行减法 | '100' - '20' → 80'100' - 'abc' → NaN'100' - true → 99(true 转 1) |
字符串 1 + 字符串 2 | 直接拼接为字符串 加号只要一侧是字符串,就触发拼接 | '100' + '20' → '10020'100 + '20' → '10020'100 + true → 101(true 转 1) |
* / % 算术运算 | 所有操作数先转数字,再执行运算 | '10' / '2' → 510 % '3' → 1'abc' * 10 → NaN |
! 值 逻辑非 | 先转布尔值,再取反 | !0 → true!'' → true![] → false!!0 → false(双重非转布尔) |
if/while 判断条件 | 自动转布尔值 假值列表:0 / '' / null / undefined / NaN / false | if (空数组) → trueif (空对象) → true |
武技:开发 view/基础 - 类型转换.html 页面,并测试 JS 类型转换。
脚本:
// 显示转换:将字符串转为整数
console.log(parseInt(' 123')); // 123 → 忽略前导空格
console.log(parseInt('abc123')); // NaN → 首字符非数字
console.log(parseInt('123e0')); // 123 → e 是非数字,停止解析
console.log(parseInt('')); // NaN → 空字符串返回 NaN(区别于 Number('') → 0)
// 显示转换:将字符串转为小数
console.log(parseFloat('3.1a4'));
console.log(parseFloat('3.a14'));
console.log(parseFloat('abc34'));
// 显示转换:保留两位小数,自动四舍五入
console.log('3.1499999 →', 3.1499999.toFixed(2));
// 隐式转换:+ 号的双重特性(拼接 vs 运算)
console.log(10 + 20); // 30 → 算术运算(无字符串)
console.log(10 + '20'); // "1020" → 字符串拼接(一侧是字符串)
console.log(10 + true); // 11 → 算术运算(true 转 1)
console.log(10 + []); // "10" → 空数组转空字符串,触发拼接
console.log(10 + {}); // "10[object Object]" → 对象转字符串
// 隐式转换:双等号比较 vs 三等号比较
console.log(0 == false); // true → 都转数字 0
console.log('' == 0); // true → 都转数字 0
console.log(null == undefined); // true → 特殊规则
console.log([] == 0); // true → 空数组转数字 0
console.log({} == '[object Object]'); // true → 对象转字符串
console.log(0 === false); // false → 类型不同(number vs boolean)
console.log(null === undefined); // false → 类型不同(null vs undefined)
// 隐式转换:逻辑非
console.log(!0); // true → 0 转 false,取反为 true
console.log(![]); // false → 空数组转 true,取反为 false
console.log(!!'abc'); // true → 双重非转布尔,等效于 Boolean('abc')
// 隐式转换:字符串相减,会先把类型都转成数字,然后再进行数学的减法计算
console.log('100' - '1');
效果图:

心法:JS 函数的四要素包括返回值,形参列表,函数名和函数体,可作为另一个函数的参数、返回值,或直接赋值给一个变量。
心法:ES5 使用
function 函数名 ( 形参 ){ 函数体 }格式定义函数,使用带名函数时,建议使用。
ES5 函数定义核心规则:
| 要素 | 规则 |
|---|---|
| 函数名 | 使用字母,下划线或 $ 开头,区分大小写 |
| 形参列表 | 1. 不允许写具体类型,也不允许使用 var 或 let 关键字 2. 数量可与实参不一致 3. 若实参数 < 形参数:未传参的形参为 undefined 4. 若实参数 > 形参数:可用 arguments 内置对象获取 5. 形参支持设置常量,变量,表达式,函数调用等类型的默认值 6. 支持使用 (...params) 来代表余下参数(本质是一个数组),变量名随意 |
| 函数体 | 1. 可包含任意 JS 代码,支持 return 返回值 2. 在不使用 return 的情况下,均默认返回 undefined |
| 返回值 | 1. return 可返回任意类型,如基本类型,对象,函数等 2. 执行 return 后函数立即终止 |
| arguments | 函数体内的内置对象,可直接使用,代表所有实参 类数组,有 length 属性和下标,但无数组方法 |
武技:开发 view/基础-ES5 函数定义.html 页面,并测试 ES5 函数定义。
脚本:
onload = () => {
es5(1, 2, 3, 4, 5, 6);
}
function fn() {
return 3;
}
/* ES5 函数 */
function es5(a, b, c = a + b + 1 + fn(), ...params) {
console.log(a, b, c);
console.log('params', params);
console.log('arguments', arguments);
}
效果图:

心法:ES6 使用
let 函数名 = ( 形参 ) => { 函数体 }格式定义函数(不支持arguments内置关键字),使用匿名函数时,建议使用。
箭头函数核心语法规则:
| 场景 | 语法格式示例 | 注意事项 |
|---|---|---|
| 多参数 多行函数体 | let r = (a, b) => { return a + b; } | 多行函数体 {} 和 return 均不能省略 |
| 多参数 单行函数体 | let r = (a, b) => a + b | 省略 {} 时,自动返回单行代码结果,无需写 return |
| 单参数 任意函数体 | let r = (n) => n * 2let r = n => n * 2 | 仅单参数可省略 (),多参数或无参数必须加 () |
| 无参数 任意函数体 | let r = () => 10 | 无参数时 () 不可省略 |
武技:开发 view/基础-ES6 函数定义.html 页面,并测试 ES6 函数定义。
脚本:
onload = () => {
es6(1, 2, 3, 4, 5, 6);
}
function fn() {
return 3;
}
/* ES6 函数 */
let es6 = (a, b, c = a + b + 1 + fn(), ...params) => {
console.log(a, b, c);
console.log('params', params);
console.log('arguments', arguments); // error
};
效果图:

心法:函数体内支持使用
this内置关键字指向绑定该事件的元素(ES5)或者 window 对象(ES6)。
this 指向问题:
获取事件源ev.target:
ev.target 是事件对象(ev)的核心属性,其始终指向触发事件的原始 DOM 元素(事件源),不受函数类型(ES5/ES6)影响,是跨场景获取事件源的通用方案,推荐优先使用。
需注意:ev 作为事件触发时自动传入的参数,仅在 事件绑定的匿名函数 / 箭头函数参数列表中 可直接声明并使用,若在函数内部手动调用该事件函数,需手动传入事件对象才能正常访问 ev.target 属性。
武技:开发 view/基础-this 关键字.html 页面,并测试事件函数中的 this 指向。
结构:
<button id="es5Btn" type="button">测试 ES5 函数中的 this 指向</button>
<button id="es6Btn" type="button">测试 ES6 函数中的 this 指向</button>
脚本:
onload = () => {
let es5Btn = document.querySelector('#es5Btn');
let es6Btn = document.querySelector('#es6Btn');
// 测试 ES5 函数中的 this 指向
es5Btn.onclick = function (ev) {
console.log('this', this);
console.log('ev.target', ev.target);
};
// 测试 ES6 函数中的 this 指向
es6Btn.onclick = (ev) => {
console.log('this', this);
console.log('ev.target', ev.target);
};
};
效果图:
心法:JS 的函数可以发生嵌套,此效果被称为闭包。
闭包特点:
武技:开发 view/基础 - 函数闭包.html 页面,并测试 JS 函数闭包。
脚本:
/* 父函数 */
onload = () => {
// 父函数变量
let fu = '父函数变量';
/* 子函数 */
function fn() {
let zi = '子函数变量';
// 子函数中可以直接使用自己的变量
console.log('子函数访问自己的变量', zi);
// 子函数中可以直接使用父函数的变量
console.log('子函数访问父函数的变量', fu);
}
// 必须要调用子函数,否则子函数无法自动执行
fn();
// 父函数中,可以直接使用自己的变量
console.log('父函数访问自己的变量', fu);
// 父函数中,无法使用子函数变量
console.log('父函数访问子函数的变量', zi); // error
};
// 外界无法直接调用子函数
// fn(); // error
效果图:

心法:JS 中的属性分为 原生属性(自动映射到 DOM 元素的 JS 对象上,如 id, class 等)和 标签属性(自定义的属性,如 data-id,apple 等)两种,而操作属性主要有三种方式,分为 分量符(点语法),中括号 和 DOM_API 三种操作方式,三种方式均遵循同名覆盖规则。
属性操作方式:
| 方式 | 代码 | 描述 | 其它 |
|---|---|---|---|
| 分量符 | obj.idobj.id = 'joezhou'delete obj.id | 获取 属性,不存在返回 undefined 添加/修改 属性,重复设置会发生覆盖 删除 属性 | 无法直接读取标签属性 不反映在 HTML 标签上 会反映在 HTML 标签上 |
| 中括号 | obj['id']obj['id'] = 'joezhou'delete obj['id'] | 获取 属性,不存在返回 undefined 添加/修改 属性,重复设置会发生覆盖 删除 属性 | 无法直接读取标签属性 不反映在 HTML 标签上 会反映在 HTML 标签上 |
| DOM_API | obj.getAttribute('id')obj.setAttribute('id', 'joezhou')obj.removeAttribute('id') | 获取 属性,不存在返回 null 添加/修改 属性,重复设置会发生覆盖 删除 属性 | 允许直接读取标签属性 会反映在 HTML 标签上 会反映在 HTML 标签上 |
武技:开发 view/文档 - 属性操作.html 文件,并测试 JS 属性的相关操作。
结构:
<!-- 原生属性:id 和 class 标签属性:apple 和 data-id -->
<div id="box" class="container" apple="good" data-id="100"></div>
脚本:
onload = () => {
const box = document.querySelector('#box');
/* ========== 点语法 ==========*/
// 读取原生属性
console.log('box.id', box.id);
// 读取关键字:关键字不能直接作为 DOM 对象的属性名,浏览器特意映射为 className 避开冲突
console.log('box.class', box.class);
console.log('box.className', box.className);
// 读取标签属性:点语法无法读取标签属性
console.log('box.apple', box.apple);
// 读取中划线属性:直接格式报错
//console.log('box.data-id', box.data-id);
// 新增属性:不会同步到 HTML 标签
box.age = 20;
console.log('新增后', box.age)
// 修改属性:不会同步到 HTML 标签
box.age = 21;
console.log('修改后', box.age);
// 删除属性
delete box.age;
// 属性不存在时返回 undefined
console.log('删除后', box.age);
/* ========== 中括号语法 ==========*/
// 读取原生属性
console.log(`box['id']`, box['id']);
// 读取关键字:关键字不能直接作为 DOM 对象的属性名,浏览器特意映射为 className 避开冲突
console.log(`box['class']`, box['class']);
console.log(`box['className']`, box['className']);
// 读取标签属性:中括号语法无法读取标签属性
console.log(`box['apple']`, box['apple']);
// 读取中划线属性:格式不报错,但 data-id 是标签属性,中括号语法无法读取标签属性
console.log(`box['data-id']`, box['data-id']);
// 新增属性:不会同步到 HTML 标签
box['age'] = 101;
console.log('新增后', box['age']);
// 修改属性:不会同步到 HTML 标签
box['age'] = 102;
console.log('修改后', box['age']);
// 删除属性
delete box['age'];
// 属性不存在时返回 undefined
console.log('删除后', box['age']);
/* ========== DOM_API 语法 ==========*/
// 读取标签属性
console.log(`box.getAttribute('id')`, box.getAttribute('id'));
// 读取关键字:DOM_API 操作的是标签的'属性名字符串',不受 JS 关键字规则限制
console.log(`box.getAttribute('class')`, box.getAttribute('class'));
console.log(`box.getAttribute('className')`, box.getAttribute('className'));
// 读取标签属性
console.log(`box.getAttribute('apple')`, box.getAttribute('apple'));
// 读取中划线属性:格式不报错,且成功读取 data-id 属性
console.log(`box.getAttribute('data-id')`, box.getAttribute('data-id'));
// 新增属性:会同步到 HTML 标签
box.setAttribute('age', '200');
console.log(`新增后`, box.getAttribute('age'));
// 修改属性:会同步到 HTML 标签
box.setAttribute('age', '201');
console.log(`修改后`, box.getAttribute('age'));
// 删除属性:HTML 上标签的对应属性也会消失
box.removeAttribute('age');
// 属性不存在时返回 null
console.log(`删除后`, box.getAttribute('age')); // null
};
效果图:

心法:JS 操作 DOM 元素样式主要分「行内样式操作」和「计算样式读取」两类,核心区别在于样式获取范围。
样式相关操作:样式名需要驼峰化处理:
| 操作类型 | 语法 | 样式范围 | 核心场景 |
|---|---|---|---|
| 读取样式 | obj.style['样式名']getComputedStyle(obj)['样式名'] | 读取行内样式 读取所有最终渲染样式 | 读取自己设置的行内样式 读取元素实际显示的样式 |
| 操作样式 | obj.style['样式名'] = '样式值' | 仅行内样式 | 临时修改单个样式 |
| 操作类名 | obj.classList.add(类名)obj.classList.remove(类名)obj.classList.toggle(类名) | 类对应的所有样式 | 批量添加样式 批量移除样式 批量切换样式 |
武技:开发 view/文档 - 样式操作.html 文件,并测试 JS 样式的相关操作。
结构:
<!--内嵌样式-->
<div id="div" class="a" style="color: red"></div>
样式:
#div {
font-size: 177px; /* 字号 */
}
.a {
border: 10px solid green; /* 边框 */
}
.b {
border: 10px solid yellow; /* 边框 */
}
脚本:
onload = () => {
// 抓取 DOM 元素
let div = document.querySelector("#div");
// 可以获取内嵌样式,无法获取内部样式
console.log('内嵌 color:', div.style['color']);
console.log('内部 fontSize:', div.style['fontSize']);
// 可以获取内嵌样式,也能获取内部样式(color 会被自动转为 RGB 格式)
console.log('内嵌 color:', getComputedStyle(div)['color']);
console.log('内部 fontSize:', getComputedStyle(div)['fontSize']);
// 设置内嵌样式
div.style['width'] = '100px';
div.style['height'] = '100px';
div.style['backgroundColor'] = 'red';
// 移除指定 class 名
div.classList.remove('a');
// 添加指定 class 名
div.classList.add('b');
};
效果图:

心法:JS 获取 DOM 元素分为「静态集合」和「动态集合」两类,核心区别在于集合是否随 DOM 变化自动更新。
常用元素操作:
| 获取方式 | 代码 | 返回类型 |
|---|---|---|
| 静态获取 | document.querySelector(selector)document.querySelectorAll(selector) | 单个 DOM 元素 NodeList 集合(DOM 变化后集合不更新) |
| 动态获取 | document.getElementById(id)document.getElementsByName(name)document.getElementsByTagName(tag) | 单个 DOM 元素(不加井号,只能通过 document 关键字获取) NodeList 集合(DOM 变化后集合自动更新) HTMLCollection 集合(DOM 变化后集合自动更新) |
| 获取父元素 | 子.parentNode | 单个 DOM 元素 |
| 获取子元素 | 父.childNodes父.children | NodeList 集合,包括文本元素 HTMLCollection 集合,不包括文本元素 |
| 创建元素 | document.createElement('') | 通过标签名凭空创造一个 DOM 元素 |
| 追加或移动元素 | 父.appendChild(子) | 追加:若子元素本不存在,则表示追加,即追加到父元素内部的末尾 移动:若子元素本就存在,则表示移动,即从原来的位置移动到末尾 |
| 插入元素 | 父.insertBefore(子 A, 子 B) | 将子元素 A 插入到子元素 B 之前 |
| 删除元素 | 父.removeChild(子) | 从父元素中删除子元素 |
武技:开发 view/文档 - 元素操作.html 文件,并测试 JS 元素操作。
结构:
<button id="btn01" type="button">静态获取元素</button>
<button id="btn02" type="button">动态获取元素</button>
<button id="btn03" type="button">获取父元素</button>
<button id="btn04" type="button">获取子元素</button>
<button id="btn05" type="button">追加 2222 到末尾</button>
<button id="btn06" type="button">移动 2222 到末尾</button>
<button id="btn07" type="button">插入 0000 到头部</button>
<button id="btn08" type="button">删除末尾的元素</button>
<ul id="items">
<li id="li1111">1111</li>
</ul>
脚本:
onload = () => {
// 抓 DOM 元素
let btn01 = document.querySelector('#btn01');
let btn02 = document.querySelector('#btn02');
let btn03 = document.querySelector('#btn03');
let btn04 = document.querySelector('#btn04');
let btn05 = document.querySelector('#btn05');
let btn06 = document.querySelector('#btn06');
let btn07 = document.querySelector('#btn07');
let btn08 = document.querySelector('#btn08');
// 静态获取元素
btn01.onclick = () => {
let items = document.querySelector('#items');
// 静态获取元素:lis.length 只获取一次
let lis = items.querySelectorAll('li');
items.innerHTML += `<li>2222</li>`;
console.log('length: ' + lis.length);
items.innerHTML += `<li>3333</li>`;
console.log('length: ' + lis.length);
items.innerHTML += `<li>4444</li>`;
console.log('length: ' + lis.length);
};
// 动态获取元素
btn02.onclick = () => {
let items = document.getElementById('items');
// 动态获取元素:lis.length 每次都重新获取
let lis = items.getElementsByTagName('li');
items.innerHTML += `<li>2222</li>`;
console.log('length: ' + lis.length);
items.innerHTML += `<li>3333</li>`;
console.log('length: ' + lis.length);
items.innerHTML += `<li>4444</li>`;
console.log('length: ' + lis.length);
};
// 获取父元素
btn03.onclick = () => {
let items = document.querySelector('#items');
// 获取 items 的父元素
console.log(items.parentNode);
};
// 获取子元素
btn04.onclick = () => {
let items = document.querySelector('#items');
// 获取 items 的子节点,包括文本元素
console.log('childNodes', items.childNodes);
// 获取 items 的子节点,不包括文本元素
console.log('children', items.children);
};
// 追加元素
btn05.onclick = () => {
let items = document.querySelector('#items');
let li = document.createElement('li');
li.innerText = '2222';
// 若子元素本不存在,则表示追加,即追加到父元素内部的末尾
items.appendChild(li);
};
// 移动元素
btn06.onclick = () => {
let items = document.querySelector('#items');
// 追加 2222 到末尾
let li2222 = document.createElement('li');
li2222.id = 'li2222';
li2222.innerText = '2222';
items.appendChild(li2222);
// 追加 3333 到末尾
let li3333 = document.createElement('li');
li3333.id = 'li3333';
li3333.innerText = '3333';
items.appendChild(li3333);
// 若子元素本就存在,则表示移动,即从原来的位置移动到末尾
items.appendChild(li2222);
};
// 插入元素
btn07.onclick = () => {
let items = document.querySelector('#items');
let li1111 = document.querySelector('#li1111');
let li = document.createElement('li');
li.innerText = '0000';
// 将 li 插入到当前第一个 li 之前
items.insertBefore(li, li1111);
};
btn08.onclick = () => {
let items = document.querySelector('#items');
// 追加 2222 到末尾
let li2222 = document.createElement('li');
li2222.id = 'li2222';
li2222.innerText = '2222';
items.appendChild(li2222);
// 追加 3333 到末尾
let li3333 = document.createElement('li');
li3333.id = 'li3333';
li3333.innerText = '3333';
items.appendChild(li3333);
// 删除最后一个 li
let lis = items.querySelectorAll('li');
items.removeChild(lis[lis.length - 1]);
};
};
效果图:
心法:JavaScript 数组是 动态可变的有序集合,支持存储任意类型元素(数字、字符串、对象、函数等),但实际开发中建议存储同类型元素以提升代码可读性和性能。
数组创建的 4 种常用方式:empty 是空占位符,不是 null 也不是 undefined,但访问时输出 undefined 结果,和 undefined 不同的是,遍历时 empty 会被跳过,而 undefined 不会:
| 数组创建方式 | 语法示例 | 示例结果 |
|---|---|---|
| 字面量创建(推荐) | let arr = [1, 2, 3]let arr = []let arr = [1, 'a', true] | [1, 2, 3] [] [1, 'a', true] |
| new Array(元素列表) | let arr = new Array(1, 2, 3) | [1, 2, 3] |
| new Array(列表长度) | let arr = new Array(3) | [empty, empty, empty] |
| Array.of(元素列表) | let arr = Array.of(1, 2, 3)let arr = Array.of(3) | [1, 2, 3] [3] |
| Array.from(类数组) | let arr = Array.from(arguments) | 对应参数组成的数组 |
| Array.from(可迭代对象) | let arr = Array.from('abc')let arr = Array.from(new Set([1,2,3]))let arr = Array.from(new Map([['a',1]]))let arr = Array.from({length:3}) | ['a', 'b', 'c'] [1, 2, 3] ['a', 1] [undefined, undefined, undefined] |
数组验证Array.isArray(arr):判断 arr 是否是一个数组对象,返回布尔结果。
武技:开发 view/基础 - 数组声明.html 页面,并测试 JS 数组声明。
脚本:
// 数组声明 - 方式 1:
const arr01 = ['a', 'b', 'c'];
// 数组声明 - 方式 2:
const arr02 = new Array(1, 2, 3);
// 数组声明 - 方式 3:
const arr03 = Array.of(3);
// 数组声明 - 方式 4:
const arr04 = Array.from('hello');
// 数组打印
console.log('通过字面量创建:', arr01);
console.log('通过 new Array() 创建:', arr02);
console.log('通过 Array.of() 创建:', arr03);
console.log('通过 Array.from() 创建:', arr04);
// 数组验证
console.log('arr01 是否为数组:', Array.isArray(arr01) ? '是' : '否');
// 数组 for-i 遍历:其中 i 表示数组角标
let result01 = '';
for (let i = 0, j = arr01.length; i < j; i++) {
result01 += i + ':' + arr01[i] + ' ';
}
console.log('for-i 遍历:', result01);
// 数组 for-in 遍历:其中 i 表示数组角标
let result02 = '';
for (let i in arr01) {
result02 += i + ':' + arr01[i] + ' ';
}
console.log('for-in 遍历:', result02);
// 数组 forEach 遍历(不带下标)
let result03 = '';
arr01.forEach(v => result03 += v + ' ');
console.log('forEach 遍历:', result03);
// 数组 forEach 遍历(带下标)
let result04 = '';
arr01.forEach((v, i) => result04 += i + ':' + v + ' ');
console.log('forEach 遍历(带下标):', result04);
效果图:

心法:解构的意义在于快速从一个数组中获取一个或多个元素的值,解构过程中,多余的变量均赋值 undefined。
| 相关代码 | 描述 |
|---|---|
let [a, b] = arr | 将数组中的元素对位赋值给 a 和 b |
let [a = 3] = arr | 将数组 0 号位赋值给 a,若 0 号位不存在则将 3 赋值为 a |
let [ , , c] = arr | 将数组 2 号位赋值给 c |
let [a, [b, c], d] = arr | 支持多维数组对位解构 |
let [a, ...p] = arr | 将数组 0 号位赋值给 a,其余位依次赋给 p 数组 |
武技:开发 view/基础 - 数组解构.html 页面,并测试 JS 数组解构。
脚本:
// 将数组中的元素对位赋值给 `a1` 和 `a2`。
let [a1, a2] = [1, 2, 3, 4];
console.log(a1, a2);
// 将数组 0 号位赋值给 `b1`,若 0 号位不存在则将 `3` 赋值为 `b1`。
let [b1 = 3] = [];
console.log(b1);
// 将数组 2 号位赋值给 `c3`。
let [, , c3] = [1, 2, 3, 4];
console.log(c3);
// 支持多维数组对位解构
let [d1, [d21, d22], d3] = [1, [11, 12], 2];
console.log(d1, d21, d22, d3);
// 将数组 0 号位赋值给 `e1`,其余位依次赋给 `e` 数组。
let [e1, ...e] = [1, 2, 3, 4];
console.log(e1, e);
效果图:

心法:JS 数组常用方法。
不作用于原数组的 API 方法:
| 数组常用 API 方法 | 描述 | 返回值 |
|---|---|---|
arr.toString() | 将 arr 转为字符串,使用 , 分隔 | 拼接后的新数组 |
arr.join('-') | 将 arr 转为字符串,使用 自定义符号 分隔 | 拼接后的新数组 |
arr.concat(arr01) | 在 arr 末尾 拼接 arr01 数组 | 拼接后的新数组 |
arr.indexOf(e)arr.lastIndexOf(e) | 在 arr 中正方向查找元素 e( 正向寻找 ) 在 arr 中反方向查找元素 e( 反向寻找 ) | 首现位置,不存在返回 -1 |
arr.map(e => {}) | 让 arr 的每个元素依次进入函数进行 处理 | 处理后的新数组 |
arr.filter(e => {}) | 让 arr 的每个元素依次进入函数进行 筛选 | 满足条件的新数组 |
arr.every(e => {}) | 让 arr 的每个元素依次进入函数进行 判断 | 全部元素满足条件才返回 true 有一个元素不满足就返回 false |
arr.some(e => {}) | 让 arr 的每个元素依次进入函数进行 判断 | 某个元素满足条件就返回 true 全部元素都不满足才返回 false |
arr.reduce((p1, p2) => {})arr.reduceRight((p1, p2) => {}) | 让 arr 的每个元素依次进入函数进行 正向归约 让 arr 的每个元素依次进入函数进行 反向归约 归约流程: 1. 初始将数组的第一个元素赋值给 p1 2. 初始将数组的第二个元素赋值给 p2 3. 进入函数体进行数据处理 4. 每次都将处理的结果重新赋值给 p1 5. 读取数组的下一个元素并赋值给 p2 6. 继续重复处理,直到数组最后一个元素 | 整体归约完毕后的新数组 |
直接作用于原数组的 API 方法:
| 数组常用 API 方法 | 描述 | 返回值 |
|---|---|---|
arr.unshift(元素列表) | 在 arr 头部 添加 元素列表(整体添加,而非依次添加) | 添加后的数组长度 |
arr.shift() | 从 arr 头部 删除 一个元素 | 被删除的那个元素 |
arr.push(元素列表) | 在 arr 尾部 添加 元素列表(整体添加,而非依次添加) | 添加后的数组长度 |
arr.pop() | 从 arr 尾部 删除 一个元素 | |
arr.splice(n, m, [元素列表]) | 从 arr 的 n 号位(包括)开始,删 m 个元素,并插入元素列表 | 被删除的元素数组 |
arr.sort()arr.sort((a, b) => a - b)arr.sort((a, b) => b - a) | 将 arr 按字符串规则升序排序 将 arr 按数字大小升序排序(正数往前移,负数往后移,零不动) 将 arr 按数字大小降序排序(正数往前移,负数往后移,零不动) | 排序后的数组 |
武技:开发 view/基础 - 数组常用方法.html 页面,并测试 JS 数组常用方法。
脚本:
/* ========== 不作用于原数组的相关方法 ========== */
// toString(): 将 arr 转化为字符串,元素之间使用逗号拼接
console.log('toString(): ', ['a', 'b', 'c', 'd', 'e'].toString());
// join(): 将 arr 转化为字符串,元素之间使用指定的字符拼接
console.log('join(): ', ['a', 'b', 'c', 'd', 'e'].join('-'));
// concat(): 将 ['c', 'd'] 连接到 ['a', 'b'] 后,返回结果数组
console.log('concat(): ', ['a', 'b'].concat(['c', 'd']));
// indexOf(): 在 arr 中正方向查找 p 首次出现的位置,不存在返回 -1
console.log('indexOf(): ', ['a', 'p', 'p', 'l', 'e'].indexOf('p'));
// lastIndexOf(): 在 arr 中反方向查找 p 首次出现的位置,不存在返回 -1
console.log('lastIndexOf(): ', ['a', 'p', 'p', 'l', 'e'].lastIndexOf('p'));
// map(): 每个元素依次进入函数,返回处理后的新数组
console.log('map(): ', [1, 3, 5, 7, 9].map(e => e * 2));
// filter(): 每个元素依次进入函数,最后返回满足条件的新数组。
console.log('filter(): ', [1, 3, 5, 7, 9].filter(e => e > 3));
// every(): 每个元素依次进入函数,全部元素均满足条件时,最终返回 true。
console.log('every(): ', [1, 3, 5, 7, 9].every(e => e > 3));
// some(): 每个元素依次进入函数,任意一个元素满足条件,直接返回 true。
console.log('some(): ', [1, 3, 5, 7, 9].some(e => e > 6));
// reduce(): 从左到右,每个元素依次进入函数,将每次的结果重新赋值给 a 参数。
console.log('reduce(): ', [1, 3, 5, 7, 9].reduce((a, b) => {
console.log('a =', a, ', b =', b);
return a - b;
}));
// reduceRight(): 从右到左,每个元素依次进入函数,将每次的结果重新赋值给 p1 参数。
console.log('reduceRight(): ', [1, 3, 5, 7, 9].reduceRight((a, b) => {
console.log('a =', a, ', b =', b);
return a - b;
}));
/* ========== 作用于原数组的相关方法 ========== */
// 原数组
let arr;
// unshift(): 在 arr 头部整体添加 ['a', 'b'],返回添加后的数组长度
arr = ['c', 'd'];
console.log('unshift(): ', arr.unshift('a', 'b'), arr.toString());
// shift(): 在 arr 头部删除一个元素,返回被删除的那个元素
arr = ['a', 'b'];
console.log('shift(): ', arr.shift(), arr.toString());
// push(): 在 arr 尾部整体添加 ['c', 'd'],返回添加后的数组长度
arr = ['a', 'b'];
console.log('push(): ', arr.push('c', 'd'), arr.toString());
// pop(): 在 arr 尾部删除一个元素,返回被删除的那个元素
arr = ['a', 'b'];
console.log('pop(): ', arr.pop(), arr.toString());
// splice 添加:从 1 号位开始(包括),删除 0 个并添加 ['b', 'c'],返回被删除的数组
arr = ['a', 'd'];
console.log('splice-insert(): ', arr.splice(1, 0, 'b', 'c'), arr.toString());
// splice 修改:从 1 号位置开始(包括),删除 2 个并添加 ['b', 'c'],返回被删除的数组
arr = ['a', 'x', 'y', 'd'];
console.log('splice-update(): ', arr.splice(1, 2, 'b', 'c'), arr.toString());
// splice 删除:从 1 号位置开始(包括),删除 2 个,返回被删除的数组
arr = ['a', 'x', 'y', 'b'];
console.log('splice-delete(): ', arr.splice(1, 2), arr.toString());
// sort(): 升序排序,默认按照字符串规则排序
arr = [1, 3, 5, 2, 11];
console.log('sort-string(): ', arr.sort(), arr.toString());
console.log('sort-number-asc(): ', arr.sort((a, b) => a - b), arr.toString());
console.log('sort-number-desc(): ', arr.sort((a, b) => b - a), arr.toString());
效果图:

心法:JSON 全称 JavaScript Object Notation,是一种轻量级的,相对无序的数据交换语言,通过 键值对 的格式存储数据,可以单独封装成 JSON 文件(该文件必须是 UTF-8 编码且不支持编写任何注释)。
JSON 对象相关操作:
| 相关代码 | 描述 |
|---|---|
let user = {'k1': v1, 'k2': v2 .. } | 每个键值对的 KEY 值必须用单引号或双引号包裹 |
user['k'] | 按 KEY 取值,不存在返回 undefined |
user['k'] = v | 按 KEY 修改,不存在视为添加操作 |
delete user['k'] | 按 KEY 删除 |
user.hasOwnProperty('k') | true 表示 k 是自己定义的,false 表示 k 是是继承下来的 |
武技:开发 view/基础-JSON 声明.html 页面,并测试 JSON 声明。
脚本:
// JSON 的声明
let user = {'id': 1, 'name': '赵四', 'gender': '男', 'hobby': ['抽烟', '喝酒']};
// 获取 JSON 中的指定数据
console.log(user['id'], user['name'], user['gender'], user['hobby'].toString());
// 在 JSON 中添加一项数据
user['age'] = 28;
console.log('新添加的 JSON 项', user['age']);
// 修改 JSON 中的指定数据
user['age'] = 29;
console.log('修改后的 JSON 项', user['age']);
// 删除 JSON 中的指定数据
delete user['gender'];
console.log('删除后的 JSON 项', user['gender']);
// 遍历 JSON 数据:JSON 数据只能使用 for-in 遍历
for (let key in user) {
// true 表示 k 是自己定义的,false 表示 k 是是继承下来的
if (user.hasOwnProperty(key)) {
console.log(key, user[key]);
}
}
效果图:

心法:解构的意义在于快速从一个 JSON 中获取一个或多个元素的 value 值,解构过程中,多余的变量均赋值 undefined。
| 相关 API | 描述 |
|---|---|
let {id, age} = user | 将 JSON 中的元素按 KEY 名对位赋值给 id 和 age 变量 |
let {password:pwd} = user | 将 JSON 中 password 项赋值给 pwd 变量 |
let {age = 9} = user | 将 JSON 中 age 项赋值给 age 变量,若 age 项不存在则赋默认值 9 |
武技:开发 view/基础-JSON 解构.html 页面,并测试 JSON 解构。
脚本:
let user = {'id': 1, 'name': '赵四', 'gender': '男', 'hobby': ['抽烟', '喝酒']};
// 将 JSON 中的元素按 KEY 名对位赋值给 id 和 name 变量。
let {id, name} = user;
console.log('id', id);
console.log('name', name);
// 将 JSON 中 hobby 赋值 aiHao 变量。
let {hobby: aiHao} = user;
console.log('aiHao', aiHao.toString());
// 将 JSON 中 sal 赋值 sal 变量,若 sal 不存在则赋值 5000。
let {sal = 5000} = user;
console.log('sal', sal);
效果图:

心法:JSON 和数组均可以执行 序列化 和 反序列化 操作。
JSON 常用方法:
| 方法 | 描述 |
|---|---|
let str = JSON.stringify(json) | 将 JSON 对象的 全部属性序列化 为字符串 |
let str = JSON.stringify(json, [id, age]) | 将 JSON 对象的 指定属性序列化 为字符串 |
let json = JSON.parse(str) | 将字符串 反序列 化成 JSON 对象,要求改字符串严格满足 JSON 格式 |
武技:开发 view/基础-JSON 方法.html 页面,并测试 JSON 常用方法。
脚本:
let user = {'id': 1, 'name': '赵四', 'gender': '男', 'hobby': ['抽烟', '喝酒']};
// 全部属性序列化:将 JSON 中全部的数据变为可传输的字符串 JSON -> String
console.log("全部属性序列化", JSON.stringify(user));
// 部分属性序列化:只序列化 name 和 gender 属性
console.log("部分属性序列化", JSON.stringify(user, ['name', 'gender']));
// 反序列化:将 JSON 格式的字符串变为可操作的数据 String -> JSON
console.log("反序列化", JSON.parse('{"name": "赵四", "gender": "男"}'));
效果图:

心法:JS 在客户端存储数据主要有 Cookie、sessionStorage、localStorage 三种方案,三者均遵循同名覆盖规则,且存储的值 仅支持字符串类型,若需要存储对象或数组,需先通过
JSON.stringify()序列化,读取时用JSON.parse()反序列化即可。
心法:cookie 默认是 会话级别 的客户端存值方案(浏览器关闭后失效),若手动设置过期时间(Expires/Max-Age),则在过期时间内,即使关闭浏览器 / 电脑,该 Cookie 值也会持续有效。
核心特性:
相关代码:
| 相关代码 | 描述 |
|---|---|
document.cookie='k=v' | 存值,同名覆盖,默认过期时间为当前会话 |
document.cookie='k=v;expires=过期时间' | 存值,同名覆盖,指定过期时间 |
document.cookie='k=v;expires=-1' | 存储,同名覆盖,指定过期时间为当前会话 |
document.cookie | 返回包含全部 cookie 的字符串,获取单个值需要自行按 ; 切割 |
武技:开发 view/存储-cookie.html 页面,并测试 cookie 操作。
脚本:
// 添加 cookie 值,默认会话级别
document.cookie = `id=1`;
document.cookie = `name=zhaosi`;
// 添加 cookie 值,指定过期时间
let now = new Date();
// 两天后过期
now.setDate(now.getDate() + 2);
document.cookie = `info=亚洲舞王;expires=${now}`;
// 2027-01-01 号过期
document.cookie = `hobby=唱歌;expires=Thu, 1 Jan 2027 00:00:00 GMT`;
// 修改 cookie 值(同名覆盖)
document.cookie = `id=2`;
// 查看所有的 cookie 值
console.log('cookie', document.cookie);
// 遍历所有的 cookie 值
document.cookie.split('; ').forEach(e => {
let arr = e.split('=');
console.log(arr[0], arr[1]);
});
效果图:

在浏览器开发者工具中查看 cookie 过期时间:

心法:封装 CookieUtil 工具文件。
| 方法 | 描述 |
|---|---|
setCookie(key, val, eDay = 1) | 向 Cookie 中添加一条指定条目,默认 1 天后过期 |
getCookie(key) | 从 Cookie 中按 KEY 取值 |
delCookie(key) | 在 Cookie 中按 KEY 删值 |
武技:封装 CookieUtil 工具文件。
util/cookie-util.js 工具文件:/*
* 向 Cookie 中添加一条指定条目
*
* param key 键
* param val 值
* param eDay 几天后过期,缺省或设置为 null 均表示设置会话级别
* return true 设置成功,false 设置失败
*/
function setCookie(key, val, eDay) {
let result = false;
// 空值保护
if (null === key || null === val) return false;
// 设置会话级别的 cookie
if (null === eDay || undefined === eDay) {
document.cookie = `${key}=${val}`;
}
// 设置指定过期时间的 cookie
else {
let now = new Date();
now.setDate(now.getDate() + eDay);
document.cookie = `${key}=${val};expires=${now}`;
}
// 设置成功
result = true;
return result;
}
/*
* 按 key 获取 Cookie 中的指定条目
*
* param key 键
* return 若 key 存在,则返回对应的 val 值,若 key 不存在,则返回 null
*/
function getCookie(key) {
let result = null;
// 空值保护
if (null === key) return null;
// "a=1; b=2;" -> ["a=1", "b=2"] -> ["a", "1"] => "1"
document.cookie.split('; ').forEach(e => {
let kvs = e.split('=');
if (kvs[0] === key) {
result = kvs[1];
}
});
return result;
}
/*
* 按 key 删除 Cookie 中的指定条目
*
* param key 键
* return true 删除成功,false 删除失败
*/
function delCookie(key) {
// 空值保护
if (null === getCookie(key)) return false;
// 设置过期时间为会话级别
return setCookie(key, '', -1);
}
util/cookie-util.js 工具文件:<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>cookie-util</title>
</head>
<body>
<button id="set-btn" type="button">设置 cookie</button>
<button id="get-btn" type="button">获取 cookie</button>
<button id="del-btn" type="button">删除 cookie</button>
<script src="../script/cookie-util.js"></script>
<script>
onload = () => {
let setBtn = document.querySelector('#set-btn');
let getBtn = document.querySelector('#get-btn');
let delBtn = document.querySelector('#del-btn');
setBtn.onclick = () => {
setCookie('name', 'zhaosi');
setCookie('age', '18');
setCookie('gender', '男');
setCookie('hobby', '唱歌', 2);
};
getBtn.onclick = () => console.log(getCookie('name'));
delBtn.onclick = () => delCookie('name');
};
</script>
</body>
</html>
心法:sessionStorage 是 会话级别 的客户端存储方案,仅在当前浏览器标签页的会话中有效,当标签页关闭或浏览器退出后,数据会自动删除,它不支持手动设置过期时间,且 不同标签页间不共享(即使同域名)。
核心特性:
相关代码:
| 相关方法 | 简述 | 详述 |
|---|---|---|
sessionStorage.setItem('k', 'v') | 存值 | 向 sessionStorage 中存储一个键值对 |
sessionStorage.getItem('k') | 取值 | 从 sessionStorage 中按 KEY 取值 |
sessionStorage.removeItem('k') | 删除 | 在 sessionStorage 中按 KEY 删值 |
sessionStorage.clear() | 清空 | 清空 sessionStorage 中的全部内容 |
武技:开发 view/存储-sessionStorage.html 页面,并测试 sessionStorage 操作。
脚本:
// 清空 sessionStorage 中的全部数据
sessionStorage.clear();
// 向 sessionStorage 中存储数据
sessionStorage.setItem('id', '1');
sessionStorage.setItem('username', 'joezhou');
// 从 sessionStorage 中获取数据
console.log('id', sessionStorage.getItem('id'));
console.log('username', sessionStorage.getItem('username'));
// 从 sessionStorage 中删除数据
sessionStorage.removeItem('id');
console.log('id', sessionStorage.getItem('id'));
console.log('username', sessionStorage.getItem('username'));
效果图:

在浏览器开发者工具中查看 sessionStorage 存储情况:

心法:localStorage 是 本地持久化 的客户端存储方案,数据存储在浏览器本地文件中,除非手动删除,否则永久存在(即使关闭浏览器或电脑),它不支持手动设置过期时间,需通过代码模拟过期逻辑。
核心特性:
相关代码:
| 相关方法 | 简述 | 详述 |
|---|---|---|
localStorage.setItem('k', 'v') | 存值 | 向 localStorage 中存储一个键值对 |
localStorage.getItem('k') | 取值 | 从 localStorage 中按 KEY 取值 |
localStorage.removeItem('k') | 删除 | 在 localStorage 中按 KEY 删值 |
localStorage.clear() | 清空 | 清空 localStorage 中的全部内容 |
武技:开发 view/存储-localStorage.html 页面,并测试 localStorage 操作。
脚本:
// 清空 localStorage 中的全部数据
localStorage.clear();
// 向 localStorage 中存储数据
localStorage.setItem('id', '1');
localStorage.setItem('username', 'joezhou');
// 从 localStorage 中获取数据
console.log('id', localStorage.getItem('id'));
console.log('username', localStorage.getItem('username'));
// 从 sessionStorage 中删除数据
localStorage.removeItem('id');
console.log('id', localStorage.getItem('id'));
console.log('username', localStorage.getItem('username'));
效果图:

在浏览器开发者工具中查看 localStorage 存储情况:


微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online