前端三剑客
文章目录
- 前端三剑客
-
- 一:学前端前需要了解的
- 二:HTML
-
- 1:标签和标签属性
- 2:各个常用标签(⭐️⭐️⭐️⭐️⭐️)
-
- [2.1:块级元素(block)](#2.1:块级元素(block))
- 2.2:行内元素(inline)
- 2.3:行内块元素(inline-block)
- 2.4:表格元素(table)
- 2.5:交互和媒体(media)
- 3:常用标签的特殊说明
- 4:HTML其他说明
- 三:CSS
-
- 1:CSS的编写位置
- 2:CSS选择器(⭐️⭐️⭐️⭐️⭐️)
-
- 2.1:通配选择器(*)
- 2.2:元素选择器(元素名)
- 2.3:类选择器(.class的值)
- 2.4:ID选择器(#id值))
- 2.5:交集选择器(选择器1选择器2)
- [2.6:并集选择器(选择器1, 选择器2)](#2.6:并集选择器(选择器1, 选择器2))
- [2.7:后代选择器(选择器1 选择器2)](#2.7:后代选择器(选择器1 选择器2))
- [2.7:子选择器(父 > 子)](#2.7:子选择器(父 > 子))
- 2.8:兄弟选择器(兄长+/~兄弟)
- 2.9:属性选择器([属性名])
- 2.10:伪选择器
- 2.11:选择器的优先级
- 3:CSS三大特性
- 4:CSS中的颜色和长度
-
- 4.1:颜色
- 4.2:长度
-
- 4.2.1:绝对单位
- [4.2.2:相对单位 - 响应式](#4.2.2:相对单位 - 响应式)
- 4.2.3:百分比%
- 4.2.4:calc()方法
- 5:CSS属性(⭐️⭐️⭐️)
- 6:定位和浮动
- 7:布局(⭐️⭐️⭐️⭐️⭐️⭐️)
- 8:SCSS
- 四:JavaScript
-
- 1:JS概述
- 2:JS基础语法
- 3:JS类和对象
-
- 3.1:概述
- 3.2:类的继承
- 3.3:构造函数和原型(⭐️⭐️⭐️⭐️⭐️⭐️)
-
- [3.3.1:构造函数 + new关键字](#3.3.1:构造函数 + new关键字)
- 3.3.2:构造函数原型prototype
- 3.3.3:`proto`对象属性
- 3.3.4:原型链
- 3.3.5:继承
- 3.4:this:当前上下文的主人
- 3.5:闭包和递归
- 3.6:JS常用对象-基本对象
- 3.7:JS常用对象-核心对象
- 4:DOM(⭐️⭐️⭐️⭐️⭐️⭐️)
- 5:BOM
- 6:存储部分
- 7:其他新特性说明
-
- 7.1:变量解构赋值
- 7.2:箭头函数
- 7.3:rest参数和spread扩展运算符
- 7.4:Symbol唯一性值
- 7.5:Promise异步(⭐️⭐️⭐️⭐️)
- [7.6:模块化(export / import)](#7.6:模块化(export / import))
- 7.7:async和await
- 8:后端交互axios
-
- [8.1:axios请求方式((⭐️⭐️⭐️⭐️⭐️)](#8.1:axios请求方式((⭐️⭐️⭐️⭐️⭐️))
- 8.2:axios实例
- 8.3:拦截器
一:学前端前需要了解的
1:C/S架构和B/S架构
C/S架构就是"需要安装的,偶尔更新的,不跨平台"的软件,例如哔哩哔哩,英雄联盟等等,C/S架构安全性高
B/S架构就是网页,"无需安装,无需更新,可跨平台",前端开发主要写的是C/S架构
2:浏览器内核
| 内核名称 | 主要使用浏览器 | 渲染引擎 | JS引擎 | 市场份额 | 特点 |
|---|---|---|---|---|---|
| Blink | Chrome Edge(新版) Opera Brave | Blink | V8 | ~70% | 性能最强 DevTools完善 PWA支持好 |
| WebKit | Safari iOS所有浏览器 macOS Safari | WebCore | JavaScriptCore(Nitro) | ~18% | 移动端优化好 系统级集成 隐私保护强 |
| Gecko | Firefox Tor Browser | Gecko (含WebRender) | SpiderMonkey | ~4% | 开源独立 隐私最强 CSS Grid调试工具好 |
| Trident | IE 6-11(已淘汰) 旧版360/QQ浏览器 | Trident | Chakra(旧) | <1% | 兼容模式需要 已停止维护 |
| EdgeHTML | Edge(2015-2019) | EdgeHTML | ChakraCore | 已废弃 | 微软过渡产品 已转向Blink |
前端后续开发需要关注的是:
| 内核 | CSS前缀 | JS API差异 | 调试工具 | 兼容性处理 |
|---|---|---|---|---|
| Blink/Chrome | -webkit-(部分) |
新API最先支持 | Chrome DevTools | 测试最新特性 |
| WebKit/Safari | -webkit-(多) |
iOS限制多(如PWA) | Safari Web Inspector | iOS真机测试必须 |
| Gecko/Firefox | -moz-(少) |
某些API实现不同 | Firefox DevTools | CSS Grid调试最佳 |
| Trident/IE | -ms-(旧) |
ES6+不支持 | IE F12工具(差) | 需要polyfill |
主攻Chrome(Blink),必测Safari(WebKit),了解Firefox(Gecko),放弃IE(Trident)。
注意这里,在css3布局中,这些css前缀很有用
🚀网页的构成 - 结构(HTML)+ 表现(CSS)+ 行为(JS)
二:HTML
1:标签和标签属性
HTML标签分为单标签和双标签,标签名称不区分大小写,但是最好小写,规范,标签可嵌套
标签属性用于给标签提供附加信息。可以写在起始标签或者单标签中
有些特殊的属性,没有属性名称,只有属性的值(例如disabled)
网页的构造 - HTML标准结构
html
<!-- 这是一个文档说明 -->
<!DOCTYPE html>
<html lang="zh-CN"> <!-- 指定当前的页面是一个简体中文界面 -->
<!-- head标签中的内容不会出现在网页中 -->
<!-- head标签中的title标签可以指定网页的标题 -->
<head>
<title>...</title>
</head>
<!-- 想要呈现在网页中的内容写在body标签中 -->
<body>
....
</body>
</html>
网页中的源代码查看
点击鼠标右键,选择"检查"或者"查看源代码"

- 【查看网页源代码】看到的是:程序员编写的源代码。
- 【检查】看到的是:经过浏览器"处理"后的源代码。
2:各个常用标签(⭐️⭐️⭐️⭐️⭐️)
但是没必要都学会,这些常用的会了就可以了
标签默认的效果不重要,语义化最重要!!!,就是这个标签设计出来是干啥的最重要,效果由css控制调整
标签分为块级标签和行内标签:
-
块级标签:独占一行,排版标签都是块级标签,块级元素中能写行内元素和块级元素
-
行内标签:不独占一行,例如(input, span),行内元素中只能写行内元素
-
行内块元素:行内元素的一种,不换行但可设宽高 基线对齐
| 类型 | 特点 | 是否可设宽高 | 是否独占一行 |
|---|---|---|---|
| 块级元素 | 默认100%宽度 独占一行 | ✅ | ✅ |
| 行内元素 | 宽度由内容决定 不换行 | ❌ | ❌ |
| 行内块元素 | 不换行但可设宽高 基线对齐 | ✅ | ❌ |
| 替换元素 | 内容可被替换 有内在尺寸 | ✅ | ❌ |
| 表格元素 | 特殊布局模型 | ✅ | ✅/❌ |
2.1:块级元素(block)
口诀 - "分节标题列表单,表格表单结构全"
- 分:div ⭐⭐⭐⭐⭐
- 节:section/article/aside/nav
- 标题:h1-h6 ⭐⭐⭐⭐⭐
- 列表:ul/ol/li/dl ⭐⭐⭐⭐⭐
- 表单:form ⭐⭐⭐⭐⭐
- 表格:table/tr/td ⭐⭐⭐⭐⭐
- 结构:header/footer/main
| 标签 | 语义 | 单双标签 | 常用属性 | 说明 |
|---|---|---|---|---|
<div> |
无语义容器 | 双标签 | class, id, style |
最常用布局容器 |
<p> |
段落 | 双标签 | class, id, style |
文本段落 |
<h1> ~ <h6> |
标题(1-6级) | 双标签 | class, id, style |
标题标签,h1最重要 |
<ul> |
无序列表 | 双标签 | class, id, style |
列表容器 |
<ol> |
有序列表 | 双标签 | type, start, reversed |
带序号列表 |
<li> |
列表项 | 双标签 | value, type |
必须放在ul/ol内 |
<table> |
表格 | 双标签 | border, cellpadding, cellspacing |
表格容器 |
<tr> |
表格行 | 双标签 | - | 表格中的行 |
<th> |
表头单元格 | 双标签 | colspan, rowspan |
表格头部单元格 |
<td> |
表格数据单元格 | 双标签 | colspan, rowspan |
表格数据单元格 |
<form> |
表单 | 双标签 | action, method, target |
表单容器 |
<header> |
页面/区块头部 | 双标签 | class, id |
HTML5语义标签 |
<footer> |
页面/区块尾部 | 双标签 | class, id |
HTML5语义标签 |
<nav> |
导航 | 双标签 | class, id |
HTML5语义标签 |
2.2:行内元素(inline)
口诀 - "文本修饰链媒体,代码引用注音义"
- 文本:span ⭐⭐⭐⭐⭐
- 修饰:em/strong/i/b/u
- 链:a(实际上是inline)⭐⭐⭐⭐⭐
- 媒体:img(特殊)⭐⭐⭐⭐⭐
- 代码:code/kbd/samp
- 引用:q/cite
- 注音:ruby/rt
| 标签 | 语义 | 单双标签 | 常用属性 | 说明 |
|---|---|---|---|---|
<span> |
无语义容器 | 双标签 | class, id, style |
最常用的行内容器 |
<a> |
链接 | 单标签 | href, style |
链接跳转标签 |
2.3:行内块元素(inline-block)
口诀 - "表单控件多媒体,按钮输入选择器"
- 表单控件:input全系列 ⭐⭐⭐⭐⭐
- 按钮:button ⭐⭐⭐⭐⭐
- 输入:textarea ⭐⭐⭐⭐⭐
- 选择:select/option ⭐⭐⭐⭐⭐
- 媒体:img/video/audio/canvas ⭐⭐⭐⭐⭐
| 标签 | 语义 | 单双标签 | 常用属性 | 说明 |
|---|---|---|---|---|
<img> |
图像 | 单标签 | src, alt, width, height |
行内替换元素 |
<input> |
输入控件 | 单标签 | type, name, value, placeholder |
类型多样,都是inline-block |
<button> |
按钮 | 双标签 | type, disabled, name |
可包含文本或HTML |
<textarea> |
多行文本输入 | 双标签 | rows, cols, placeholder |
多行文本框 |
<select> |
下拉选择 | 双标签 | name, multiple, size |
下拉列表 |
<option> |
下拉选项 | 双标签 | value, selected, disabled |
select的子元素 |
<label> |
表单标签 | 双标签 | for |
关联表单控件 |
<progress> |
进度条 | 双标签 | value, max |
HTML5进度指示器 |
<video> |
视频 | 双标签 | src, controls, width, height |
HTML5视频播放器 |
<audio> |
音频 | 双标签 | src, controls |
HTML5音频播放器 |
<iframe> |
内联框架 | 双标签 | src, width, height |
嵌入其他页面 |
2.4:表格元素(table)
| 标签 | 语义 | 块级/行内 | 默认样式 | 特殊作用 |
|---|---|---|---|---|
<table> |
表格容器 | 块级 | 无特殊 | 整个表格的根元素 |
<thead> |
表头区域 | 块级 | 通常加粗居中 | 语义化表头,打印时每页重复 |
<tbody> |
表格主体 | 块级 | 正常文本 | 主要数据区域,可以有多个 |
<tfoot> |
表尾区域 | 块级 | 可能加粗 | 汇总行,打印时每页底部重复 |
<tr> |
表格行 | 块级 | 水平排列单元格 | 组织水平方向的数据 |
<th> |
表头单元格 | 表格单元格 | 加粗、居中 | 列或行的标题,有scope属性 |
<td> |
表格数据单元格 | 表格单元格 | 正常左对齐 | 实际数据单元格 |
<table> (表格容器)
├── <caption> (表格标题 - 可选)
├── <colgroup> (列分组 - 可选)
├── <thead> (表头区域 - 语义化)
│ └── <tr> (表头行)
│ ├── <th> (标题单元格1)
│ ├── <th> (标题单元格2)
│ └── <th> (标题单元格3)
├── <tbody> (表格主体 - 可以有多个)
│ ├── <tr> (数据行1)
│ │ ├── <td> (数据单元格1)
│ │ ├── <td> (数据单元格2)
│ │ └── <td> (数据单元格3)
│ └── <tr> (数据行2)
│ ├── <td> (数据单元格4)
│ ├── <td> (数据单元格5)
│ └── <td> (数据单元格6)
└── <tfoot> (表尾区域 - 可选)
└── <tr> (汇总行)
├── <td> (汇总数据1)
├── <td> (汇总数据2)
└── <td> (汇总数据3)
2.5:交互和媒体(media)
| 标签 | 语义 | 单双标签 | 常用属性 | 说明 |
|---|---|---|---|---|
<details> |
细节控件 | 双标签 | open |
可展开/折叠的内容 |
<summary> |
details标题 | 双标签 | - | details的可见标题 |
<dialog> |
对话框 | 双标签 | open |
HTML5对话框 |
<menu> |
菜单列表 | 双标签 | type |
命令菜单 |
<menuitem> |
菜单项 | 单标签 | type, label |
menu的命令项 |
3:常用标签的特殊说明
| 标签 | 必须属性 | 推荐属性 | 禁止/避免 | 特殊说明 |
|---|---|---|---|---|
<img> |
src, alt |
width, height, loading |
无alt | alt即使为空也要写 |
<a> |
href(内部锚点可空) |
target="_blank"时加rel |
href="javascript:..." |
外部链接要安全 |
<input> |
type, name(表单时) |
id, placeholder |
无label | radio/checkbox需分组 |
<form> |
- | method, action, enctype |
无提交按钮 | 文件上传需enctype |
<table> |
- | scope(th用) |
用表格布局 | 要有caption或aria-label |
<label> |
for或包裹控件 |
- | 无关联控件 | 提升可访问性 |
<button> |
type |
disabled, aria-label |
无type | type默认submit(表单内) |
<meta> |
charset或name+content |
viewport |
- | 移动端必须viewport |
<iframe> |
title |
sandbox, allow |
无title | 安全考虑加sandbox |
<video> |
- | controls, preload |
无后备内容 | 提供多个source格式 |
<textarea> |
name |
rows, cols, maxlength |
- | 用CSS控制大小更佳 |
<select> |
name |
multiple, size |
无option | 选项用<option> |
<option> |
value |
selected, disabled |
- | 显示文本在标签间 |
4:HTML其他说明
4.1:字符实体
所谓字符实体就是用一个形式表示某种符号,HTML认这种符号。
字符实体由三部分组成:一个&和一个实体名称(或者一个#和一个实体编号),最后加上一个分号;
4.2:全局属性
常见的全局属性(大部分的标签都可以写的属性叫做全局属性)
| 属性 | 描述 |
|---|---|
id |
为元素指定唯一标识符。 |
class |
为元素指定一个或多个类名(用空格分隔)。 |
style |
为元素指定行内CSS样式。 |
title |
提供元素的额外提示信息(鼠标悬停时显示)。 |
lang |
定义元素内容的语言代码(如 en、zh-CN)。 |
dir |
设置文本方向:ltr(从左到右)或 rtl(从右到左)。 |
hidden |
隐藏元素(布尔属性,无需值)。 |
可访问性和交互
| 属性 | 描述 |
|---|---|
tabindex |
控制Tab键导航顺序(0为自然顺序,-1为可聚焦但不可通过Tab导航)。 |
accesskey |
为元素指定键盘快捷键(如 accesskey="s")。 |
contenteditable |
允许用户编辑元素内容(布尔属性)。 |
spellcheck |
启用拼写检查(true/false)。 |
draggable |
控制元素是否可拖放(true/false)。 |
role |
定义元素的ARIA角色(增强可访问性)。 |
aria-* |
ARIA属性,用于描述元素的无障碍信息(如 aria-label)。 |
4.3:meta元信息
html
<!-- 配置字符编码 -->
<meta charset="utf-8">
<!-- 针对IE浏览器的兼容性配置(IE8+ 尝试使用 Edge内核渲染) -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- 针对移动端的配置 -->
<meta name="viewport"content="width=device-width,initial-scale=1.0">
<!-- 配置网页关键字 -->
<meta name="keywords" content="8-12个以英文逗号隔开的单词/词语">
<!-- 配置网页的描述信息 -->
<meta name="description" content="80字以内的一段话,与网站内容相关">
<!-- 针对搜索引擎爬虫配置, robots协议 -->
<meta name="robots" content="?">
<!-- 配置网页作者 -->
<meta name="author" content="tony">
<!-- 配置网页生成工具 -->
<meta name="generator" content="Visual Studio Code">
<!-- 配置网页版权信息 -->
<meta name="copyright" content="2023-2027©版权所有">
<!-- 配置网页自动刷新 -->
<!-- 10s钟后去哪? 如果后面不配置网址,将会原地刷新 -->
<meta http-equiv="refresh" content="10;url=http://www.baidu.com">
所以项目中,html的header一般是这样的...
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<!-- 字符编码(必须放在最前面) -->
<meta charset="UTF-8">
<!-- 视口设置(响应式必备) -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 浏览器兼容性 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- 页面标题 -->
<title>页面标题 | 公司/品牌名</title>
<!-- 页面描述(SEO重要) -->
<meta name="description" content="150-160字符的页面描述">
<!-- 引入CSS -->
<link rel="stylesheet" href="styles/main.css">
<!-- 网站图标 -->
<link rel="icon" href="/favicon.ico" type="image/x-icon">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
</head>
<body>
...
</body>
</html>
4.4:父、子、祖先、后代、兄弟
其实就是族谱树的概念延伸...
父元素:直接包裹某一个元素的元素,就是该元素的父元素
子元素:被父元素直接包含的元素叫做子元素
html
<!-- div是h1和ul的父元素,不是li的父元素 -->
<!-- ul是li的父元素 -->
<!-- 反过来同理 -->
<!-- h1和ul是div的子元素,li不是div的子元素 -->
<!-- li是ul的子元素 -->
<div>
<h1>你好,这里是前端三剑客</h1>
<ul>
<li>html</li>
<li>css</li>
<li>javascript</li>
</ul>
</div>
祖先元素:父亲的父亲...,一直往外找,都是祖先,父元素也是祖先的一种,但是一般还是称呼为父元素
后代元素:儿子的儿子...,一直往里找,都是后代,子元素也是后代的一种,但是一般还是称呼为子元素
兄弟元素,具有相同父元素的元素,互为兄弟元素
html
<!-- div就是li的祖先,li就是div的后代 -->
<!-- h1和ul是兄弟元素,因为他们有相同的父元素div, 这几个li是兄弟元素,因为他们有相同的父元素ul -->
<div>
<h1>你好,这里是前端三剑客</h1>
<ul>
<li>html</li>
<li>css</li>
<li>javascript</li>
</ul>
</div>
三:CSS
1:CSS的编写位置
优先级规则:
行内样式 > 内部样式 = 外部样式
行内样式
- style属性的值不能随便写,写要符合CSS语法规范,是
key:value;的形式。 - 行内样式表,只能控制当前标签的样式,对其他标签无效。
不推荐大量使用,只有对当前元素添加简单样式时,才偶尔使用。
html
<body>
<h1 style="color: rebeccapurple; font-size: 99px;"></h1>
</body>
内部样式
写在html页面内部,将所有的CSS代码提取出来,单独放在<style>标签中
只有当前文件能用,一般放在header中
html
<head>
<meta charset="UTF-8">
<title>Document</title>
<!-- 内部样式,控制当前页面的指定选择器的样式信息 -->
<style>
h1 {
color: rebeccapurple;
font-size: 40px;
}
</style>
</head>
外部样式
写在单独.css文件中,随后在HTML文件中引入使用。
link必须在header中,rel指定的是引入的文档与当前文档之间的关系,都写stylesheet, href是文件相对位置
实际开发中,几乎都使用外部样式,这是最推荐的使用方式!
html
<head>
<!-- 字符编码(必须放在最前面) -->
<meta charset="UTF-8">
<!-- 引入CSS -->
<link rel="stylesheet" href="styles/main.css">
<!-- 网站图标 -->
<link rel="icon" href="/favicon.ico" type="image/x-icon">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
</head>
2:CSS选择器(⭐️⭐️⭐️⭐️⭐️)
CSS语法由两部分构成:
- 选择器:找到要添加样式的元素。
- 声明块:设置具体的样式(声明块是由一个或多个声明组成的),声明的格式为:属性名:属性值;
所以成功给指定元素渲染样式,选择器的指定至关重要
2.1:通配选择器(*)
就是*, 可以匹配所有的HTML元素
看似鸡肋,但是在清除样式的时候,会有很大帮助
2.2:元素选择器(元素名)
无法实现差异化设置,只能对一类元素统一设置样式
css
标签名 {
属性名:属性值;
属性名:属性值;
}
/* 选中所有的p元素,设置其颜色是blue, 设置其字体大小是16px */
p {
color: blue;
font-size: 16px;
}
2.3:类选择器(.class的值)
根据元素的class值,来选中某些元素,class就是我们给元素规定的特征
一个html标签中不可有多个class声明,但一个class属性中可以由多个值
html
<!--该写法错误,元素的属性不能重复,后写的会失效-->
<h1 class="speak" class="big">你好啊</h1>
<!--该写法正确,class属性,能写多个值-->
<h1 class="speak big">你好啊</h1>
css
.[class的值] {
属性名:属性值;
属性名:属性值;
}
/* 选中所有class值为speak的元素 */
.speak {
color: red;
}
/* 选中所有class值为answer的元素 */
.answer {
color: blue;
}
2.4:ID选择器(#id值)
- 一个元素只能拥有一个id属性,多个元素的id属性值不能相同(双唯一性)
- 一个元素可以同时拥有id和class属性。
css
#ID值 {
属性名:属性值;
属性名:属性值;
}
/* 选中id值为earthy的那个元素 */
#earthy {
color: red;
font-size: 60px;
}
2.5:交集选择器(选择器1选择器2)
选中同时符合多个条件的元素,就是选择器和选择器之间是并且的关系
- 有标签名,标签名必须写在前面。
- id选择器、理论上可以作为交集的条件,但实际应用中几乎不用。因为没有意义。
- 交集选择器中不可能出现两个元素选择器,因为一个元素,不可能即是p元素又是 span 元素。
- 用的最多的交集选择器是:元素选择器配合类名选择器,例如:
p.beauty。
css
选择器1选择器2选择器n {
属性名:属性值;
属性名:属性值;
}
/* 选中:类名为beauty的p元素,为此种写法用的非常多!!!!*/
p.beauty {
color: blue;
}
/* 选中:类名包含rich和beauty的元素 */
.rich.beauty {
color: green,
}
2.6:并集选择器(选择器1, 选择器2)
选中多个选择器对应的元素,满足选择器之一就可以,又叫分组选择器
-
并集选择器,我们一般竖着写。
逗号可以理解成或者 -
任何形式的选择器,都可以作为并集选择器的一部分。
-
并集选择器,通常用于集体声明,可以缩小样式表体积。
css
选择器1, 选择器2, 选择器n {
属性名:属性值;
属性名:属性值;
}
/* 选中id为peiqi,或类名为rich,或类名为beauty的元素 */
#peiqi,
.rich,
.beauty {
font-size: 40px;
background-color: skyblue;
width: 200px;
}
2.7:后代选择器(选择器1 选择器2)
选中指定元素中,符合要求的后代元素(儿子也算后代),只会改变最内一层(选中层)的样式。
- 选择器之间,用
空格隔开,空格可以理解为:"xxx中的",其实就是后代的意思。
css
选择器1 选择器2 选择器3 ... 选择器n {
属性名:属性值;
属性名:属性值;
}
/* 选中ul中的所有li,注意不会改变ul的样式 */
ul li{
color:red;
}
/* 选中ul 中所有 li中的 a */
ul li a {
color:orange;
}
/* 选中类名为subject(class=subject)元素中的所有li */
.subject li {
color:blue;
}
/* 选中类名为subject元素中的所有类名为front-end的li */
.subject li.front-end {
color:blue;
}
2.7:子选择器(父 > 子)
选中指定元素中,符合要求的子元素(儿子元素)。(先写父,再写子)
选择器之间,用>隔开,>可以理解为:"xxx 的子代",其实就是儿子的意思。

2.8:兄弟选择器(兄长+/~兄弟)
⚠️ 只能选到后面的兄弟,前面的选不到
相邻兄弟选择器 -> 相邻兄弟选择器(+)选择的是前一个元素后面紧接着的兄弟元素
css
前一个元素 + 紧接着的兄弟元素 {
属性: 值;
/* 更多样式属性 */
}
通用兄弟选择器 -> 通用兄弟选择器(~)选择的是前一个元素后面所有的兄弟元素
css
前一个元素 ~ 兄弟元素 {
属性: 值;
/* 更多样式属性 */
}
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
/* + 兄弟元素, 在h2后面的第一个p标签都执行这个样式,也就是第一段 */
div.container > h2 + p {
color: red;
}
/* ~ 兄弟元素, 在class=inner的div后面的兄弟p标签都执行这个样式,也就是第二段和第三段 */
div.inner ~ p {
color: blue;
margin-left: 20px;
}
</style>
</head>
<body>
<div class="container">
<h2>标题</h2>
<p>第一段</p>
<div class="inner">一个div</div>
<p>第二段</p>
<p>第三段</p>
</div>
</body>
</html>

2.9:属性选择器([属性名])
分为考虑属性的值和不考虑属性的值,用[]包裹
[属性名]选中具有某个属性的元素。[属性名 = "值"]包含某个属性,且属性值等于指定值的元素。[属性名 ^= "值"]选中包含某个属性,且属性值以指定的值开头的元素。[属性名 $= "值"]选中包含某个属性,且属性值以指定的值结尾的元素。[属性名 *= "值"]选择包含某个属性,属性值包含指定值的元素。
css
/* 不考虑属性的值 */
[attr] {
/* 属性名attr的样式 */
}
/*考虑属性值*/
/* 选择所有具有 "attr" 属性且值为 "value" 的元素 */
[attr="value"] {
color: red;
}
/* 选择所有具有 "attr" 属性且值以 "value" 开头的元素 */
[attr^="value"] {
color: green;
}
/* 选择所有具有 "attr" 属性且值以 "value" 结尾的元素 */
[attr$="value"] {
color: orange;
}
/* 选择所有具有 "attr" 属性且值包含 "value" 的元素 */
[attr*="value"] {
color: purple;
}
2.10:伪选择器
伪选择器分为伪类选择器和伪元素选择器,下面这两个视频讲的很好:
注意最常用的四个伪类选择器的放置顺序LVHA -> :link :visited :hover :active
2.11:选择器的优先级
原则:越是能够精确定位元素的,优先级越高
!important:无需多言,打遍天下无敌手,但是不要乱用- 内联样式style
- id选择器
- 伪类选择器
- 属性选择器 = class选择器
- 元素选择器
- 通配选择器:非常低,因为不能够定位元素,通用配置
- 继承属性...CSS三大特性之一
相同的选择器,后面声明的比前面声明的优先级高,相同属性后者覆盖前者;
在VS Code或者WebStorm中,可以通过特异性查看优先级:

特异性用于判断多个 CSS 规则应用于同一个元素时,哪个规则的样式应该生效。
特异性越高,优先级越高。
特异性值是一个四元组 (a, b, c, d),但在大多数情况下,a 值通常为 0,因此通常只显示 (b, c, d)。
- a:内联样式。直接在 HTML 元素的 style 属性中定义的样式。
- b:ID 选择器(#id)。使用 #id 选择器。
- c:类选择器(.class_name)、属性选择器([alt])和伪类选择器(s1:xxx)
- d:元素选择器(tag_name)和伪元素选择器(s1::xxx)。使用 div、p::before 等选择器。
所以上面的(1, 1, 0)表示有ID选择器,有类选择器,可以说优先级非常的高了
3:CSS三大特性
层叠性,继承性和优先级
| 特性 | 核心思想 | 比喻 |
|---|---|---|
| 层叠性 | 冲突时如何解决 | 多人指挥时听谁的 |
| 继承性 | 样式自动传递 | 家族特征传承 |
| 优先级 | 权重决定胜负 | 身份证比学生证权威 |
3.1:层叠性
当多个 CSS 规则作用于同一个元素时,会根据一定的规则进行"层叠",最终确定使用哪个样式
层叠规则(按顺序判断):
- 来源顺序:相同权重的规则,后出现的覆盖先出现的(就近原则)
- 优先级:不同权重的规则,优先级高的生效(详见下文优先级部分)
- 重要性:
!important声明具有最高优先级
css
/* 示例:层叠性 */
p {
color: blue;
}
/* 后定义的相同选择器会覆盖前一个 */
p {
color: red; /* 最终生效 */
}
3.2:继承性
某些 CSS 属性会被子元素自动继承,无需重复定义。
就像现实生活中的遗传一样,孩子自动获得父母的某些特征,但不是全部特征!
可以继承的属性一般都是文本相关的属性:font-family, color, line-height, text-align...
不可继承的属性一般都是布局和盒模型相关的属性:width, height, margin, padding, border...
使用inherit可以强制继承
css
/* 场景1:统一网站字体 */
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto;
/* 所有子元素都自动用这个字体,不用重复写 */
}
/* 场景2:深色模式文字颜色 */
body.dark-mode {
color: #f0f0f0; /* 一次设置,全局生效 */
}
/* 场景3:统一链接颜色 */
article {
color: #333;
}
article a {
color: inherit; /* 继承 article 的灰色,而不是默认蓝色 */
text-decoration: underline;
}
3.3:优先级
就是上面说的选择器的优先级,当多个选择器作用于同一元素时,通过权重计算决定哪个规则生效。
css
/* 示例:优先级比较 */
#content p.text { /* 权重: 0,1,0,1 (ID + 元素) */
color: blue;
}
div#content p { /* 权重: 0,1,0,2 (ID + 两个元素) */
color: green; /* 生效:0,1,0,2 > 0,1,0,1 */
}
p { /* 权重: 0,0,0,1 */
color: red !important; /* !important 优先级最高 */
}
4:CSS中的颜色和长度
4.1:颜色
4.1.1:颜色标准名
CSS 定义了一系列标准的颜色名,可以直接在 CSS 代码中使用。这些颜色名是预定义的,不需要额外的解释或转换
直接参考网址:https://developer.mozilla.org/en-US/docs/Web/CSS/named-color
4.1.2:HEX,RGB,RGBA,HEXA
RGB代表"红-绿-蓝"(Red-Green-Blue),它是一种加色模型,通过混合不同强度的红色、绿色和蓝色光来创建各种颜色
在RGB模型中,每个颜色通道的值范围从0到255,其中0表示该颜色通道没有光(即最暗),255表示该颜色通道的光是最亮的
RGBA是在RGB的基础上增加了一个Alpha通道,代表"红-绿-蓝-Alpha"。Alpha通道用于控制颜色的透明度
Alpha取值范围是0.0到1.0,其中0.0表示完全透明,1.0表示完全不透明
十六进制法(HEX)和RGB同理。只不过是十六进制的,HEXA和RGBA同理。
css
color: red; /* 颜色名 */
color: #FF0000; /* 十六进制 */
color: rgb(255, 0, 0); /* RGB */
color: rgba(255, 0, 0, 0.5); /* RGB + 透明度 Alpha */
color: #FF000080; /* 8位十六进制(带透明度) */
4.1.3:HSL,HSLA
HSL是一种将RGB色彩模型中的点在圆柱坐标系中的表示法。
https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/hsl#specifications
这两种表示法试图做到比基于笛卡尔坐标系的几何结构RGB更加直观。是运用最广的颜色系统之一。
HSL 代表色调(Hue)、饱和度(Saturation)和亮度(Lightness)。
HSL 颜色模型是圆柱坐标中的一个圆锥形色彩空间,它将颜色描述为三个维度:hsl(H, S%, L%)
- H(色调,Hue):色调是色彩的基本属性,它决定了颜色的种类,比如红色、黄色或蓝色。在 HSL 中,色调用角度来表示,范围是 0° 到 360°。其中,0° 或 360° 表示红色,120° 表示绿色,240° 表示蓝色
- S(饱和度,Saturation):饱和度是指颜色的纯度,它描述了颜色中灰色成分的多少。饱和度的范围是 0% 到 100%,其中 0% 表示灰色(没有颜色),100% 表示完全饱和的颜色
- L(亮度,Lightness):亮度描述了颜色的明亮程度。亮度的范围也是 0% 到 100%,其中 0% 表示黑色,50% 表示正常亮度,100% 表示白色
HSLA 是 HSL 的扩展,增加了 Alpha 通道,用于表示颜色的透明度。A介于0.0~1.0
css
/* 语法:hsl(色相, 饱和度, 明度) */
.element {
color: hsl(0, 100%, 50%); /* 纯红色 */
color: hsl(120, 100%, 50%); /* 纯绿色 */
color: hsl(240, 100%, 50%); /* 纯蓝色 */
}
/*
色相(Hue):0-360度色轮(0红,120绿,240蓝)
饱和度(Saturation):0%-100%(0%灰色,100%纯色)
明度(Lightness):0%-100%(0%黑,50%正常,100%白)
*/
4.1.4:currentColor关键字
该关键字用来表示引用当前元素的 color 值
css
/* 引用当前元素的 color 值 */
.element {
color: #3498db;
border: 2px solid currentColor; /* 边框使用文字颜色 */
box-shadow: 0 0 10px currentColor;
}
4.2:长度
像素是相对单位,就是电脑屏幕的小正方形分割,像素越高的情况下,越清晰
| 单位 | 说明 |
|---|---|
| 像素px | 基于屏幕分辨率的固定单位,通常用于网页设计 |
| 百分比% | 相对于父元素的百分比,常用于响应式设计 |
| em | 相对于当前元素的字体大小。如果当前元素的字体大小是16px,那么1em等于16px |
CSS3新增了许多长度单位,这些单位提供了更多的灵活性和精确性,适用于不同的布局需求
| 单位 | 说明 |
|---|---|
| rem | rem是相对于根元素(通常是<html>)的字体大小。假设根元素的字体大小为16px,则1rem等于16px |
| vw | vw是视口宽度的百分比。1vw等于视口宽度的1% |
| vh | vh是视口高度的百分比。1vh等于视口高度的1% |
| vmin | vmin是视口宽度和高度中较小值的百分比。1vmin等于视口较小尺寸的1% |
| vmax (viewport maximum) | vmax是视口宽度和高度中较大值的百分比。1vmax等于视口较大尺寸的1% |
4.2.1:绝对单位
像素和物理单位
css
/* 像素(最常用) */
.element { width: 100px; }
/* 物理单位(打印时使用) */
.element {
width: 2.54cm; /* 厘米 */
width: 25.4mm; /* 毫米 */
width: 1in; /* 英寸 = 2.54cm */
width: 72pt; /* 点 = 1/72英寸 */
width: 6pc; /* 派卡 = 12点 */
}
4.2.2:相对单位 - 响应式
相对于字体的大小
css
/* em:相对于当前元素的字体大小 */
.container {
font-size: 16px;
padding: 2em; /* 2em = 2倍当前字体的大小 = 32px (16 × 2) */
}
.inner {
font-size: 0.8em; /* 相对于父元素的0.8倍字体大小 = 16 * 0.8 = 12.8px */
margin: 1.5em; /* 相对于自己的字体大小的1.5倍 = 12.8 * 1.5 = 19.2px */
}
/* rem:相对于根元素(html)的字体大小 */
html { font-size: 16px; }
.element {
font-size: 1rem; /* 16 * 1 = 16px */
padding: 2rem; /* 16 * 2 = 32px */
margin: 0.5rem; /* 16 * 0.5 = 8px */
}
/* ex:相对于当前字体的小写x高度 */
/* ch:相对于数字0的宽度 */
相对于视口(Viewport)
css
/* vw:视口宽度的1% */
.element {
width: 50vw; /* 视口宽度的50% */
}
/* vh:视口高度的1% */
.element {
height: 100vh; /* 满屏高度 */
}
/* vmin:vw和vh中较小的那个 */
.element {
font-size: 5vmin; /* 在手机竖屏时相对于高度 */
}
/* vmax:vw和vh中较大的那个 */
.element {
font-size: 5vmax; /* 在手机横屏时相对于宽度 */
}
4.2.3:百分比%
是父元素的百分之多少?
css
.parent {
width: 500px;
height: 300px;
}
.child {
width: 50%; /* 250px (父元素宽度的50%) */
height: 50%; /* 150px (父元素高度的50%) */
padding: 10%; /* 水平方向相对于父元素宽度,垂直方向相对于父元素宽度(注意!) */
}
4.2.4:calc()方法
calc() 是 CSS3 中的计算函数,允许在声明 CSS 属性值时执行数学运算
- ✅ 混合单位计算 :如
px + %、rem + vw - ✅ 四则运算 :支持
+ - * / - ✅ 动态计算:运行时计算,响应视口变化
- ✅ 任何数值属性 :可用在
width、height、margin、font-size等
常用在响应式布局中的平滑过渡
css
.container {
/* 宽度 = 100%视口 - 左右各20px边距 */
width: calc(100vw - 40px);
/* 高度 = 视口高度 - 头部80px - 底部60px */
height: calc(100vh - 80px - 60px);
}
/* 字体响应式公式 */
font-size: calc([最小px] + ([最大px] - [最小px]) * ((100vw - [最小视口px]) / ([最大视口px] - [最小视口px])));
/* 示例:在 375px~1440px 视口中,字体从 16px~20px */
font-size: calc(16px + (20 - 16) * ((100vw - 375px) / (1440 - 375)));
5:CSS属性(⭐️⭐️⭐️)
CSS属性繁杂,有现成的网站已经分好类,现去搜查
6:定位和浮动
6.1:盒子模型
每个HTML元素都是一个"盒子",就像快递包裹一样有层层包装:

6.1.1:两种盒子模型
传统盒子模型 -
content-boxwidth指定的是内容区的宽度,然后根据各个部分推算总宽度
css
div {
box-sizing: content-box; /* 默认值 */
width: 200px;
padding: 20px;
border: 5px solid black;
margin: 10px;
}
总宽度 = width + padding左右 + border左右 + margin左右
总高度 = height + padding上下 + border上下 + margin上下
例子计算:
width: 200px
padding: 20px(左右各20,共40px)
border: 5px(左右各5,共10px)
margin: 10px(左右各10,共20px)
实际占用宽度 = 200 + 40 + 10 = 250px(不含margin)
整个盒子占位宽度 = 250 + 20 = 270px(含margin)
边框盒子(现代开发都用这个) -
broder-boxwidth指定的是总宽度,通过padding和broder反推内容区的宽度
css
/* 现代开发推荐用这个! */
div {
box-sizing: border-box; /* ✅ 推荐! */
width: 200px;
padding: 20px;
border: 5px solid black;
}
width = 内容区 + padding + border
你设置的width就是最终可视宽度!
例子:
width: 200px(总宽度固定)
padding: 20px(左右各20,占40px)
border: 5px(左右各5,占10px)
内容区宽度自动计算 = 200 - 40 - 10 = 150px
6.1.2:盒子属性
- padding:内部填充,背景色会显示
- margin:外部间隔,完全透明
- border:中间边界,样式丰富
- box-sizing: border-box:现代开发必用!
尺寸相关属性
| 属性 | 作用 | 常用值 | 示例 |
|---|---|---|---|
width |
设置内容区宽度 | 长度值、百分比、auto | width: 200px; |
height |
设置内容区高度 | 长度值、百分比、auto | height: 100px; |
min-width |
最小宽度 | 长度值、百分比 | min-width: 300px; |
max-width |
最大宽度 | 长度值、百分比 | max-width: 1200px; |
min-height |
最小高度 | 长度值、百分比 | min-height: 200px; |
max-height |
最大高度 | 长度值、百分比 | max-height: 500px; |
内边距 Padding
| 值的数量 | 含义 | 示例 |
|---|---|---|
| 1个值 | 四边相同 | padding: 20px; |
| 2个值 | 上下 | 左右 | padding: 10px 20px; |
| 3个值 | 上 | 左右 | 下 | padding: 10px 20px 15px; |
| 4个值 | 上 | 右 | 下 | 左 | padding: 10px 15px 20px 25px; |
| 属性 | 作用 | 常用值 | 示例 |
|---|---|---|---|
padding |
简写属性(四边) | 1-4个长度值 | padding: 20px; |
padding-top |
上内边距 | 长度值 | padding-top: 10px; |
padding-right |
右内边距 | 长度值 | padding-right: 15px; |
padding-bottom |
下内边距 | 长度值 | padding-bottom: 10px; |
padding-left |
左内边距 | 长度值 | padding-left: 15px; |
边框 - border
| 属性 | 作用 | 常用值 | 示例 |
|---|---|---|---|
border |
边框简写 | 宽度 样式 颜色 |
border: 2px solid red; |
border-width |
边框宽度 | 长度值 | border-width: 2px; |
border-style |
边框样式 | 见下表 | border-style: dashed; |
border-color |
边框颜色 | 颜色值 | border-color: #333; |
单边边框属性
| 属性 | 作用 | 示例 |
|---|---|---|
border-top |
上边框 | border-top: 1px solid red; |
border-right |
右边框 | border-right: 2px dashed blue; |
border-bottom |
下边框 | border-bottom: 3px dotted green; |
border-left |
左边框 | border-left: 1px solid #ccc; |
border-top-width |
上边框宽度 | border-top-width: 3px; |
border-top-style |
上边框样式 | border-top-style: solid; |
border-top-color |
上边框颜色 | border-top-color: red; |
圆角边框
| 属性 | 作用 | 示例 |
|---|---|---|
border-radius |
边框圆角 | border-radius: 10px; |
border-top-left-radius |
左上圆角 | border-top-left-radius: 5px; |
border-top-right-radius |
右上圆角 | border-top-right-radius: 5px; |
border-bottom-right-radius |
右下圆角 | border-bottom-right-radius: 5px; |
border-bottom-left-radius |
左下圆角 | border-bottom-left-radius: 5px; |
外边距 - margin
| 属性 | 作用 | 常用值 | 示例 |
|---|---|---|---|
margin |
简写属性(四边) | 1-4个长度值、auto | margin: 20px; |
margin-top |
上外边距 | 长度值、auto | margin-top: 10px; |
margin-right |
右外边距 | 长度值、auto | margin-right: 15px; |
margin-bottom |
下外边距 | 长度值、auto | margin-bottom: 20px; |
margin-left |
左外边距 | 长度值、auto | margin-left: 15px; |
| 值的数量 | 含义 | 示例 |
|---|---|---|
| 1个值 | 四边相同 | margin: 20px; |
| 2个值 | 上下 | 左右 | margin: 10px auto;(居中) |
| 3个值 | 上 | 左右 | 下 | margin: 10px 20px 15px; |
| 4个值 | 上 | 右 | 下 | 左 | margin: 10px 15px 20px 25px; |
常见样式
css
.card {
box-sizing: border-box; /* 重要! */
width: 300px;
padding: 20px; /* 内边距 */
border: 1px solid #ddd; /* 边框 */
border-radius: 8px; /* 圆角 */
margin: 15px; /* 外边距 */
background-color: white;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.button {
box-sizing: border-box;
display: inline-block;
padding: 10px 24px; /* 上下10px,左右24px */
border: 2px solid #007bff;
border-radius: 4px;
margin: 0 10px 10px 0; /* 上0 右10 下10 左0 */
background-color: #007bff;
color: white;
}
/* 常用全局重置 */
* {
box-sizing: border-box; /* 统一盒子模型 */
margin: 0; /* 清除默认外边距 */
padding: 0; /* 清除默认内边距 */
}
6.2:定位position
有五种定位方式
sticky = relative + fixed = 偏移到一定位置之后固定

| 属性 | 作用 | 值 |
|---|---|---|
top right bottom left |
定位偏移 | 长度、百分比 |
z-index |
层叠顺序 | 整数(可正可负) |
clip |
裁剪定位元素 | rect() |
6.3:浮动float
基本被现代布局flexbox和grid替代,只有部分的文字环绕图片还在用
css
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.news {
float: left;
width: 800px;
height: 200px;
margin: 0 20px 0 20px;
}
</style>
</head>
<body>
<img class="news" src="news.png" alt="新闻图片">
<p>北京,2025年3月15日电 ------ 今天,深度求索公司正式发布了其人工智能助手DeepSeek的全新功能升级,标志着AI技术在日常生活和工作场景中的应用迈出了重要一步。此次更新不仅在技术层面实现了重大突破,更为用户带来了前所未有的智能体验。
据深度求索公司技术负责人介绍,新版DeepSeek具备更强大的多模态理解和生成能力。系统现在能够更好地理解用户的复杂需求,提供更加精准、个性化的服务。与以往版本相比,此次升级特别优化了在编程辅助、学术研究、创意写作等专业领域的表现,响应速度提升40%,准确率提高25%。
"我们致力于让人工智能技术真正惠及每一个人,"深度求索公司CEO在发布会上表示,"DeepSeek不仅仅是工具,更是用户的智能伙伴。我们相信,技术的价值在于赋能,在于让复杂变得简单,让高效成为可能。"
市场分析人士指出,此次升级正值全球AI技术快速发展期。随着算力成本的下降和算法效率的提升,AI助手正从"新奇科技"转变为"日常必需品"。DeepSeek的更新恰逢其时,有望在教育、办公、创意产业等多个领域发挥更大作用。
值得注意的是,新版DeepSeek特别强调了安全性和隐私保护。系统采用端到端加密技术,确保用户数据安全,并在设计之初就融入了伦理考量,避免可能产生的偏见和误导。公司表示,他们建立了一套完整的内容审核和安全机制,确保AI助手的输出既智能又可靠。
用户体验方面,DeepSeek新增了多项实用功能。用户现在可以通过自然对话的方式完成复杂任务规划、数据分析甚至创意构思。测试用户反馈显示,新版本在理解上下文、保持对话连贯性方面表现突出,大大提升了使用效率。
业内专家认为,DeepSeek的此次升级不仅是技术的进步,更是AI应用理念的革新。它展现了人工智能从"执行命令"到"理解意图"的转变,预示着人机交互将更加自然、更加智能。
随着人工智能技术的不断成熟,我们有理由相信,像DeepSeek这样的智能助手将在未来扮演越来越重要的角色,为社会发展注入新的动能,为人们的生活和工作带来更多可能性。
深度求索公司表示,他们将继续投入研发,推动AI技术向更智能、更安全、更普惠的方向发展,让科技创新真正服务于人类的美好生活。</p>
</body>
</html>

float有一个经典的问题就是:父元素高度塌陷!!,
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.outer {
border: 2px solid red;
}
.float_inner {
float: left;
height: 100px;
}
</style>
</head>
<body>
<!-- 红框盒子高度变成0了!(因为子元素浮起来了,不占位置) -->
<div class="outer">
<div class="float_inner">
我浮起来了!
</div>
</div>
</body>
</html>
解决方案一:塌陷的父盒子加上撑杆
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.outer {
border: 2px solid red;
}
/* 方式一:在后面加个"撑杆"
使用伪元素::after作用在父元素, 使用clear: both清除两边浮动
*/
.outer::after {
content: "";
display: block;
clear: both; /* 清除两边浮动 */
}
.float_inner {
float: left;
height: 100px;
}
</style>
</head>
<body>
<!-- 红框盒子高度变成0了!(因为子元素浮起来了,不占位置) -->
<div class="outer">
<div class="float_inner">
我浮起来了!
</div>
</div>
</body>
</html>
方式二:塌陷的父盒子变成不漏的容器
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.outer {
border: 2px solid red;
/* 方式二:让父类变成不漏的容器 */
display: flow-root;
}
.float_inner {
float: left;
height: 100px;
}
</style>
</head>
<body>
<!-- 红框盒子高度变成0了!(因为子元素浮起来了,不占位置) -->
<div class="outer">
<div class="float_inner">
我浮起来了!
</div>
</div>
</body>
</html>
7:布局(⭐️⭐️⭐️⭐️⭐️⭐️)
7.1:弹性盒子flexbox(⭐️⭐️⭐️⭐️⭐️⭐️)
flex布局是CSS经典布局,可以通过简单的一行设置就可以对指定容器开启flex布局
css
/* 告诉浏览器:"我要用Flex布局了!" */
.container {
display: flex;
}
7.1.1:flex容器和项目
flex布局中,有两个重要角色
flex容器
可以理解成"父母",管理整个容器的整体布局
css
/* 父母管整体布局 */
.container {
display: flex; /* 激活Flex */
flex-direction: row; /* 排列方向 */
justify-content: center; /* 水平对齐 */
align-items: center; /* 垂直对齐 */
}
flex项目
可以理解成为"孩子",管理自己的大小
css
/* 孩子管自己的大小 */
.item {
flex: 1; /* 自动分配空间 */
align-self: center; /* 自己单独对齐 */
}
7.1.2:flex容器的六大权力
flex-direction -> 排列方向的选择 -> 横着摆还是竖着摆
css
.container {
/* 四个方向可选 */
flex-direction: row; /* → 默认:水平排列 */
flex-direction: row-reverse; /* ← 水平反方向 */
flex-direction: column; /* ↓ 垂直排列 */
flex-direction: column-reverse; /* ↑ 垂直反方向 */
}

justify-content -> 主轴方向对齐策略
css
.container {
/* 水平对齐(如果flex-direction: row) */
justify-content: flex-start; /* ← 靠左 */
justify-content: flex-end; /* → 靠右 */
justify-content: center; /* 中间 */
justify-content: space-between; /* 两端对齐,中间平均 */
justify-content: space-around; /* 四周有间隔 */
justify-content: space-evenly; /* 完全平均 */
}
align-items -> 交叉轴方向对齐策略
css
.container {
/* 垂直对齐(如果flex-direction: row) */
align-items: stretch; /* 默认:拉伸填满 */
align-items: flex-start; /* ↑ 顶部对齐 */
align-items: flex-end; /* ↓ 底部对齐 */
align-items: center; /* 垂直居中 */
align-items: baseline; /* 文字基线对齐 */
}
flex-wrap -> 是否换行
css
.container {
flex-wrap: nowrap; /* 默认:不换行,挤在一起 */
flex-wrap: wrap; /* 换行:像书架放不下换下一层 */
flex-wrap: wrap-reverse; /* 反向换行 */
}
flex-flow -> 方向+换行(简写)
css
.container {
/* 相当于:flex-direction + flex-wrap */
flex-flow: row wrap;
}
align-content -> 多行对齐
css
/* 当有多行时才有效 */
.container {
align-content: flex-start; /* 多行整体靠上 */
align-content: center; /* 多行整体居中 */
align-content: space-between; /* 行间距相等 */
}
7.1.3:flex项目的三大权力
flex -> 三合一魔法属性
css
.item {
/* 格式:flex: 放大 缩小 基准 */
flex: 1 1 200px;
}
.item1 { flex: 1; } /* = 1 1 0% 平均分配 */
.item2 { flex: auto; } /* = 1 1 auto 按内容 */
.item3 { flex: none; } /* = 0 0 auto 不伸缩 */
.item4 { flex: 0 0 200px; } /* 固定宽度200px */
- flex-grow:有多余空间时,我能分多少
- flex-shrink:空间不够时,我能缩多少
- flex-basis:我理想的大小是多少
order -> 排队顺序
数字越小,排的越靠前
css
/* 默认所有项目order: 0 */
.item1 { order: 1; } /* 排到后面 */
.item2 { order: -1; } /* 排到前面 */
align-self -> 自己单独对齐
css
.item {
align-self: auto; /* 默认:听父母的 */
align-self: flex-start; /* 我自己靠上 */
align-self: center; /* 我自己居中 */
align-self: flex-end; /* 我自己靠下 */
}
7.1.4:flex常见问题
flex: 1什么意思
flex: 1 = flex: 1 1 0%,意思是:能放大、能缩小、基准为0,平均分配剩余空间。
如何让最后一个元素靠右
css
.item:last-child {
margin-left: auto; /* 如何让最后一个元素靠右 */
}
如何垂直居中
css
.container {
display: flex;
align-items: center; /* 垂直居中 */
justify-content: center; /* 水平居中 */
}
7.2:grid布局(⭐️⭐️⭐️⭐️⭐️⭐️)
7.2.1:网格编排
格子是如何画出来的,网格编排的所有的属性都是处理网格的分布
display:grid-> 容器声明网格布局
css
.container {
display: grid; /* 这个元素的所有直系子元素将成为网格元素 */
}
这个属性会将容器中所有的子项变成块盒
这一点非常重要,例如子项中有<span>,这个子项也会变成块盒
grid-template-columns/grid-template-rows
通过 grid-template-columns 和 grid-template-rows 属性来定义网格中的列和行。
这些属性定义了网格的轨道,一个网格轨道就是网格中任意两条线之间的空间。
grid-template-columns就是设置列的宽度,设计网格有几列就写几个宽度值。
css
/*
如果有12个子项,将会是3行,4列
如果是5个子项,将会是2行,4列,第二行只有第一列有
auto = 内容宽度 + padding + border + margin
*/
.grid-container {
display: grid;
grid-template-columns: auto auto auto auto; /* 定义列的个数是4 */
/* 还可以使用repeat */
grid-template-columns: repeat(4, auto); /*同上*/
/* 还能够混用,下面这个是5列 */
/* 第一列的宽度固定是100px
后面3列的宽度根据内容而定
最后一列的宽度占grid-container宽度的20%
*/
grid-template-columns: 100px repeat(3, auto) 20%;
}
fr单位的引入
网格引入了 fr 单位来帮助我们创建灵活的网格轨道。一个 fr 单位代表网格容器中可用空间的一等份。
以下实例定义了一个网格定义将创建三个相等宽度的轨道,这些轨道会随着可用空间增长和收缩。
css
.grid-container {
display: grid;
grid-template-columns: 1fr 1fr 1fr; /* 三列各占据grid-container宽度的1/3 */
grid-template-columns: 2fr 2fr 2fr; /* 同上,三列个占据1/3 */
grid-template-columns: 1fr 2fr 1fr; /* 第一列1/4, 第二列1/2,第三列1/4 */
}
/* 更加复杂一点 */
.container {
display: grid;
/* 创建自动适应的网格列:每列至少250px,有多余空间就平分,空间不够就换行 */
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
/* 自动适应列数,每列最小250px */
gap: 20px;
}
同理,grid-template-rows声明的是每一行的高度

justify-content属性
justify-content 属性用于对齐容器内的网格
设置如何分配顺着弹性容器主轴(或者网格行轴) 的元素之间及其周围的空间。
css
/* 对齐方式 */
justify-content: center; /* 居中排列 */
justify-content: start; /* 从行首开始排列 */
justify-content: end; /* 从行尾开始排列 */
justify-content: flex-start; /* 从行首起始位置开始排列 */
justify-content: flex-end; /* 从行尾位置开始排列 */
justify-content: left; /* 一个挨一个在对齐容器得左边缘 */
justify-content: right; /* 元素以容器右边缘为基准,一个挨着一个对齐, */
/* 基线对齐 */
justify-content: baseline;
justify-content: first baseline;
justify-content: last baseline;
/* 分配弹性元素方式 */
justify-content: space-between; /* 均匀排列每个元素
首个元素放置于起点,末尾元素放置于终点 */
justify-content: space-around; /* 均匀排列每个元素
每个元素周围分配相同的空间 */
justify-content: space-evenly; /* 均匀排列每个元素
每个元素之间的间隔相等 */
justify-content: stretch; /* 均匀排列每个元素
'auto'-sized 的元素会被拉伸以适应容器的大小 */
/* 溢出对齐方式 */
justify-content: safe center;
justify-content: unsafe center;
/* 全局值 */
justify-content: inherit;
justify-content: initial;
justify-content: unset;
align-content属性
用于设置垂直方向上的网格元素在容器中的对齐方式
网格元素的总高度必须小于容器的高度才能使 align-content 属性生效
同理,还是start/end/center/space-between/space-around/space-evenly/stretch等等
grid-gap / grid-row-gap / grid-column-gap
可以通过这三个属性调整网格间距。
- grid-column-gap 属性来设置列之间的网格间距
- grid-row-gap 属性来设置行之间的网格间距
- grid-gap 属性是 grid-row-gap 和 the grid-column-gap 属性的简写
网格线
列与列,行与行之间的交接处就是网格线。
Grid 会为我们创建编号的网格线来让我们来定位每一个网格元素。
这个在后面子项放置非常重要,能划分出每一个子项划分的地盘。
7.2.2:子项放置
格子画好之后,html子项在格子中是如何分布的
网格容器包含了一个或多个网格元素。
默认情况下,网格容器的每一列和每一行都有一个网格元素,我们也可以设置网格元素跨越多个列或行,行和列为行号。这就用到了上面网格线的知识
grid-column / grid-column-start / grid-column-end
定义了网格元素列的开始和结束位置
css
/* 这两个表示方式是一致的,都表明这个网格元素占据1,2,3,4列 */
.item1 {
grid-column: 1 / 5;
}
.item1 {
grid-column-start: 1;
grid-column-end: 5;
}
grid-row / grid-row-start / grid-row-end
同理,定义了网格元素行的开始和结束位置
直接上一个例子:
html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
<style>
.grid-container {
display: grid;
grid-template-columns: repeat(4, auto);
justify-content: center;
gap: 10px 10px;
background-color: #2196F3;
padding: 10px;
}
.grid-item {
background-color: rgba(255, 255, 255, 0.8);
border: 1px solid rgba(0, 0, 0, 0.8);
border-radius: 10px;
padding: 20px;
font-size: 30px;
text-align: center;
}
/* 一号元素占据左上角的2行2列网格 */
.item1 {
grid-column: 1 / 3;
grid-row: 1 / 3;
text-align: center;
}
</style>
</head>
<body>
<div class="grid-container">
<div class="grid-item item1">1</div>
<div class="grid-item">2</div>
<div class="grid-item">3</div>
<div class="grid-item">4</div>
<div class="grid-item">5</div>
<div class="grid-item">6</div>
<div class="grid-item">7</div>
<div class="grid-item">8</div>
<div class="grid-item">9</div>
</div>
</body>
</html>

grid-area 属性是简写
css
/* "item8" 从第 1 行开始和第 2 列开始, 第 5 行和第 6 列结束 */
.item8 {
grid-area: 1 / 2 / 5 / 6;
}
可以使用关键字 "span" 来定义元素将跨越的行数
css
/* "item8" 从第 2 行开始和第 1 列开始, 横跨 2 行 3 列。 */
/* (2, 1) -> (3, 3) */
.item8 {
grid-area: 2 / 1 / span 2 / span 3;
}
7.3:响应式布局(⭐️⭐️⭐️⭐️⭐️⭐️)
响应式布局是设计理念,表示的是页面上的元素可以根据页面大小自动动态的调整样式。
7.3.1:流式布局
所谓流式布局,就是像水一样流动,不使用固定像素,而是使用百分比、视口单位
css
/* 不使用固定像素,使用百分比、视口单位 */
.container {
width: 90%; /* 百分比宽度 */
max-width: 1200px; /* 最大限制 */
min-width: 300px; /* 最小限制 */
}
.img {
max-width: 100%; /* 图片自适应 */
height: auto; /* 保持比例 */
}
.text {
font-size: clamp(16px, 2vw, 24px); /* 弹性字体大小 */
}
7.3.2:弹性盒子
使用上面的flexbox布局也可以满足响应式
css
/* flex容器 */
.container {
display: flex;
flex-wrap: wrap; /* 允许换行 */
gap: 20px; /* 间距自适应 */
}
/* flex子项 */
.item {
flex: 1 1 300px; /* 最小300px,可伸缩 */
}
7.3.3:网格布局
使用上面的grid布局也可以满足响应式
使用grid-template-cloumns和grid-template-rows即可完成响应式
css
.container {
display: grid;
/* 自动适应列数,每列最小250px, 最大1份 */
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
}
7.3.4:响应式技巧
@media - 精确控制
css
/* 超小设备(手机,竖屏) */
@media (max-width: 576px) { }
/* 小设备(手机,横屏) */
@media (min-width: 577px) and (max-width: 768px) { }
/* 中等设备(平板) */
@media (min-width: 769px) and (max-width: 992px) { }
/* 大设备(电脑) */
@media (min-width: 993px) and (max-width: 1200px) { }
/* 超大设备(大屏幕) */
@media (min-width: 1201px) { }
视口单位(vw, vh, vmin, vmax)
| 单位 | 含义 | 基准 | 示例值 | 典型用途 |
|---|---|---|---|---|
| vw | 视口宽度百分比 | 窗口宽度 | 50vw = 窗口宽度的50% |
响应式宽度 |
| vh | 视口高度百分比 | 窗口高度 | 100vh = 整个窗口高度 |
全屏背景 |
| vmin | 视口较小尺寸的% | 宽高中较小的 | 50vmin = 宽高中较小的50% |
正方形元素 |
| vmax | 视口较大尺寸的% | 宽高中较大的 | 100vmax = 宽高中较大的100% |
覆盖整个屏幕 |
css
/* 相对于视口大小 */
.title {
font-size: calc(16px + 1vw); /* 字体随视口变化 */
padding: 5vh 5vw; /* 内边距相对视口 */
width: 80vw; /* 宽度占视口80% */
}
clamp()智能限制
css
/* 最小值 | 理想值 | 最大值 */
.font-responsive {
font-size: clamp(16px, 2vw, 24px);
/* 不小于16px,不大于24px,理想是2vw */
}
.width-responsive {
/* 不小于300像素,不大于800像素,理想是50%宽度 */
width: clamp(300px, 50%, 800px);
}
图片自适应
css
img {
max-width: 100%; /* 关键! */
height: auto; /* 保持比例 */
}
/* 响应式背景图 */
.hero {
background-image: url('small.jpg');
background-size: cover;
}
@media (min-width: 768px) {
.hero {
background-image: url('large.jpg');
}
}
响应式表格
css
.table-responsive {
overflow-x: auto; /* 小屏幕上可以横向滚动 */
}
8:SCSS
四:JavaScript
JS = ESMAScript + DOM + BOM
如果你掌握了Java, JS的学习将会非常的容易
1:JS概述
JS是解释型语言(不需要编译成为机器码,开发容易 + 运行缓慢),动态语言(类型动态)
类似于JAVA和Cpp的语法结构,严格区分大小写
在项目中,可以通过两种方式使用JS:分别是标签引用和文件引用
html
<script>
alert("Hello,World!");
</script>
<script src="main.js"></script> <!-- 通过src属性指定js文件的位置 -->
JS的输出分成三种:页面输出,控制台输出和弹出窗口输出,其中控制台输出最为常用,用于前端调试
js
// 页面输出
document.write("Hello,World!");
// 控制台输出
console.log("输出一条日志"); // 最常用
// 弹出窗口输出
alert("Hello,World!");
JS注释同java, 单行注释//和多行注释/* */
2:JS基础语法
如果你学习过任何一种后端语言,例如java,这部分非常好理解
2.1:JS变量
注意关键字和保留字不能作为变量名,JS命名规范还是那一套,一点说明的就是最好使用驼峰命名法
在 JavaScript 中,一般常量声明用const, 变量声明用let,es6有let之后很少用var了
js
let a = 10
const b = 20
a = "str"
// b = 30 // error, 因为b是常量,不能被修改
2.2:JS基本数据类型
JS有五种基本数据类型:字符串类型(String), 数值类型(Number), 布尔类型(Boolean), undefined和null
这5种之外的类型都称为Object,所以总的来看JavaScript中共有六种数据类型
可以使用typeof(变量名)的方法查看一个变量的数据类型
Number 类型用来表示整数和浮点数,-1.7976931348623157e+308 ~ +1.7976931348623157e+308, 如果超过了这个范围将会返回±Infinity。0以上的最小值是5e-324, 还有一个特殊的数字是NaN(not a number)
0b开头二进制,0开头8进制,0x开头十六进制
在使用 var/let 声明变量但未对其加以初始化时,这个变量的值就是 undefined。
js
let b;
console.log(b + " " + typeof(b)) // undefined undefined
b = false
console.log(b + " " + typeof(b)) // false boolean
null是另一个特殊类型,表示空的Object, undefined是null衍生出来的, null的数据类型是Object
重点 - 数据类型的转换

js
// 其他类型转成字符串
let a1 = 123;
let b1 = String(a1);
let c1 = a1 + "";
let d1 = a1.toString();
console.log(typeof(a1), typeof(b1), typeof(c1), typeof(d1)) // number string string string
// 字符串转成数字
let a2 = "123";
let b2 = Number(a2);
let c2 = +a2;
let d2 = parseInt(a2);
let e2 = parseFloat(a2);
console.log(typeof(a2), typeof(b2), typeof(c2), typeof(d2), typeof(e2)) // string number number number number
// 其他类型转成boolean
// 0, "", null, undefined, NaN 这五个转换成false, 其他都会转换成true
let a3 = null
console.log(Boolean(a3)) // false
let b3 = 0
console.log(Boolean(b3)) // false
2.3:运算符
算数运算符 / 关系运算符 / 赋值运算符 / 逻辑运算符 不再过多赘述,都懂
- 算数运算符:
+ - * / % ++ -- - 关系运算符:
> < >= <= - 赋值运算符:
= += -= *= /= %= - 逻辑运算符:
&&(全真才真,短路机制) ||(有真则真,短路机制) !(真假取反) - 三元表达式:
exp ? ans1 : ans2,如果exp的结果为真,取ans1,否则取ans2 - 逗号运算符:从左到右
重点说下JS的比较运算符:== != === !==,注意一点就是==和!=比较的时候会进行类型转换,先将两个比较对象转换成为同一个类型,然后再比较,而===和!==则不会,不一样就是不一样
js
let c = null
let d = undefined
console.log(c == d) // true
console.log(c === d) // false
运算符优先级不好记,但是只要记住一点:你想要优先算的,加括号就好了,先算括号内的(仅次于创建)
2.4:控制语句
更是和Java长的几乎一样,会java就会JS的
if 条件控制语句
js
if (exp) {
statement of exp is true;
} else if (exp2) {
statement of exp2 is true;
} else {
statement of others;
}
let age = 18;
if (age < 18) {
console.log("小于18岁了");
} else if (age == 18) {
console.log("已经18岁了");
} else {
console.log("大于18岁了")
}
switch...case
js
switch (语句) {
case 表达式1:
语句...
case 表达式2:
语句...
default:
语句...
}
// 例如
let sex = '男';
switch (sex) {
case '女':
console.log("是女生");
break;
case '男':
default:
console.log("是男生");
}
循环
js
while (exp) {
statement...
}
do {
语句...
} while (条件表达式); // 至少执行一次
for(初始化表达式; 条件表达式; 更新表达式) {
语句...
}
// 例如
for (var i = 1; i <= 10; i++) {
console.log(i);
}
break和continue关键字
- break:结束最近的一次循环,可以在循环和switch语句中使用。
- continue:结束本次循环,执行下一次循环,只能在循环中使用。
2.5:函数function
函数有4种写法
js
// 第一类:正式写法
// 可提前调用,所谓提前调用,就是调用在函数声明之前写
console.log(greet("小明")); // 你好,小明!
function greet(name) {
return "你好," + name + "!";
}
// 第二类:函数表达式
// 像给变量赋值一个函数
const sayHello = function(name) {
return "Hello, " + name;
};
console.log(sayHello("Tom")); // Hello, Tom
// 第三类:箭头函数
// 简洁写法,适合简单函数
const add = (a, b) => a + b;
console.log(add(2, 3)); // 5
// 多个参数用括号
const multiply = (x, y) => {
return x * y;
};
// 第四类:定义后立刻执行,像泡面:拆开就泡
(function() {
console.log("立即执行!");
})();
3:JS类和对象
3.1:概述
JS是面向对象的语言,所谓面向对象,就是用对象object表示现实世界中具体的事物,这点和Java理念一致,万物皆对象
对象分成属性和方法:属性就是事务的特征,方法就是事务的行为。例如手机这个对象有屏幕属性和打电话这个行为方法
类class抽象了对象的公共部分,泛指一大类,通过类的实例化得到对象。
类比一下:类是汽车的设计图纸,对象是具体的汽车🚗:设计图纸通过实例化得到具体的汽车
对象的方法就是属于某个对象的函数
js
// 类的声明
class name {
// class body
}
// 实例化,得到具体的对象,类必须使用 new 实例化对象
let xxx = new name()
构造函数
constructor() 方法是类的构造函数(默认方法),用于传递参数,返回实例对象。
通过 new 命令生成对象实例时,自动调用该方法。
如果没有显示定义, 类内部会自动给我们创建一个参数为空的constructor()
js
class Person {
constructor(name,age) { // constructor 构造方法或者构造函数
this.name = name;
this.age = age;
}
}
let ldh = new Person('刘德华', 18);
console.log(ldh.name);
类方法
方法之间不能加逗号分隔,同时方法不需要添加 function 关键字
js
class Person {
constructor(name,age) { // constructor 构造器或者构造函数
this.name = name;
this.age = age;
}
say() {
console.log(this.name + '你好');
}
}
let ldh = new Person('刘德华', 18);
ldh.say() // 刘德华你好
3.2:类的继承
子类可以继承父类的一些属性和方法,extends关键字
js
class Father {
constructor(surname) {
this.surname= surname;
}
say() {
console.log('你的姓是' + this.surname);
}
}
class Son extends Father{ // 这样子类就继承了父类的属性和方法
}
let damao= new Son('刘');
damao.say();
super 关键字用于子类访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数
js
class Father {
constructor(surname) {
this.surname = surname;
}
saySurname() {
console.log('我的姓是' + this.surname);
}
}
class Son extends Father { // 这样子类就继承了父类的属性和方法
constructor(surname, fristname) {
super(surname); // 调用父类的constructor(surname)
this.fristname = fristname;
}
sayFristname() {
super.saySurname();
console.log("我的名字是:" + this.fristname);
}
}
let son = new Son('王', '小王');
son.sayFristname();
this 关键字:constructor 里面的this指向实例对象, 方法里面的this 指向这个方法的调用者
3.3:构造函数和原型(⭐️⭐️⭐️⭐️⭐️⭐️)
3.3.1:构造函数 + new关键字
在JS中,构造函数有两点需要注意:首字母大写 + 和new关键字一起使用。
js
function Star(uname, age) {
this.uname = uname;
this.age = age;
this.sing = function () {
console.log('I am singing');
}
}
let xiaoming = new Star('xiaoming', 18);
xiaoming.sing() // I am singsing
let xiaohong = new Star('xiaohong', 19);
xiaohong.sing() // I am singsing
new会在执行的时候做四件事:面试常考
-
在内存中创建一个新的空对象
-
让 this 指向这个新的对象。
-
执行构造函数里面的代码,给这个新对象添加属性和方法。
-
返回这个新对象(所以构造函数里面不需要 return )
静态成员 & 实例成员
JS中可以在构造函数中添加一些成员,如果在构造函数本身添加,称为静态成员,反之如果加在内部的this,称为实例成员
静态成员只能由构造函数本事访问,实例成员仅能由实例化的对象访问
3.3.2:构造函数原型prototype
上面这个方法有一个十分明显的问题,就是存在浪费内存的问题,每一个对象是各自单独的函数,而构造函数通过原型分配的函数是所有对象所共享的,因此,我们可以把那些不变的方法,直接定义在 prototype 对象上,这样所有对象的实例就可以共享这些方法。
-
只有函数才有 prototype 属性
-
它是给未来通过new创建的对象准备的"公共模具"
-
所有通过该构造函数创建的对象,都共享这个模具
javascript
// 定义一个构造函数 - 玩具生产线
function Toy(name) {
this.name = name
}
// 给这条生产线配置"模具", 这条生产线的所有的玩具都会公用这一个模具
// 这也是原型模式的核心 - 共享属性和方法
Toy.prototype = {
play() {
console.log(`玩${this.name}`)
},
material: "塑料"
}
// 通过new 关键字创建一个玩具,生产线生产一个小汽车
// 小汽车使用模具中的模具,获得共享方法play(), 共享属性material
let toy = new Toy("小汽车")
toy.play()
console.log(toy.constructor === Toy) // false -> toy对象是由Object创建的
console.log(toy instanceof Toy) // true -> toy对象是由Toy构造函数创建的
console.log(toy.constructor.name) // Object -> 表示toy对象是由Object创建的
3.3.3:__proto__对象属性
对象都会有一个属性 __proto__ ,他指向构造函数的 prototype 原型对象
js
对象.__proto__ === 创建它的构造函数.prototype
__proto__对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线
但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象 prototype
javascript
// 定义一个构造函数 - 玩具生产线
function Toy(name) {
this.name = name
}
// 给这条生产线配置"模具", 这条生产线的所有的玩具都会公用这一个模具
// 这也是原型模式的核心 - 共享属性和方法
Toy.prototype = {
play() {
console.log(`玩${this.name}`)
},
material: "塑料"
}
// 通过new 关键字创建一个玩具,生产线生产一个小汽车
// 小汽车使用模具中的模具,获得共享方法play(), 共享属性material
let toy = new Toy("小汽车")
toy.play()
console.log(toy.constructor === Toy) // false -> toy对象是由Object创建的
console.log(toy instanceof Toy) // true -> toy对象是由Toy构造函数创建的
console.log(toy.constructor.name) // Object -> 表示toy对象是由Object创建的
// 可以将__proto__属性理解为产品的标签
// car.__proto__ 就是问:"小汽车,你是用哪个模具生产的?"
console.log(toy.__proto__ === Toy.prototype) // true -> toy.__proto__属性指向了Toy.prototype对象
console.log(toy.hasOwnProperty('play')); // false - play不是car自己的
console.log('play' in toy); // true - 但可以通过原型链找到
3.3.4:原型链
而原型链这个概念就是一个"找东西"的过程
-
当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。
-
如果没有就查找它的原型(也就是 __proto__指向的 prototype 原型对象)。
-
如果还没有就查找原型对象的原型(Object的原型对象)。
-
依此类推一直找到 Object 为止(null)。
-
__proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线。
javascript
// 定义一个构造函数 - 玩具生产线
function Toy(name) {
this.name = name
}
// 给这条生产线配置"模具", 这条生产线的所有的玩具都会公用这一个模具
// 这也是原型模式的核心 - 共享属性和方法
Toy.prototype = {
play() {
console.log(`玩${this.name}`)
},
material: "塑料"
}
// 通过new 关键字创建一个玩具,生产线生产一个小汽车
// 小汽车使用模具中的模具,获得共享方法play(), 共享属性material
let toy = new Toy("小汽车")
toy.play()
// 当访问car.play()时,JS的查找顺序:
// 1. 先看car自己有没有play属性 -> 没有
// 2. 看car.__proto__(即Toy.prototype)有没有 -> 有。找到了,结束查找
// 3. 如果还没有,继续看Toy.prototype.__proto__(Object.prototype)
// 4. 直到找到或到达null
console.log(car.__proto__.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null - 到头了
console.log(toy.constructor === Toy) // false -> toy对象是由Object创建的
console.log(toy instanceof Toy) // true -> toy对象是由Toy构造函数创建的
console.log(toy.constructor.name) // Object -> 表示toy对象是由Object创建的
// 可以将__proto__属性理解为产品的标签
// car.__proto__ 就是问:"小汽车,你是用哪个模具生产的?"
console.log(toy.__proto__ === Toy.prototype) // true -> toy.__proto__属性指向了Toy.prototype对象
console.log(toy.hasOwnProperty('play')); // false - play不是car自己的
console.log('play' in toy); // true - 但可以通过原型链找到
/*
就是这么一个链....
toy(玩具)
↓ __proto__
Toy.prototype(模具)
↓ __proto__
Object.prototype(基础模具)
↓ __proto__
null
*/

| 特性 | prototype |
__proto__ |
|---|---|---|
| 归属 | 函数独有 | 对象都有 |
| 作用 | 作为新建对象的模板 | 指向创建者的原型 |
| 访问 | Func.prototype |
obj.__proto__ |
| 标准 | ES标准属性 | 非标准,但有浏览器实现 |
现在都使用Object.getPrototypeOf(),因为obj.__proto__写法不规范
js
let car = new Toy('小汽车');
// 不推荐(非标准)
console.log(car.__proto__);
// 推荐(标准方法)
console.log(Object.getPrototypeOf(car)); // 等同于car.__proto__
console.log(Object.getPrototypeOf(car) === Toy.prototype); // true
3.3.5:继承
ES6之前并没有给我们提供 extends 继承。我们可以通过构造函数+原型对象模拟实现继承,被称为组合继承。
ES6之后使用class + extends方式实现继承和原型,非常好理解,看下面例子就知道ES6之后如何实现了
js
class Animal {
constructor(name) {
this.name = name; // 实例属性
}
// 自动添加到 Animal.prototype
eat() {
console.log(`${this.name}在吃东西`);
}
}
class Dog extends Animal {
// 子类的构造方法
constructor(name, breed) {
super(name); // 相当于 Animal.call(this, name)
this.breed = breed;
}
// 自动添加到 Dog.prototype
bark() {
console.log(`${this.name}在汪汪叫`);
}
// 可以重写父类方法
eat() {
super.eat(); // 调用父类的eat方法
console.log('吃的是狗粮');
}
}
const myDog = new Dog('旺财', '金毛');
myDog.eat(); // "旺财在吃东西" "吃的是狗粮"
myDog.bark(); // "旺财在汪汪叫"
由此可以总结出类class的本质
class本质还是function,ES6的类其实就是语法糖,更加面向编程。
类的所有方法都定义在类的prototype属性上
类创建的实例,里面也有__proto__ 指向类的prototype原型对象
3.4:this:当前上下文的主人
this就是当前代码执行所在的环境,就是谁调用,this就指向谁
this取决于函数的调用方式,而不是定义的位置
js
// 示例1:全局上下文
// this指向的就是window
console.log(this); // 浏览器中指向window,Node.js中指向global
// 示例2:对象方法中的this
// this指向的就是调用这个方法的对象
const person = {
name: "小明",
sayName() {
console.log(this.name); // this指向person对象
}
};
person.sayName(); // 输出:"小明"
// 示例3:构造函数中的this
// this指定的就是新穿件的对象
function Car(brand) {
this.brand = brand; // this指向新创建的对象
}
const myCar = new Car("Toyota");
// 示例4:箭头函数的this
const obj = {
name: "小红",
regularFunc: function() {
console.log(this.name); // 正常指向obj
},
arrowFunc: () => {
console.log(this.name); // 指向外层作用域,这里可能是window/undefined
}
};
// 实例5:计时器中的this指向window
let cat = {
name: "喵喵",
sayName() {
setTimeout(function(){console.log(this)}, 1000) // 指向window
}
}
- 方法中的this,指向调用方法的对象
- 全局环境下指向全局对象
- 全局函数中的this,指向全局对象
- 内部函数中的this,指向全局对象
- 事件中的this,指向触发事件的DOM对象
- 构造函数中的this,指向new创建的对象【面试题new的作用】
- 箭头函数中的this,指向定义函数上下文的this。
- 使用闭包,var获取dom的索引。
可以通过call() / apply() / bind()方法,来改变this的指向
js
const person = {
name: "小明"
};
function introduce(greeting) {
console.log(`${greeting}, 我是${this.name}`);
}
introduce("你好") // 你好, 我是undefined
// 1. call() - 强行改变this指向, this的指向就是call的第一个参数
introduce.call(person, "你好"); // 你好, 我是小明
// 2. apply() - 强行改变this指向, this的指向就是apply的第一个参数
introduce.apply(person, ["你好"]); // 你好, 我是小明
// 3. bind() - 返回新函数,稍后调用
const boundFunc = introduce.bind(person, "你好");
boundFunc(); // 你好, 我是小明
3.5:闭包和递归
闭包是指函数能够记住并访问它被创建时的词法作用域,即使函数在其作用域外执行
简单的来说就是一个函数可以访问其所在外部函数的变量,即闭包 = 内层函数 + 引用的外层函数变量。
js
function outer() {
// 内层函数 + 这个a变量构成闭包Closure
let a = 100;
function inner() {
console.log("我是内部函数,我能引用外部函数的变量a: " + a);
}
}
outer()
通常使用一个函数包裹住闭包结构,以起到对变量保护的作用。

无法直接在外部访问i这个变量,完成了数据封装。
闭包使用场景:节流和防抖,vue3和react的hook
⚠️ 闭包可能会导致内存泄漏:

递归是函数直接或间接调用自身来解决问题的方法,俄罗斯套娃🪆
js
// 经典示例:计算阶乘
// 5! = 5 × 4 × 3 × 2 × 1 = 120
function factorial(n) {
// 终止条件:当n为1时,停止递归
if (n === 1) {
return 1;
}
// 递归步骤:n! = n × (n-1)!
return n * factorial(n - 1);
}
console.log(factorial(5)); // 120
递归三大要素:终止条件(递归何时结束) + 递归步骤(如何将问题分解为小的同类问题) + 递归调用(我调我自己)
js
// 场景1:树形结构的遍历
// 遍历DOM树
function traverseDOM(node, depth = 0) {
// 输出当前节点
console.log(" ".repeat(depth * 2) + node.nodeName);
// 递归遍历子节点
node.childNodes.forEach(child => {
if (child.nodeType === 1) { // 元素节点
traverseDOM(child, depth + 1);
}
});
}
// traverseDOM(document.body);
// 场景2:深拷贝
function deepClone(obj) {
// 终止条件:不是对象或是null
if (typeof obj !== 'object' || obj === null) {
return obj;
}
// 创建新对象或数组
const clone = Array.isArray(obj) ? [] : {};
// 递归复制每个属性
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key]);
}
}
return clone;
}
const original = { a: 1, b: { c: 2, d: [3, 4] } };
const cloned = deepClone(original);
⚠️ 必须有终止条件,否则会无限递归导致栈溢出
⚠️ 递归层级不宜过深(通常不超过1000层)
3.6:JS常用对象-基本对象
3.6.1:原始对象
JavaScript 中最基础的对象,所有其他对象都继承自 Object。
原始对象对于高级调试非常有用
| 创建方式 | 语法 | 示例 | 说明 |
|---|---|---|---|
| 字面量 | {} | let obj = {} | 最常用方式 |
| 构造函数 | new Object() | let obj = new Object() | 较少使用 |
| Object.create() | Object.create(proto) | let obj = Object.create(null) | 指定原型 |
属性操作
| 方法 | 描述 | 返回值 | 是否改变原对象 |
|---|---|---|---|
| Object.keys() | 获取自身可枚举属性名数组 | Array | ❌ |
| Object.values() | 获取自身可枚举属性值数组 | Array | ❌ |
| Object.entries() | 获取自身可枚举键值对数组 | Array | ❌ |
| Object.hasOwn() | 检查是否拥有自身属性 | boolean | ❌ |
| Object.getOwnPropertyNames() | 获取所有自身属性名 | Array | ❌ |
对象操作
| 方法 | 描述 | 返回值 | 是否改变原对象 |
|---|---|---|---|
Object.assign() |
合并对象(浅拷贝) | 新对象 | ❌ |
Object.freeze() |
冻结对象 | 被冻结对象 | ✅ |
Object.seal() |
密封对象 | 被密封对象 | ✅ |
Object.preventExtensions() |
阻止扩展 | 对象 | ✅ |
Object.is() |
值比较(同值相等) | boolean | ❌ |
属性描述符
| 方法 | 描述 | 返回值 | 是否改变原对象 |
|---|---|---|---|
| Object.defineProperty() | 定义/修改属性 | 对象 | ✅ |
| Object.defineProperties() | 定义多个属性 | 对象 | ✅ |
| Object.getOwnPropertyDescriptor() | 获取属性描述符 | 描述符对象 | ❌ |
| Object.getOwnPropertyDescriptors() | 获取所有属性描述符 | 描述符对象 | ❌ |
原型操作
| 方法 | 描述 | 返回值 | 是否改变原对象 |
|---|---|---|---|
| Object.getPrototypeOf() | 获取原型 | prototype对象 | ❌ |
| Object.setPrototypeOf() | 设置原型 | 对象 | ✅ |
| Object.isPrototypeOf() | 检查是否为原型 | boolean | ❌ |
es6新特性
| 方法/特性 | 描述 | 示例 |
|---|---|---|
| 扩展运算符 | 对象展开 | {...obj1, ...obj2} |
| 解构赋值 | 提取属性 | const {name, age} = person |
| 属性简写 | 同名属性简写 | {name, age} |
| 计算属性名 | 动态属性名 | {[key]: value} |
| Object.hasOwn() | 替代hasOwnProperty | Object.hasOwn(obj, 'prop') |
| Object.fromEntries() | 键值对数组转对象 | Object.fromEntries([['a',1]]) |
3.6.2:String对象
表示和操作文本数据,字符串是不可变的。重要性不用多说
查询与访问
| 方法 | 描述 | 返回值 | 是否改变原字符串 |
|---|---|---|---|
| length | 字符串长度 | number | - |
| charAt() | 获取指定位置字符 | string | ❌ |
| charCodeAt() | 获取字符编码 | number | ❌ |
| codePointAt() | 获取Unicode编码 | number | ❌ |
| [] | 索引访问字符 | string | ❌ |
| indexOf() | 查找子串首次位置 | number | ❌ |
| lastIndexOf() | 查找子串最后位置 | number | ❌ |
| includes() | 是否包含子串 | boolean | ❌ |
| startsWith() | 是否以子串开头 | boolean | ❌ |
| endsWith() | 是否以子串结尾 | boolean | ❌ |
| search() | 正则搜索位置 | number | ❌ |
提取与分割
| 方法 | 描述 | 返回值 | 是否改变原字符串 |
|---|---|---|---|
| slice() | 提取子串 | string | ❌ |
| substring() | 提取子串 | string | ❌ |
| substr() | 提取子串 | string | ❌ |
| split() | 分割为数组 | Array | ❌ |
| match() | 正则匹配 | Array/null | ❌ |
| matchAll() | 正则匹配所有 | Iterator | ❌ |
修改与转换
| 方法 | 描述 | 返回值 | 是否改变原字符串 |
|---|---|---|---|
| concat() | 连接字符串 | string | ❌ |
| repeat() | 重复字符串 | string | ❌ |
| padStart() | 开头填充 | string | ❌ |
| padEnd() | 结尾填充 | string | ❌ |
| toUpperCase() | 转大写 | string | ❌ |
| toLowerCase() | 转小写 | string | ❌ |
| toLocaleUpperCase() | 本地化转大写 | string | ❌ |
| toLocaleLowerCase() | 本地化转小写 | string | ❌ |
| normalize() | Unicode正规化 | string | ❌ |
修剪与替换
| 方法 | 描述 | 返回值 | 是否改变原字符串 |
|---|---|---|---|
| trim() | 去除两端空格 | string | ❌ |
| trimStart() | 去除开头空格 | string | ❌ |
| trimEnd() | 去除结尾空格 | string | ❌ |
| replace() | 替换子串 | string | ❌ |
| replaceAll() | 替换所有子串 | string | ❌ |
静态方法
| 方法 | 描述 |
|---|---|
| String.fromCharCode() | 编码转字符 |
| String.fromCodePoint() | Unicode转字符 |
| String.raw() | 原始模板字符串 |
3.6.3:number数字对象
实例方法
| 方法 | 描述 | 返回值 |
|---|---|---|
| toFixed() | 固定小数位数 | string |
| toExponential() | 科学计数法 | string |
| toPrecision() | 指定精度 | string |
| toString() | 转为字符串 | string |
| valueOf() | 获取原始值 | number |
静态属性(常量)
| 属性 | 描述 | 值 |
|---|---|---|
| Number.MAX_VALUE | 最大正数 | 1.7976931348623157e+308 |
| Number.MIN_VALUE | 最小正数 | 5e-324 |
| Number.MAX_SAFE_INTEGER | 最大安全整数 | 9007199254740991 |
| Number.MIN_SAFE_INTEGER | 最小安全整数 | -9007199254740991 |
| Number.POSITIVE_INFINITY | 正无穷 | Infinity |
| Number.NEGATIVE_INFINITY | 负无穷 | -Infinity |
| Number.NaN | 非数字 | NaN |
| Number.EPSILON | 最小精度 | 2.220446049250313e-16 |
静态方法
| 方法 | 描述 | 返回值 |
|---|---|---|
| Number.isNaN() | 是否为NaN | boolean |
| Number.isFinite() | 是否为有限数 | boolean |
| Number.isInteger() | 是否为整数 | boolean |
| Number.isSafeInteger() | 是否为安全整数 | boolean |
| Number.parseFloat() | 解析浮点数 | number |
| Number.parseInt() | 解析整数 | number |
3.6.4:Boolean对象
以下值在布尔上下文中为 false,除了下面表格的是false,其他的全是true
| 值 | 类型 |
|---|---|
| false | Boolean |
| 0 | Number |
| -0 | Number |
| 0n | BigInt |
| "" | String |
| null | Object |
| undefined | Undefined |
| NaN | Number |
3.7:JS常用对象-核心对象
3.7.1:数组对象
数组对象可以放入不同类型的对象,可以理解为python的list
js
let arr = new Array();
arr[0] = 1;
arr[1] = 2;
arr[2] = "2"; // 可以放入不同的类型,可以理解为python的list
// 还可以使用字面量创建
let arr = [1, 2, 3, 4, 5]
let arr = [1, "1", 2, "2"]
数组常见操作如下:非常像python的list方法
数组方法非常的重要,后面在框架实战中,对于接受到的后端数据,很多数组的处理,还有复选框的处理,一定要熟练运用
增删改方法
| 方法 | 描述 | 返回值 | 是否改变原数组 | 示例 |
|---|---|---|---|---|
push() |
末尾添加一个或多个元素 | 新数组长度 | ✅ | arr.push(4,5) |
pop() |
删除最后一个元素 | 被删除的元素 | ✅ | arr.pop() |
unshift() |
开头添加一个或多个元素 | 新数组长度 | ✅ | arr.unshift(0) |
shift() |
删除第一个元素 | 被删除的元素 | ✅ | arr.shift() |
splice() |
删除/替换/添加元素 | 被删除的元素数组 | ✅ | arr.splice(1,2,'a','b') |
fill() |
填充数组元素 | 修改后的数组 | ✅ | arr.fill(0,1,3) |
查询遍历方法
| 方法 | 描述 | 返回值 | 是否改变原数组 | 示例 |
|---|---|---|---|---|
forEach() |
遍历数组执行回调 | undefined | ❌ | arr.forEach(item => console.log(item)) |
map() |
映射新数组 | 新数组 | ❌ | arr.map(x => x*2) |
filter() |
过滤满足条件的元素 | 新数组 | ❌ | arr.filter(x => x>5) |
find() |
查找第一个满足条件的元素 | 元素或undefined | ❌ | arr.find(x => x>5) |
findIndex() |
查找第一个满足条件的索引 | 索引或-1 | ❌ | arr.findIndex(x => x>5) |
indexOf() |
查找元素首次出现位置 | 索引或-1 | ❌ | arr.indexOf(5) |
lastIndexOf() |
查找元素最后出现位置 | 索引或-1 | ❌ | arr.lastIndexOf(5) |
includes() |
是否包含某元素 | boolean | ❌ | arr.includes(5) |
some() |
是否有元素满足条件 | boolean | ❌ | arr.some(x => x>5) |
every() |
是否所有元素满足条件 | boolean | ❌ | arr.every(x => x>5) |
排序转换方法
| 方法 | 描述 | 返回值 | 是否改变原数组 | 示例 |
|---|---|---|---|---|
sort() |
数组排序 | 排序后的数组 | ✅ | arr.sort((a,b) => a-b) |
reverse() |
反转数组顺序 | 反转后的数组 | ✅ | arr.reverse() |
join() |
数组转字符串 | 字符串 | ❌ | arr.join('-') |
toString() |
数组转字符串 | 字符串 | ❌ | arr.toString() |
concat() |
合并多个数组 | 新数组 | ❌ | arr.concat(arr2,arr3) |
slice() |
截取数组部分 | 新数组 | ❌ | arr.slice(1,4) |
flat() |
数组扁平化 | 新数组 | ❌ | arr.flat(2) |
flatMap() |
映射后扁平化 | 新数组 | ❌ | arr.flatMap(x => [x, x*2]) |
其他实用方法
| 方法 | 描述 | 返回值 | 是否改变原数组 | 示例 |
|---|---|---|---|---|
reduce() |
累计器 | 累计结果 | ❌ | arr.reduce((sum,cur) => sum+cur,0) |
reduceRight() |
从右累计 | 累计结果 | ❌ | arr.reduceRight(...) |
keys() |
返回键名迭代器 | 迭代器 | ❌ | [...arr.keys()] |
values() |
返回值迭代器 | 迭代器 | ❌ | [...arr.values()] |
entries() |
返回键值对迭代器 | 迭代器 | ❌ | [...arr.entries()] |
Array.isArray() |
判断是否为数组 | boolean | - | Array.isArray(arr) |
Array.from() |
类数组转数组 | 新数组 | - | Array.from('hello') |
Array.of() |
创建数组 | 新数组 | - | Array.of(1,2,3) |
ES6+新增方法
| 方法 | 描述 | 返回值 | 是否改变原数组 | 示例 |
|---|---|---|---|---|
findLast() |
查找最后一个满足条件的 | 元素或undefined | ❌ | arr.findLast(x => x>5) |
findLastIndex() |
查找最后一个满足条件的索引 | 索引或-1 | ❌ | arr.findLastIndex(x => x>5) |
toSorted() |
排序(不改变原数组) | 新数组 | ❌ | arr.toSorted() |
toReversed() |
反转(不改变原数组) | 新数组 | ❌ | arr.toReversed() |
toSpliced() |
增删改(不改变原数组) | 新数组 | ❌ | arr.toSpliced(1,2) |
with() |
修改指定位置值(不改变原数组) | 新数组 | ❌ | arr.with(2, 100) |
使用建议速查表
| 需求场景 | 推荐方法 |
|---|---|
| 添加元素到末尾 | push() |
| 从末尾删除元素 | pop() |
| 添加元素到开头 | unshift() |
| 从开头删除元素 | shift() |
| 在任意位置增删改 | splice() |
| 遍历数组 | forEach() |
| 创建新数组映射 | map() |
| 过滤数组 | filter() |
| 查找元素 | find() / includes() |
| 检查条件 | some() / every() |
| 数组排序 | sort() / toSorted() |
| 数组反转 | reverse() / toReversed() |
| 数组拼接 | concat() |
| 数组切片 | slice() |
| 数组转字符串 | join() |
| 数组累加 | reduce() |
| 数组扁平化 | flat() / flatMap() |
| 不改变原数组的修改 | with() / toSpliced() |
3.7.2:Date对象
JavaScript Date 对象用于处理日期和时间。它基于 Unix 时间戳(1970年1月1日 UTC 以来的毫秒数)
⚠️ 月份从 0 开始(0=一月,11=十二月),星期从 0 开始(0=周日,6=周六)
⚠️ 时间基于本地时区,除非指定 UTC
Data对象的创建
| 创建方式 | 语法示例 | 说明 |
|---|---|---|
| 当前时间 | new Date() | 当前日期和时间 |
| 时间戳 | new Date(timestamp) | 毫秒时间戳 |
| 日期字符串 | new Date(dateString) | 可解析的日期字符串 |
| 年月日时分秒 | new Date(year, month[, day, hour, minute, second, ms]) | 指定各部分 |
| UTC时间 | new Date(Date.UTC(...)) | 创建UTC时间 |
js
// 1. 当前时间
const now = new Date(); // 当前日期时间
// 2. 时间戳(毫秒)
const timestamp = new Date(1609459200000); // 2021-01-01 00:00:00
// 3. 日期字符串(推荐标准格式)
const dateStr = new Date('2024-12-25T10:30:00');
const dateStr2 = new Date('December 25, 2024 10:30:00');
// 4. 指定参数
const specificDate = new Date(2024, 11, 25, 10, 30, 0); // 注意:11表示12月
// 5. UTC时间
const utcDate = new Date(Date.UTC(2024, 11, 25, 10, 30, 0));
Date对象方法
时间获取方法和设置方法,分别是getxxx和setxxx,现代编译器都有代码提示,非常好理解
这里列举下格式化输出的方法,工作中操作时间对象的时候可能会遇到转化的情况:
| 方法 | 返回格式 | 示例(北京时间) |
|---|---|---|
| toString() | 完整日期时间字符串 | "Wed Dec 25 2024 14:30:45 GMT+0800 (中国标准时间)" |
| toDateString() | 日期部分字符串 | "Wed Dec 25 2024" |
| toTimeString() | 时间部分字符串 | "14:30:45 GMT+0800 (中国标准时间)" |
| toLocaleString() | 本地格式日期时间 | "2024/12/25 14:30:45" |
| toLocaleDateString() | 本地格式日期 | "2024/12/25" |
| toLocaleTimeString() | 本地格式时间 | "14:30:45" |
| toUTCString() | UTC格式字符串 | "Wed, 25 Dec 2024 06:30:45 GMT" |
| toISOString() | ISO 8601格式 | "2024-12-25T06:30:45.000Z" |
| toJSON() | JSON格式(同ISO) | "2024-12-25T06:30:45.000Z" |
常用工具方法
js
// 是否同一天
function isSameDay(date1, date2) {
return date1.getFullYear() === date2.getFullYear() &&
date1.getMonth() === date2.getMonth() &&
date1.getDate() === date2.getDate();
}
// 加减天数
const addDays = (date, days) => {
const result = new Date(date);
result.setDate(result.getDate() + days);
return result;
};
// 加减月份
const addMonths = (date, months) => {
const result = new Date(date);
result.setMonth(result.getMonth() + months);
return result;
};
// 计算日期差(天数)
const dateDiffInDays = (date1, date2) => {
const timeDiff = Math.abs(date2.getTime() - date1.getTime());
return Math.ceil(timeDiff / (1000 * 60 * 60 * 24));
};
// 常用格式化函数
function formatDate(date, format = 'YYYY-MM-DD') {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
const formats = {
'YYYY-MM-DD': `${year}-${month}-${day}`,
'YYYY/MM/DD': `${year}/${month}/${day}`,
'YYYY-MM-DD HH:mm:ss': `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`,
'HH:mm:ss': `${hours}:${minutes}:${seconds}`,
'MM/DD/YYYY': `${month}/${day}/${year}`,
};
return formats[format] || `${year}-${month}-${day}`;
}
// 星期几
function getChineseDay(date) {
const days = ['日', '一', '二', '三', '四', '五', '六'];
return `星期${days[date.getDay()]}`;
}
// 日期有效性验证
function isValidDate(date) {
return date instanceof Date && !isNaN(date.getTime());
}
3.7.3:Map映射对象
键值对集合,键可以是任意类型,保持插入顺序, map一般用于缓存计算结果和数据分组,分组的依据就是Key
| 创建方式 | 语法 | 示例 |
|---|---|---|
| 构造函数 | new Map() |
new Map() |
| 带初始值 | new Map(iterable) |
new Map([['a',1],['b',2]]) |
| 核心方法如下: |
| 方法 | 描述 | 返回值 | 是否改变原Map |
|---|---|---|---|
| set(key, value) | 设置键值对 | Map对象 | ✅ |
| get(key) | 获取键对应的值 | value 或 undefined | ❌ |
| has(key) | 检查键是否存在 | boolean | ❌ |
| delete(key) | 删除键值对 | boolean | ✅ |
| clear() | 清空所有键值对 | undefined | ✅ |
| size | 获取元素数量 | number | - |
| keys() | 返回键的迭代器 | Iterator | ❌ |
| values() | 返回值的迭代器 | Iterator | ❌ |
| entries() | 返回键值对的迭代器 | Iterator | ❌ |
| forEach() | 遍历Map | undefined | ❌ |
3.7.4:set集合对象
存储唯一值的集合,保持插入顺序。set是特殊的map, 可以想象成map的key就是set
set一般用于数据去重(唯一性)和标签之类的存储,还有就是权限检查等等...
| 创建方式 | 语法 | 示例 |
|---|---|---|
| 构造函数 | new Set() |
new Set() |
| 带初始值 | new Set(iterable) |
new Set([1,2,3,2,1]) |
| 核心方法如下: | ||
| 方法 | 描述 | 返回值 |
| --------------- | --------------------- | ----------- |
| add(value) | 添加值 | Set对象 |
| has(value) | 检查值是否存在 | boolean |
| delete(value) | 删除值 | boolean |
| clear() | 清空所有值 | undefined |
| size | 获取元素数量 | number |
| values() | 返回值的迭代器 | Iterator |
| keys() | 同values() | Iterator |
| entries() | 返回[value, value]迭代器 | Iterator |
| forEach() | 遍历Set | undefined |
js
// 并集
function union(setA, setB) {
return new Set([...setA, ...setB]);
}
// 交集
function intersection(setA, setB) {
return new Set([...setA].filter(x => setB.has(x)));
}
// 差集
function difference(setA, setB) {
return new Set([...setA].filter(x => !setB.has(x)));
}
// 对称差集
function symmetricDifference(setA, setB) {
return union(difference(setA, setB), difference(setB, setA));
}
// 子集检查
function isSubset(setA, setB) {
return [...setA].every(x => setB.has(x));
}
3.7.5:Math数学对象
| 常量 | 描述 | 近似值 |
|---|---|---|
| Math.PI | 圆周率 | 3.141592653589793 |
| Math.E | 自然常数 | 2.718281828459045 |
| Math.LN2 | 2的自然对数 | 0.6931471805599453 |
| Math.LN10 | 10的自然对数 | 2.302585092994046 |
| Math.LOG2E | 以2为底E的对数 | 1.4426950408889634 |
| Math.LOG10E | 以10为底E的对数 | 0.4342944819032518 |
| Math.SQRT2 | 2的平方根 | 1.4142135623730951 |
| Math.SQRT1_2 | 1/2的平方根 | 0.7071067811865476 |
舍入方法,最大最小值啥的那一套,不再赘述列举...
js
// 生成随机数
function randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function randomFloat(min, max) {
return Math.random() * (max - min) + min;
}
// 限制数值范围
function clamp(value, min, max) {
return Math.max(min, Math.min(max, value));
}
// 线性插值
function lerp(start, end, t) {
return start + (end - start) * t;
}
// 映射数值范围
function map(value, fromMin, fromMax, toMin, toMax) {
return (value - fromMin) * (toMax - toMin) / (fromMax - fromMin) + toMin;
}
// 计算两点距离
function distance(x1, y1, x2, y2) {
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
}
// 角度与弧度转换
function toRadians(degrees) {
return degrees * Math.PI / 180;
}
function toDegrees(radians) {
return radians * 180 / Math.PI;
}
// 概率相关
function probability(percent) {
return Math.random() < percent / 100;
}
function weightedRandom(weights) {
const total = weights.reduce((sum, weight) => sum + weight, 0);
const random = Math.random() * total;
let sum = 0;
for (let i = 0; i < weights.length; i++) {
sum += weights[i];
if (random < sum) return i;
}
return weights.length - 1;
}
3.7.6:Reg正则对象
正则语法不再赘述,这里只说明js中正则对象相关的方法
| 方法 | 描述 | 返回值 | 是否改变原对象 |
|---|---|---|---|
| test(string) | 测试是否匹配 | boolean | ❌ |
| exec(string) | 执行匹配搜索 | Array/null | ❌ |
| String.match(regexp) | 字符串匹配 | Array/null | ❌ |
| String.matchAll(regexp) | 匹配所有 | Iterator | ❌ |
| String.replace(regexp, replacement) | 替换匹配文本 | string | ❌ |
| String.replaceAll(regexp, replacement) | 替换所有匹配 | string | ❌ |
| String.search(regexp) | 搜索匹配位置 | number | ❌ |
| String.split(regexp) | 使用正则分割 | Array | ❌ |
js
// 常用验证正则
const patterns = {
email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
phoneCN: /^1[3-9]\d{9}$/,
phoneUS: /^\+1\s?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$/,
url: /^(https?:\/\/)?([\w-]+\.)+[\w-]+(\/[\w-./?%&=]*)?$/,
ipv4: /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/,
strongPassword: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/,
dateYYYYMMDD: /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/,
hexColor: /^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/,
username: /^[a-zA-Z0-9_]{3,20}$/,
};
// 正则工具函数
function testPattern(pattern, text) {
return pattern.test(text);
}
function extractMatches(pattern, text) {
const matches = [];
let match;
while ((match = pattern.exec(text)) !== null) {
matches.push(match[0]);
}
return matches;
}
function replaceWithCallback(pattern, text, callback) {
return text.replace(pattern, callback);
}
// 复杂正则示例:提取CSS颜色值
function extractCSSColors(css) {
const colorPattern = /#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})|rgb\((\d+),\s*(\d+),\s*(\d+)\)|rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)/g;
return css.match(colorPattern) || [];
}
// 正则构建器(动态创建正则)
function buildRegex(patterns, flags = 'i') {
const escapedPatterns = patterns.map(p =>
p.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
);
return new RegExp(`(?:${escapedPatterns.join('|')})`, flags);
}
3.7.7:Error错误对象
表示运行时错误,用于异常处理
先了解下错误的类型:
| 类型 | 描述 | 触发场景 |
|---|---|---|
| Error | 通用错误 | 自定义错误 |
| SyntaxError | 语法错误 | 代码语法问题 |
| TypeError | 类型错误 | 类型不匹配 |
| ReferenceError | 引用错误 | 未定义变量 |
| RangeError | 范围错误 | 数值超出范围 |
| URIError | URI错误 | URI处理错误 |
| EvalError | eval错误 | eval函数错误 |
| AggregateError | 聚合错误 | 多个错误集合 |
| 下面的都是Error的子对象类型 |
核心属性:name / message / stack / cause
看了下面的使用例子就知道如何处理错误对象了,要么就是throw出去,要么就是try...catch住,然后对catch块处理
js
// 基础错误处理
try {
// 可能出错的代码
throw new Error('Something went wrong');
} catch (error) {
console.error('错误名称:', error.name);
console.error('错误信息:', error.message);
console.error('堆栈跟踪:', error.stack);
// 处理错误
} finally {
// 无论是否出错都会执行
console.log('清理工作');
}
// 自定义错误类型
class ValidationError extends Error {
constructor(message, field) {
super(message);
this.name = 'ValidationError';
this.field = field;
this.timestamp = new Date().toISOString();
}
// 原型方法
toJSON() {
return {
name: this.name,
message: this.message,
field: this.field,
timestamp: this.timestamp,
stack: this.stack
};
}
}
// 使用自定义错误
function validateUser(user) {
if (!user.name) {
throw new ValidationError('姓名不能为空', 'name');
}
if (user.age < 0) {
throw new ValidationError('年龄不能为负数', 'age');
}
}
// 错误链(ES2022)
function processData(data) {
try {
return JSON.parse(data);
} catch (error) {
throw new Error('数据处理失败', { cause: error });
}
}
// 异步错误处理
async function fetchData(url) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('获取数据失败:', error);
throw error; // 重新抛出
}
}
// Promise错误处理
fetchData('/api/data')
.then(data => console.log(data))
.catch(error => console.error('Promise错误:', error));
// 全局错误处理
window.addEventListener('error', (event) => {
console.error('全局错误:', event.error);
// 可以发送到错误监控服务
sendToErrorMonitoring(event.error);
});
// 未处理的Promise拒绝
window.addEventListener('unhandledrejection', (event) => {
console.error('未处理的Promise拒绝:', event.reason);
});
3.7.8:Json对象
处理JSON结构,非常的简单,核心方法就是两个
| 方法 | 描述 | 返回值 |
|---|---|---|
JSON.parse(text[, reviver]) |
解析JSON字符串 | JavaScript值 |
JSON.stringify(value[, replacer[, space]]) |
转为JSON字符串 | string |
4:DOM(⭐️⭐️⭐️⭐️⭐️⭐️)
当网页被加载时,浏览器会创建页面的文档对象模型(Document Object Model)。HTML DOM 模型被结构化为 对象树

通过DOM树,JS获得创建动态 HTML 的所有力量
4.1:元素定位(⭐️⭐️⭐️⭐️⭐️⭐️)
只有定位到元素,才能对元素进行操作,因此DOM提供了一系列选择器相关的方法定位HTML元素
| 方法 | 描述 |
|---|---|
document.getElementById(id) |
通过元素 id 来查找元素。 |
document.getElementsByTagName(name) |
通过标签名来查找元素 |
document.getElementsByClassName(name) |
通过类名来查找元素 |
document.querySelector(CSS选择器) |
通过CSS选择器选择一个元素 |
document.querySelectorAll(CSS选择器) |
通过CSS选择器选择多个元素 |
4.2:元素操作(⭐️⭐️)
定位到元素之后,就可以对元素的内容和属性进行操作了
获取HTML元素的值和属性
| 方法 | 描述 |
|---|---|
元素节点.innerText |
获取 HTML 元素的 inner Text。 |
元素节点.innerHTML |
获取 HTML 元素的 inner HTML。 |
元素节点.属性 |
获取 HTML 元素的属性值。 |
元素节点.getAttribute([attribute]) |
获取 HTML 元素的属性值。 |
元素节点.style.样式 |
获取 HTML 元素的行内样式值。 |
更改HTML元素的值和属性
| 方法 | 描述 |
|---|---|
元素节点.innerText = new text content |
改变元素的 inner Text |
元素节点.innerHTML = new html content |
改变元素的 inner HTML |
元素节点.属性 = new value |
改变 HTML 元素的属性值 |
元素节点.setAttribute(attribute, value) |
改变 HTML 元素的属性值 |
元素节点.style.样式 = new style |
改变 HTML 元素的行内样式值 |
增、删、替换HTML元素
| 方法 | 描述 |
|---|---|
document.createElement(element) |
创建 HTML 元素节点。 |
document.createAttribute(attribute) |
创建 HTML 属性节点。 |
document.createTextNode(text) |
创建 HTML 文本节点。 |
元素节点.removeChild(element) |
删除 HTML 元素。 |
元素节点.appendChild(element) |
添加 HTML 元素。 |
元素节点.replaceChild(element) |
替换 HTML 元素。 |
元素节点.insertBefore(element) |
在指定的子节点前面插入新的子节点。 |
定位关联元素
| 方法 | 描述 |
|---|---|
元素节点.parentNode |
返回元素的父节点。 |
元素节点.parentElement |
返回元素的父元素。 |
元素节点.childNodes |
返回元素的一个子节点的数组(包含空白文本Text节点)。 |
元素节点.children |
返回元素的一个子元素的集合(不包含空白文本Text节点)。 |
元素节点.firstChild |
返回元素的第一个子节点(包含空白文本Text节点)。 |
元素节点.firstElementChild |
返回元素的第一个子元素(不包含空白文本Text节点)。 |
元素节点.lastChild |
返回元素的最后一个子节点(包含空白文本Text节点)。 |
元素节点.lastElementChild |
返回元素的最后一个子元素(不包含空白文本Text节点)。 |
元素节点.previousSibling |
返回某个元素紧接之前节点(包含空白文本Text节点)。 |
元素节点.previousElementSibling |
返回指定元素的前一个兄弟元素(相同节点树层中的前一个元素节点)。 |
元素节点.nextSibling |
返回某个元素紧接之后节点(包含空白文本Text节点)。 |
元素节点.nextElementSibling |
返回指定元素的后一个兄弟元素(相同节点树层中的下一个元素节点)。 |
js
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="box">
<ul id="ul">
<li><a href="https://www.baidu.com">我是超链接1</a></li>
<li id="two"><a href="https://www.baidu.com">我是超链接2</a></li>
<li><a href="https://www.baidu.com">我是超链接3</a></li>
<li><a href="https://www.baidu.com">我是超链接4</a></li>
</ul>
</div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var box = document.getElementById("box");
var ul = document.getElementById("ul");
var two = document.getElementById("two");
console.log(ul.parentNode);
console.log(ul.parentElement);
console.log("===============");
console.log(box.childNodes);
console.log(box.children);
console.log("===============");
console.log(ul.firstChild);
console.log(ul.firstElementChild);
console.log(ul.lastChild);
console.log(ul.lastElementChild);
console.log("===============");
console.log(two.previousSibling);
console.log(two.previousElementSibling);
console.log(two.nextSibling);
console.log(two.nextElementSibling);
</script>
</body>
</html>
4.3:事件响应(⭐️⭐️⭐️⭐️⭐️⭐️)
HTML事件可以触发浏览器中的行为,比方说当用户点击某个 HTML 元素时启动一段 JavaScript。
这个才是实际开发中经常使用的!!!
4.3.1:窗口事件
由窗口触发该事件 (同样适用于 <body> 标签):
| 属性 | 描述 |
|---|---|
| onblur | 当窗口失去焦点时运行脚本。 |
| onfocus | 当窗口获得焦点时运行脚本。 |
| onload | 当文档加载之后运行脚本。 |
| onresize | 当调整窗口大小时运行脚本。 |
| onstorage | 当 Web Storage 区域更新时(存储空间中的数据发生变化时)运行脚本。 |
4.3.2:表单事件(⭐️⭐️⭐️⭐️⭐️)
单事件在HTML表单中触发 (适用于所有 HTML 元素,但该HTML元素需在form表单内):
| 属性 | 描述 |
|---|---|
| onblur | 当元素失去焦点时运行脚本。 |
| onfocus | 当元素获得焦点时运行脚本。 |
| onchange | 当元素改变时运行脚本。 |
| oninput | 当元素获得用户输入时运行脚本。 |
| oninvalid | 当元素无效时运行脚本。 |
| onselect | 当选取元素时运行脚本。 |
| onsubmit | 当提交表单时运行脚本。 |
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form>
<input type="text" id="text">
</form>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var textInput = document.getElementById("text");
/* 当文本框获取焦点,文本框背景为红色 */
textInput.onfocus = function () {
this.style.background = "red";
};
/* 当文本框失去焦点,文本框背景为绿色 */
textInput.onblur = function () {
this.style.background = "green";
};
</script>
</body>
</html>
4.3.3:键盘事件
| 属性 | 描述 |
|---|---|
| onkeydown | 当按下按键时运行脚本。 |
| onkeyup | 当松开按键时运行脚本。 |
| onkeypress | 当按下并松开按键时运行脚本。 |
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
/* 当键盘按下判断当前的按键是不是 a ,如果是就输出true,否则输出false */
window.onkeydown = function (event) {
/* 解决兼容性问题 */
event = event || window.event;
if (event.keyCode == 65) {
console.log("true");
} else {
console.log("false");
}
};
</script>
</body>
</html>
4.3.4:事件对象event(⭐️⭐️⭐️⭐️⭐️⭐️)
概述
Event 对象代表事件的状态,比如事件在其中发生的元素、键盘按键的状态、鼠标的位置、鼠标的状态
事件对象 (event):在事件处理程序中传递的参数,包含与事件相关的所有信息。
主要用途:
- 获取事件类型、目标元素、鼠标位置、键盘按键等。
- 控制事件的传播和默认行为。
常见属性:
event.type:事件类型。event.target和event.currentTarget:事件目标。event.clientX和event.clientY:鼠标位置。event.button:鼠标按钮。event.key和event.code:键盘按键。event.stopPropagation()和event.preventDefault():控制事件传播和默认行为。
在IE8中,响应函数被触发时,浏览器不会传递事件对象,在IE8及以下的浏览器中,是将事件对象作为window对象的属性保存的。
解决事件对象的兼容性问题:event = event || window.event;
事件冒泡
事件的冒泡(Bubble):所谓的冒泡指的就是事件的向上传导,当后代元素上的事件被触发时,其祖先元素的相同事件也会被触发,在开发中大部分情况冒泡都是有用的,如果不希望发生事件冒泡可以通过事件对象来取消冒泡。
js
function stopBubble(event) {
// 如果提供了事件对象,则这是一个非IE浏览器
if (event && event.stopPropagation) {
// 因此它支持W3C的stopPropagation()方法
event.stopPropagation();
} else {
// 否则,我们需要使用IE的方式来取消事件冒泡
window.event.cancelBubble = true;
}
}
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
#div1 {
width: 200px;
height: 200px;
background: pink;
}
#div2 {
width: 100px;
height: 100px;
background: coral;
}
</style>
</head>
<body>
<div id="div1">
我是DIV1
<div id="div2">
我是DIV2
</div>
</div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var div1 = document.getElementById("div1");
var div2 = document.getElementById("div2");
// 为div1绑定单击事件
div1.onclick = function () {
console.log("div1 的单击事件触发了!");
stopBubble();
};
// 为div2绑定单击事件
div2.onclick = function () {
console.log("div2 的单击事件触发了!");
stopBubble();
};
// 取消事件冒泡
function stopBubble(event) {
// 如果提供了事件对象,则这是一个非IE浏览器
if (event && event.stopPropagation) {
// 因此它支持W3C的stopPropagation()方法
event.stopPropagation();
} else {
// 否则,我们需要使用IE的方式来取消事件冒泡
window.event.cancelBubble = true;
}
}
</script>
</body>
</html>
事件委派
反之,我们可以利用事件的冒泡,完成事件的委派
所谓事件的委派,是指将事件统一绑定给元素的共同的祖先元素,这样当后代元素上的事件触发时,会一直冒泡到祖先元素,从而通过祖先元素的响应函数来处理事件。
当我们希望只绑定一次事件,即可应用到多个的元素上,即使元素是后添加的,我们可以尝试将其绑定给元素的共同的祖先元素,这样通过冒泡到祖先元素,就可以通过祖先元素的响应函数来处理事件
事件委派是利用了事件冒泡,通过委派可以减少事件绑定的次数,提高程序的性能。
js
<!-- 为ul列表中的所有a标签都绑定单击事件 -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<ul id="u1">
<li><a href="javascript:;" class="link">超链接一</a></li>
<li><a href="javascript:;" class="link">超链接二</a></li>
<li><a href="javascript:;" class="link">超链接三</a></li>
</ul>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
var u1 = document.getElementById("u1");
// 为ul绑定一个单击响应函数
u1.onclick = function (event) {
event = event || window.event;
// 如果触发事件的对象是我们期望的元素,则执行,否则不执行
if (event.target.className == "link") {
console.log("我是ul的单击响应函数");
}
};
</script>
</body>
</html>
事件解绑
我们以前绑定事件代码只能一个事件绑定一个函数,那我们要是想一个事件对应多个函数,并且不存在兼容性的问题该如何解决呢?
此时就可以写两个公共方法,一个用于事件的绑定,一个用于事件的解绑
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button id="btn1">按钮1</button>
<button id="btn2">按钮2</button>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
function f1() {
console.log("output1 ...");
};
function f2() {
console.log("output2 ...");
};
// 为按钮1的单击事件绑定两个函数
addEventListener(document.getElementById("btn1"), "click", f1);
addEventListener(document.getElementById("btn1"), "click", f2);
// 点击按钮2取消按钮1的单机事件绑定函数f1
document.getElementById("btn2").onclick = function () {
removeEventListener(document.getElementById("btn1"), "click", f1);
};
/*为元素绑定事件兼容性代码*/
function addEventListener(element, type, fn) {
if (element.addEventListener) {
// 如果希望在捕获阶段就触发事件,这里的第三个参数设置为true
element.addEventListener(type, fn, false);
} else if (element.attachEvent) {
element.attachEvent("on" + type, fn);
} else {
element["on" + type] = fn;
}
}
/*为元素解绑事件兼容性代码*/
function removeEventListener(element, type, fnName) {
if (element.removeEventListener) {
element.removeEventListener(type, fnName, false);
} else if (element.detachEvent) {
element.detachEvent("on" + type, fnName);
} else {
element["on" + type] = null;
}
}
</script>
</body>
</html>
事件传播
W3C将事件传播分成了三个阶段:
- 捕获阶段:在捕获阶段时从最外层的祖先元素,向目标元素进行事件的捕获,但是默认此时不会触发事件
- 目标阶段:事件捕获到目标元素,捕获结束开始在目标元素上触发事件
- 冒泡阶段:事件从目标元素向它的祖先元素传递,依次触发祖先元素上的事件
如果希望在捕获阶段就触发事件,可以将addEventListener()的第三个参数设置为true
一般情况下我们不会希望在捕获阶段触发事件,所以这个参数一般都是false
4.3.5:鼠标事件(⭐️⭐️⭐️⭐️⭐️⭐️)
| 属性 | 描述 |
|---|---|
| onclick | 当单击鼠标时运行脚本 |
| ondblclick | 当双击鼠标时运行脚本。 |
| onmousedown | 当按下鼠标按钮时运行脚本。 |
| onmouseup | 当松开鼠标按钮时运行脚本。 |
| onmousemove | 当鼠标指针移动时运行脚本。 |
| onmouseover | 当鼠标指针移至元素之上时运行脚本,不可以阻止冒泡。 |
| onmouseout | 当鼠标指针移出元素时运行脚本,不可以阻止冒泡。 |
| onmouseenter | 当鼠标指针移至元素之上时运行脚本,可以阻止冒泡。 |
| onmouseleave | 当鼠标指针移出元素时运行脚本,可以阻止冒泡。 |
| onmousewheel | 当转动鼠标滚轮时运行脚本。 |
| onscroll | 当滚动元素的滚动条时运行脚本。 |
| 4.3.6:媒体事件 |
通过视频(videos),图像(images)或音频(audio) 触发该事件。
| 事件名称 | 触发时机 | 使用场景 |
|---|---|---|
| onplay(⭐️) | 媒体开始播放时触发 | 播放状态跟踪,记录播放开始时间 |
| onpause(⭐️) | 媒体暂停时触发 | 暂停状态跟踪,暂停时保存播放位置 |
| onended(⭐️) | 媒体播放到结尾时触发 | 播放结束时自动播放下一个或显示结束界面 |
| ontimeupdate(⭐️) | 媒体当前播放位置改变时触发(每秒触发4-10次) | 更新进度条、显示当前播放时间 |
| onloadedmetadata(⭐️) | 媒体元数据(时长、尺寸等)加载完成时触发 | 获取视频/音频总时长、分辨率等信息 |
| onloadeddata | 当前帧的媒体数据加载完成时触发 | 媒体可以开始播放时初始化播放器 |
| oncanplay | 媒体可以开始播放时触发(可能有缓冲) | 启用播放按钮,或触发自动播放 |
| onvolumechange | 媒体音量改变或被设置为静音时触发 | 更新音量控制UI,同步静音状态 |
| onerror(⭐️) | 媒体加载或播放过程中发生错误时触发 | 错误处理,显示错误提示信息 |
| onwaiting | 媒体因缓冲而暂停播放(等待数据)时触发 | 显示加载动画,提示用户正在缓冲 |
| onplaying | 媒体实际开始播放时触发 | 与onplay配合,确认媒体正在播放 |
| onseeking | 用户开始手动跳转播放位置时触发 | 显示跳转中的状态提示 |
| onseeked | 用户跳转播放位置完成时触发 | 隐藏跳转提示,恢复播放 |
js
const video = document.getElementById('myVideo');
video.onplay = () => console.log('开始播放');
video.onpause = () => console.log('已暂停');
video.onended = () => console.log('播放结束');
// 进度条更新(核心功能)
video.ontimeupdate = () => {
const progress = (video.currentTime / video.duration) * 100;
progressBar.style.width = `${progress}%`;
};
// 获取视频信息
video.onloadedmetadata = () => {
console.log(`时长: ${video.duration}秒`);
console.log(`分辨率: ${video.videoWidth}x${video.videoHeight}`);
};
5:BOM
浏览器对象模型(BOM)可以使我们通过JS来操作浏览器,在BOM中为我们提供了一组对象,用来完成对浏览器的操作
这些BOM对象在浏览器中都是作为window对象的属性保存的,可以通过window对象来使用,也可以直接使用

5.1:全局对象window
弹出框,警告框和提示框
- 警告框 ->
window.alert(警告信息) - 确认框 ->
window.confirm(确认信息) - 提示框 ->
window.prompt("提示信息", 默认值)
定时事件(⭐️⭐️⭐️⭐️⭐️)
JavaScript 可以在时间间隔内执行,这就是所谓的定时事件(Timing Events)。
window 对象允许以指定的时间间隔执行代码,这些时间间隔称为定时事件。
js
setTimeout(function, milliseconds) // 在等待指定的毫秒数后执行函数。
setInterval(function, milliseconds) // 等同于 setTimeout(),但持续重复执行该函数。
// 例如
// 在按钮按下之后3s控制台打印Hello
btn.onclick = function () {
// 创建延时器
var timer = setTimeout(function () {
console.log("Hello");
}, 3000);
}
// 在按钮按下之后每过1s控制台打印Hello
btn.onclick = function () {
// 创建定时器
var timer = setInterval(function () {
console.log("Hello");
}, 1000);
// 清除定时器
// clearInterval(timer);
};
其他常用属性和方法
| 属性、方法 | 说明 |
|---|---|
window.innerHeight/window.innerWidth |
浏览器窗口的内高度/宽度 |
| window.open(url, name, specs, replace) | 打开新的窗口 |
| window.close() | 关闭当前的窗口 |
| window.removeTo(x, y) | 移动当前的窗口到(x, y) |
| window.resizeTo(w, h) | 调整当前窗口 |
5.2:浏览器信息
Navigator对象中的大部分属性都已经不能帮助我们识别浏览器了,一般我们只会使用userAgent来判断浏览器的信息
js
let ua = navigator.userAgent;
5.3:地址栏信息
Location对象中封装了浏览器的地址栏的信息,如果直接打印location,则可以获取到地址栏的信息
js
console.log(location); //输出location对象
console.log(location.href); //输出当前地址的全路径地址
console.log(location.origin); //输出当前地址的来源
console.log(location.protocol); //输出当前地址的协议
console.log(location.hostname); //输出当前地址的主机名
console.log(location.host); //输出当前地址的主机
console.log(location.port); //输出当前地址的端口号
console.log(location.pathname); //输出当前地址的路径部分
console.log(location.search); //输出当前地址的?后边的参数部分
// assign():用来跳转到其它的页面,作用和直接修改location一样
location.assign("https://www.baidu.com");
// reload():用于重新加载当前页面,作用和刷新按钮一样
// 如果在方法中传递一个true,作为参数,则会强制清空缓存刷新页面
location.reload(true);
// replace():可以使用一个新的页面替换当前页面,调用完毕也会跳转页面
// 它不会生成历史记录,不能使用回退按钮回退
location.replace("https://www.baidu.com")
5.4:历史信息
主要是对历史信息向前翻页,向后翻页,跳转到某一个历史记录
js
// back():可以回退到上一个页面,作用和浏览器的回退按钮一样
history.back();
// forward():可以跳转到下一个页面,作用和浏览器的前进按钮一样
history.forward();
// go():可以用来跳转到指定的页面,它需要一个整数作为参数
// 1:表示向前跳转一个页面,相当于forward()
// 2:表示向前跳转两个页面
// -1:表示向后跳转一个页面,相当于back()
// -2:表示向后跳转两个页面
history.go(3);
6:存储部分
6.1:cookie
Cookie 是一些数据,存储于你电脑上的文本文件中,当 web 服务器向浏览器发送 web 页面时,在连接关闭后,服务端不会记录用户的信息,Cookie 的作用就是用于解决 "如何记录客户端的用户信息":
- 当用户访问 web 页面时,它的名字可以记录在 cookie 中。
- 在用户下一次访问该页面时,可以在 cookie 中读取用户访问记录。
JavaScript 可以使用 document.cookie 属性来创建 、读取、及删除 Cookie。
js
// 设置和读取
document.cookie = "username=zhangsan";
let cookies = document.cookie; // 拿到cookie
console.log(cookies);
// 修改,同名key覆盖
document.cookie = "username=zhangsan";
document.cookie = "username=lisi"; // 同名key覆盖,value变成lisi
var cookies = document.cookie;
console.log(cookies);
js
/**
* Cookie值设置函数
* @param cname cookie名称
* @param cvalue cookie值
* @param exdays 过期天数
*/
function setCookie(cname, cvalue, exdays) {
// 创建一个时间对象并设置值
var d = new Date();
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
// 过期时间就是这个时间对象的GMT格式
var expires = "expires=" + d.toGMTString();
// 设置cookie
document.cookie = cname + "=" + cvalue + "; " + expires;
}
/**
* Cookie值获取函数
* @param cname cookie名称
* @returns {string}
*/
function getCookie(cname) {
var name = cname + "=";
// 将cookie通过;分割,得到每一个cookie
var ca = document.cookie.split(';');
// 遍历所有的cookie
for (var i = 0; i < ca.length; i++) {
// 前后去除空格
var c = ca[i].trim();
// 如果找到了对应的key
if (c.indexOf(name) == 0) {
// 通过截取,拿到value
return c.substring(name.length, c.length);
}
}
// 否则返回空串
return "";
}
6.2:webStorage
虽然WebStorage是HTML5新增的本地存储解决方案之一,但并不是为了取代Cookie而制定的标准
Cookie作为HTTP协议的一部分用来处理客户端和服务器通信是不可或缺的,session正是依赖于实现的客户端状态保持。
WebStorage的意图在于解决本来不应该Cookie做,却不得不用Cookie的本地存储的应用场景。
localStorage和sessionStorage只能存储字符串类型
localStorage在本地永久性存储数据,除非显式将其删除或清空。
| 方法 | 说明 |
|---|---|
| localStorage.setItem(key,value); | 保存单个数据 |
| localStorage.getItem(key); | 读取单个数据 |
| localStorage.removeItem(key); | 删除单个数据 |
| localStorage.clear(); | 删除所有数据 |
| localStorage.key(index); | 获取某个索引的key |
js
// 保存数据
localStorage.setItem("username", "zhangsan");
// 读取单个数据
console.log(localStorage.getItem("username")); // zhangsan
console.log("===============");
// 删除单个数据
localStorage.removeItem("username");
console.log(localStorage.getItem("username")); // null
console.log("===============");
// 保存两个数据
localStorage.setItem("age", 18);
localStorage.setItem("sex", "男");
console.log("age=" + localStorage.getItem("age")); // 18
console.log("sex=" + localStorage.getItem("sex")); // 男
console.log("===============");
// 使用for-in循环来迭代localStorage中的键值对、属性和方法:
for (let key in localStorage) {
console.log(key + "=" + localStorage[key]);
}
console.log("===============");
// 使用for循环来迭代localStorage中的键值对:
for (let i = 0; i < localStorage.length; i++) {
let key = localStorage.key(i);
let value = localStorage.getItem(key);
console.log(key + "=" + value);
}
console.log("===============");
// 删除所有数据
localStorage.clear();
7:其他新特性说明
7.1:变量解构赋值
js
//数组的解构赋值
const arr = ["张学友", "刘德华", "黎明", "郭富城"];
let [zhang, liu, li, guo] = arr; // 取了个别名
console.log(zhang); // "张学友"
console.log(liu); // "刘德华"
console.log(li); // "黎明"
console.log(guo); // "郭富城"
//对象的解构赋值
const lin = {
name: "林志颖",
tags: ["车手", "歌手", "小旋风", "演员"]
};
let {name, tags} = lin;
console.log(name); // 林志颖
console.log(tags); // ["车手", "歌手", "小旋风", "演员"]
7.2:箭头函数
箭头函数的注意点:
- 如果形参只有一个,则小括号可以省略
- 函数体如果只有一条语句,则花括号可以省略,函数的返回值为该条语句的执行结果
- 箭头函数 this 指向声明时所在作用域下 this 的值,箭头函数不会更改 this 指向,用来指定回调函数会非常合适
- 箭头函数不能作为构造函数实例化
- 不能使用 arguments 实参
js
let fn = (arg1, arg2, arg3) => {
return arg1 + arg2 + arg3;
}
// this 指向声明时所在作用域中 this 的值
let fn = () => {
console.log(this); // window
}
fn();
let school = {
name: "张三",
getName() {
let subFun = () => {
console.log(this); // {name: "张三", getName: f}
}
subFun();
}
};
school.getName();
7.3:rest参数和spread扩展运算符
rest 参数非常适合不定个数参数函数的场景
js
// 作用与 arguments 类似
function add(...args) {
console.log(args); // [1, 2, 3, 4, 5]
}
add(1, 2, 3, 4, 5);
// rest 参数必须是最后一个形参
function minus(a, b, ...args) {
console.log(a, b, args); // 100 1 [2, 3, 4, 5, 19]
}
minus(100, 1, 2, 3, 4, 5, 19);
扩展运算符(spread)也是三个点(...)
它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列,对数组进行解包。
js
// 展开数组
let tfboys = ["德玛西亚之力", "德玛西亚之翼", "德玛西亚皇子"];
function fn() {
console.log(arguments);
}
fn(...tfboys);
7.4:Symbol唯一性值
ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值
它是 JavaScript 语言的第七种数据类型,是一种类似于字符串的数据类型,Symbol 特点如下:
- Symbol 的值是唯一的,用来解决命名冲突的问题
- Symbol 值不能与其它数据进行运算
- Symbol 定义的对象属性不能使用 for...in 循环遍 历 ,但是可以使用 Reflect.ownKeys 来获取对象的所有键名
js
//创建 Symbol
let s1 = Symbol();
console.log(s1); // Symbol()
console.log(typeof s1); // symbol
//添加标识的 Symbol
let s2 = Symbol("张三");
let s2_2 = Symbol("张三");
console.log(s2); // Symbol(张三)
console.log(s2_2); // Symbol(张三)
console.log(s2 === s2_2); // false
//使用 Symbol for 定义
let s3 = Symbol.for("张三");
let s3_2 = Symbol.for("张三");
console.log(s3); //Symbol(张三)
console.log(s3_2); // Symbol(张三)
console.log(s3 === s3_2); // true
//在方法中使用 Symbol
let game = {
name: "狼人杀",
[Symbol('say')]: function () {
console.log("我可以发言")
},
[Symbol('zibao')]: function () {
console.log('我可以自爆');
}
};
console.log(game);
7.5:Promise异步(⭐️⭐️⭐️⭐️)
语法上 Promise 是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果
常用于调用后端接口
js
// 模拟调用后端接口的函数,返回一个 Promise
function fetchDataFromAPI(url) {
return new Promise((resolve, reject) => {
// 模拟异步请求
setTimeout(() => {
const mockResponse = {
status: 200,
data: {
id: 1,
name: '张三',
email: 'zhangsan@example.com'
}
};
// 模拟成功响应
if (url === '/api/user') {
resolve(mockResponse); // 状态变为 Fulfilled
} else {
// 模拟错误
reject(new Error('404: 接口未找到')); // 状态变为 Rejected
}
}, 1000); // 模拟网络延迟
});
}
// 使用 Promise 调用接口
fetchDataFromAPI('/api/user')
// fulfilled状态下的入口,参数是resolve中的参数mockResponse
.then((response) => {
console.log('请求成功:', response);
// 处理响应数据
const userData = response.data;
console.log('用户数据:', userData);
console.log(`用户名: ${userData.name}, 邮箱: ${userData.email}`);
// 返回处理后的数据,供下一个 then 使用
return userData;
})
// 链式调用,参数是上一个then的return
.then((userData) => {
// 链式调用,进一步处理数据
console.log('数据进一步处理完成:', userData);
})
// rejected状态下的入口,参数是reject的参数
.catch((error) => {
// 捕获所有错误
console.error('请求失败:', error.message);
})
.finally(() => {
// 无论成功或失败都会执行
console.log('请求结束,清理工作...');
});
调用 then 方法,then 方法的返回结果是 Promise 对象,对象状态由回调函数的执行结果决定,如果回调函数中返回的结果是 非 promise 类型的属性,状态为成功,返回值为对象的成功的值
如果只想处理错误状态,我们可以使用 catch 方法
Promise的静态方法
- Promise.all() - 等待所有 Promise 完成
- Promise.race() - 竞速,第一个完成或失败的
- Promise.allSettled() - 等待所有 Promise 完成(无论成功失败)
- Promise.any() - 第一个成功的 Promise
- Promise.resolve() 和 Promise.reject()
js
// 同时调用多个接口,等待所有都完成
const promise1 = fetch('/api/user');
const promise2 = fetch('/api/orders');
const promise3 = fetch('/api/settings');
Promise.all([promise1, promise2, promise3])
.then(([userData, ordersData, settingsData]) => {
console.log('所有数据加载完成');
console.log('用户:', userData);
console.log('订单:', ordersData);
console.log('设置:', settingsData);
})
.catch(error => {
console.error('有一个请求失败了:', error);
// 只要有一个失败,整个 Promise.all 就失败
});
// 实际示例
const userId = 1;
Promise.all([
getUserInfo(userId),
getUserOrders(userId),
getUserMessages(userId)
])
.then(([info, orders, messages]) => {
// 所有数据都已获取
renderUserProfile(info, orders, messages);
});
7.6:模块化(export / import)
想象一下你的代码是一个工具箱:
- export = 把工具放到工具箱外面让别人用
- import = 从别人的工具箱里拿工具用
- 以前所有工具堆在一起,现在分门别类放好,用的时候再拿
js
// math.js - 导出模块
// 方式1:命名导出(可以导出多个)
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export function multiply(a, b) {
return a * b;
}
// 方式2:默认导出(一个文件只能有一个)
const calculator = {
add,
multiply,
PI
};
export default calculator;
// main.js - 导入模块
// 导入命名导出
import { PI, add } from './math.js';
console.log(PI); // 3.14159
console.log(add(2, 3)); // 5
// 导入默认导出
import calc from './math.js';
console.log(calc.multiply(2, 3)); // 6
// 导入全部
import * as MathUtils from './math.js';
console.log(MathUtils.PI); // 3.14159
7.7:async和await
- 异步 = "你先忙,我继续干别的,你好了叫我"
- async/await = "我先暂停,等你好了我再继续"(代码看起来像同步,实际是异步)
- 让异步代码读起来像同步代码一样简单
js
// 传统 Promise 写法(回调地狱的解决方案)
function fetchData() {
return fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
console.log(data);
return data;
})
.catch(error => {
console.error('Error:', error);
});
}
// async/await 写法(更直观)
async function fetchDataWithAsync() {
try {
// await = "等待这个操作完成"
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
return data;
} catch (error) {
console.error('Error:', error);
}
}
// 实际使用示例
async function getUserAndPosts(userId) {
// 顺序执行,第二个等第一个完成
const user = await fetch(`/api/users/${userId}`);
const posts = await fetch(`/api/users/${userId}/posts`);
// 并行执行,同时发起请求
const [userData, postsData] = await Promise.all([
user.json(),
posts.json()
]);
return { user: userData, posts: postsData };
}
// async 函数总是返回 Promise
fetchDataWithAsync().then(result => {
console.log('Got result:', result);
});
8:后端交互axios
- axios = 专业的快递小哥,专门帮你发送和接收数据
- 比浏览器自带的 fetch 更好用(自动转换JSON、错误处理等)
- 可以设置统一的规则(比如所有请求都带上认证信息)
8.1:axios请求方式((⭐️⭐️⭐️⭐️⭐️)
js
import axios from 'axios';
// 1. GET 请求(获取数据)
axios.get('https://api.example.com/users')
.then(response => {
console.log(response.data); // 服务器返回的数据
})
.catch(error => {
console.error('请求失败:', error);
});
// 2. POST 请求(提交数据)
axios.post('https://api.example.com/users', {
name: '张三',
age: 25
})
.then(response => {
console.log('创建成功:', response.data);
});
// 3. PUT 请求(更新整个资源)
axios.put('https://api.example.com/users/1', {
name: '张三',
age: 26 // 更新年龄
});
// 4. PATCH 请求(更新部分资源)
axios.patch('https://api.example.com/users/1', {
age: 26 // 只更新年龄字段
});
// 5. DELETE 请求(删除数据)
axios.delete('https://api.example.com/users/1');
// 6. 并发请求(同时发送多个请求)
axios.all([
axios.get('/api/users'),
axios.get('/api/posts')
])
.then(axios.spread((usersRes, postsRes) => {
console.log('Users:', usersRes.data);
console.log('Posts:', postsRes.data);
}));
8.2:axios实例
js
// 创建自定义实例(统一配置)
const apiClient = axios.create({
baseURL: 'https://api.example.com', // 基础URL
timeout: 5000, // 5秒超时
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer your-token-here'
}
});
// 使用实例发送请求
apiClient.get('/users'); // 实际请求:https://api.example.com/users
apiClient.post('/login', {
username: 'admin',
password: '123456'
});
// 不同API使用不同实例
const authApi = axios.create({
baseURL: 'https://auth.example.com',
timeout: 3000
});
const dataApi = axios.create({
baseURL: 'https://data.example.com',
timeout: 10000
});
8.3:拦截器
js
// 请求拦截器(发送请求前做点什么)
axios.interceptors.request.use(
config => {
// 在发送请求前
console.log('正在发送请求:', config.url);
// 添加token到请求头
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
// 可以修改请求配置
config.headers['X-Custom-Header'] = 'custom-value';
return config; // 必须返回配置
},
error => {
// 请求错误处理
console.error('请求出错:', error);
return Promise.reject(error);
}
);
// 响应拦截器(收到响应后做点什么)
axios.interceptors.response.use(
response => {
// 2xx 状态码触发
console.log('收到响应:', response.status);
// 统一处理数据格式
if (response.data.code === 0) {
return response.data.data; // 直接返回业务数据
} else {
// 业务错误
return Promise.reject(new Error(response.data.message));
}
},
error => {
// 超出 2xx 状态码触发
console.error('响应错误:', error.response?.status);
// 统一错误处理
if (error.response?.status === 401) {
// token过期,跳转到登录页
window.location.href = '/login';
} else if (error.response?.status === 404) {
console.error('接口不存在');
} else if (error.response?.status === 500) {
console.error('服务器错误');
}
return Promise.reject(error);
}
);
// 移除拦截器
const requestInterceptor = axios.interceptors.request.use(config => config);
axios.interceptors.request.eject(requestInterceptor);