文章目录
- 一、概述
- 二、JS基础
-
- [2.1 JS在chrome中的运行](#2.1 JS在chrome中的运行)
-
- [2.1.1 chrome F12开发者模式](#2.1.1 chrome F12开发者模式)
- [2.1.2 chrome snippets小脚本](#2.1.2 chrome snippets小脚本)
- [2.2 JS显示数据](#2.2 JS显示数据)
- [2.3 JS操作元素](#2.3 JS操作元素)
- 三、JS语法
-
- [3.1 JS字面量【所见即所得】](#3.1 JS字面量【所见即所得】)
- [3.2 JS变量](#3.2 JS变量)
- [3.3 JS操作符](#3.3 JS操作符)
-
- [3.3.1 算术运算操作符](#3.3.1 算术运算操作符)
- [3.3.2 赋值操作符](#3.3.2 赋值操作符)
- [3.3.3 两类操作符核心注意事项](#3.3.3 两类操作符核心注意事项)
- [3.3.4 三元运算符(简化if-else)](#3.3.4 三元运算符(简化if-else))
- [3.3.5 其他常用运算符](#3.3.5 其他常用运算符)
-
- [(1) typeof 类型判断](#(1) typeof 类型判断)
- [(2)空值合并运算符 ??](#(2)空值合并运算符 ??)
- [(3)可选链操作符 ?.](#(3)可选链操作符 ?.)
- (4)运算符优先级(新手速记)
- [3.4 JS数据类型](#3.4 JS数据类型)
-
- [3.4.1 数据类型简介](#3.4.1 数据类型简介)
- [3.4.2 JS动态类型](#3.4.2 JS动态类型)
- [3.4.3 数据类型核心分类](#3.4.3 数据类型核心分类)
- [3.4.4 基本数据类型(7种,必掌握)](#3.4.4 基本数据类型(7种,必掌握))
- [3.4.5 引用数据类型(核心3种)](#3.4.5 引用数据类型(核心3种))
- [3.4.6 数据类型判断方法(新手首选)](#3.4.6 数据类型判断方法(新手首选))
-
- [(1) typeof 运算符(最常用,适合基本类型)](#(1) typeof 运算符(最常用,适合基本类型))
- [(2) Array.isArray() 专门判断数组](#(2) Array.isArray() 专门判断数组)
- [3.4.7 两类数据类型核心区别(必记)](#3.4.7 两类数据类型核心区别(必记))
- [3.4.8 新手常见数据类型误区](#3.4.8 新手常见数据类型误区)
- [3.4.9 数据类型速记口诀](#3.4.9 数据类型速记口诀)
- [3.5 JS注释](#3.5 JS注释)
-
- [3.5.1. 单行注释(最常用)](#3.5.1. 单行注释(最常用))
- [3.5.2. 多行注释(块注释)](#3.5.2. 多行注释(块注释))
- [3.5.3. 文档注释(JSDoc规范)](#3.5.3. 文档注释(JSDoc规范))
- [3.6 JS语句](#3.6 JS语句)
-
- [3.6.1 表达式语句(基础语句)](#3.6.1 表达式语句(基础语句))
- [3.6.2 分支语句(条件判断,选执行)](#3.6.2 分支语句(条件判断,选执行))
-
- [(1)if 语句(单分支)](#(1)if 语句(单分支))
- [(2) if...else 语句(双分支)](#(2) if...else 语句(双分支))
- [(3)if...else if...else 语句(多分支)](#(3)if...else if...else 语句(多分支))
- [(4) switch 语句(精准多分支)](#(4) switch 语句(精准多分支))
- 分支语句注意事项
- [3.6.3 循环语句(重复执行,省代码)](#3.6.3 循环语句(重复执行,省代码))
-
- [(1) for 循环(最常用,已知循环次数)](#(1) for 循环(最常用,已知循环次数))
- [(2)while 循环(未知循环次数)](#(2)while 循环(未知循环次数))
- [(3)do...while 循环(先执行后判断)](#(3)do...while 循环(先执行后判断))
- [(4) for...in 循环(遍历对象/数组)](#(4) for...in 循环(遍历对象/数组))
- 循环语句注意事项
- [3.6.4 跳转语句(改变执行流程)](#3.6.4 跳转语句(改变执行流程))
-
- [(1) break 语句](#(1) break 语句)
- [(2)continue 语句](#(2)continue 语句)
- 跳转语句注意事项
- [3.6.5 异常处理语句(容错,防代码崩溃)](#3.6.5 异常处理语句(容错,防代码崩溃))
- [四、 JS对象](#四、 JS对象)
-
- [4.1 对象基础概念](#4.1 对象基础概念)
- [4.2 对象的两种创建方式](#4.2 对象的两种创建方式)
-
- [4.2.1 字面量创建(最常用、推荐)](#4.2.1 字面量创建(最常用、推荐))
- [4.2.2 构造函数创建](#4.2.2 构造函数创建)
- [4.3 对象的核心操作(增删改查)](#4.3 对象的核心操作(增删改查))
-
- [4.3.1 查(获取属性/方法)](#4.3.1 查(获取属性/方法))
- [4.3.2 改(修改属性)](#4.3.2 改(修改属性))
- [4.3.3 增(添加属性/方法)](#4.3.3 增(添加属性/方法))
- [4.3.4 删(删除属性)](#4.3.4 删(删除属性))
- [4.4 对象中的this关键字](#4.4 对象中的this关键字)
- [4.5 对象的遍历(循环获取所有属性)](#4.5 对象的遍历(循环获取所有属性))
- [4.6 对象的核心特性](#4.6 对象的核心特性)
- [4.7 新手常见注意事项与避坑](#4.7 新手常见注意事项与避坑)
- 五、JS作用域
-
- [5.1 作用域基础核心概念](#5.1 作用域基础核心概念)
- [5.2 作用域分类与详细用法](#5.2 作用域分类与详细用法)
-
- [5.2.1 全局作用域](#5.2.1 全局作用域)
- [5.2.2 函数作用域(局部作用域)](#5.2.2 函数作用域(局部作用域))
- [5.2.3 块级作用域(ES6新增)](#5.2.3 块级作用域(ES6新增))
- [5.3 作用域链核心原理](#5.3 作用域链核心原理)
- [5.4 变量提升与作用域的关系](#5.4 变量提升与作用域的关系)
-
- [5.4.1 var声明的变量提升](#5.4.1 var声明的变量提升)
- [5.4.2 let/const声明的变量提升](#5.4.2 let/const声明的变量提升)
- [5.5 闭包与作用域(核心延伸)](#5.5 闭包与作用域(核心延伸))
- [5.6 作用域核心易错点总结](#5.6 作用域核心易错点总结)
- 六、JS事件
-
- [6.1 事件基础概念](#6.1 事件基础概念)
- [6.2 JS事件三种绑定方式](#6.2 JS事件三种绑定方式)
-
- [6.2.1 行内事件绑定(HTML属性绑定)](#6.2.1 行内事件绑定(HTML属性绑定))
- [6.2.2 DOM对象事件绑定(on+事件名)](#6.2.2 DOM对象事件绑定(on+事件名))
- [6.2.3 事件监听绑定(addEventListener,推荐)](#6.2.3 事件监听绑定(addEventListener,推荐))
- [6.3 JS常用事件类型分类](#6.3 JS常用事件类型分类)
-
- [6.3.1 鼠标事件(用户鼠标操作)](#6.3.1 鼠标事件(用户鼠标操作))
- [6.3.2 表单事件(输入框、表单操作)](#6.3.2 表单事件(输入框、表单操作))
- [6.3.3 键盘事件(键盘按键操作)](#6.3.3 键盘事件(键盘按键操作))
- [6.3.4 浏览器/页面事件](#6.3.4 浏览器/页面事件)
- [6.4 事件流(事件执行顺序)](#6.4 事件流(事件执行顺序))
- [6.5 事件冒泡与事件捕获](#6.5 事件冒泡与事件捕获)
-
- [6.5.1 事件冒泡(默认)](#6.5.1 事件冒泡(默认))
- [6.5.2 阻止事件冒泡](#6.5.2 阻止事件冒泡)
- [6.5.3 事件捕获](#6.5.3 事件捕获)
- [6.6 事件对象(e/event)](#6.6 事件对象(e/event))
- [6.7 事件委托(事件代理,重点)](#6.7 事件委托(事件代理,重点))
- [6.8 JS事件核心注意事项](#6.8 JS事件核心注意事项)
- 七、JS比较、逻辑运算符【操作符补充1】
-
- [7.1 比较运算符](#7.1 比较运算符)
-
- [7.1.1 相等性比较运算符(核心高频)](#7.1.1 相等性比较运算符(核心高频))
- [7.1.2 大小比较运算符](#7.1.2 大小比较运算符)
- [7.2 逻辑运算符](#7.2 逻辑运算符)
-
- [7.2.1 三大逻辑运算符详情](#7.2.1 三大逻辑运算符详情)
- [7.2.2 实际业务场景示例](#7.2.2 实际业务场景示例)
- [7.2.3 核心注意事项](#7.2.3 核心注意事项)
- [7.3 两类运算符综合速记](#7.3 两类运算符综合速记)
- [八、JS this关键字](#八、JS this关键字)
-
- [8.1 this核心基础认知](#8.1 this核心基础认知)
- [8.2 this五大绑定规则(按优先级排序)](#8.2 this五大绑定规则(按优先级排序))
-
- [8.2.1 new绑定(优先级最高)](#8.2.1 new绑定(优先级最高))
- [8.2.2 显式绑定(call/apply/bind)](#8.2.2 显式绑定(call/apply/bind))
- [8.2.3 隐式绑定](#8.2.3 隐式绑定)
- [8.2.4 默认绑定](#8.2.4 默认绑定)
- [8.2.5. 箭头函数绑定(优先级仅次于new)](#8.2.5. 箭头函数绑定(优先级仅次于new))
- [8.3 this绑定优先级速记](#8.3 this绑定优先级速记)
- [8.4 高频特殊场景与this指向](#8.4 高频特殊场景与this指向)
-
- [8.4.1 定时器中的this](#8.4.1 定时器中的this)
- [8.4.2 事件监听中的this](#8.4.2 事件监听中的this)
- [8.4.3 闭包中的this](#8.4.3 闭包中的this)
- [8.5 高频易错点总结](#8.5 高频易错点总结)
- [九、JS HTML DOM](#九、JS HTML DOM)
-
- [9.1 DOM核心基础概念](#9.1 DOM核心基础概念)
- [9.2 DOM元素获取(核心操作第一步)](#9.2 DOM元素获取(核心操作第一步))
- [9.3 DOM元素内容操作](#9.3 DOM元素内容操作)
- [9.4 DOM元素属性操作](#9.4 DOM元素属性操作)
-
- [9.4.1 原生属性操作(自带属性,如id、src、href、title)](#9.4.1 原生属性操作(自带属性,如id、src、href、title))
- [9.4.2 自定义属性操作](#9.4.2 自定义属性操作)
- [9.4.3 类名操作(样式控制核心)](#9.4.3 类名操作(样式控制核心))
- [9.4.4 样式操作(行内样式)](#9.4.4 样式操作(行内样式))
- [9.5 DOM节点操作(增删改查节点)](#9.5 DOM节点操作(增删改查节点))
-
- [9.5.1 创建新节点](#9.5.1 创建新节点)
- [9.5.2 添加节点到页面](#9.5.2 添加节点到页面)
- [9.5.3 删除节点](#9.5.3 删除节点)
- [9.5.4 替换节点](#9.5.4 替换节点)
- [9.5.5 节点层级关系查找](#9.5.5 节点层级关系查找)
- [9.6 DOM事件基础(交互核心)](#9.6 DOM事件基础(交互核心))
- [9.7 DOM操作高频注意事项](#9.7 DOM操作高频注意事项)
一、概述
JavaScript多用来控制网页的行为。
如需在 HTML 页面中插入 JavaScript,请使用 会告诉 JavaScript 在何处开始和结束。
二、JS基础
2.1 JS在chrome中的运行
2.1.1 chrome F12开发者模式

回车就可得到结果。
2.1.2 chrome snippets小脚本

2.2 JS显示数据
JavaScript 可以通过不同的方式来输出数据:[1](#1)
- 使用 window.alert() 弹出 --警告框--。
- 使用 document.write() 方法将内容写到 --HTML 文档--中。
- 使用 innerHTML 写入到 --HTML 元素--。
- 使用 console.log() 写入到--浏览器的控制台--。
2.3 JS操作元素
如需从 JavaScript 访问某个 HTML 元素,您可以使用 document.getElementById(id) 方法。
<!DOCTYPE html><html>
<body>
<h1>我的第一个 Web 页面</h1>
<p id="demo">我的第一个段落</p>
<script>
document.getElementById("demo").innerHTML = "段落已修改。";
</script>
</body>
</html>
三、JS语法
3.1 JS字面量【所见即所得】
- 字符串用 "..."
- 数字用 123
- 对象用 {Name:"Bob", age:50, gender:"male"}
- 数组用 [40, 100, 1, 5, 25, 10]
- 函数用 function myFunction(a, b) { return a * b;}
3.2 JS变量
在JavaScript中 变量用于存储数据值,使用关键字 var 来定义变量,使用等号 = 来为变量赋值。
javascript
var x,y
x = 1
y = 2
console.log(x+y)
// 控制台输出 3
3.3 JS操作符
操作符特指算术运算操作符 与赋值操作符,是JS中完成数值计算、变量赋值的核心符号。
3.3.1 算术运算操作符
用于对数值进行基础数学运算,是最常用的运算符号,需重点注意字符串拼接、特殊运算规则等新手易错点。
| 运算符 | 含义 | 代码示例 | 执行结果 | 新手必避坑 |
|---|---|---|---|---|
| + | 加法 / 字符串拼接 | 10 + 5'10' + 5 | 15'105' | 运算两边只要有一边是字符串,就会执行拼接操作,而非数值加法 |
| - | 减法 | 10 - 5 | 5 | 仅支持纯数值运算,非数字会隐式转换,易出现NaN |
| * | 乘法 | 10 * 5 | 50 | 浮点数相乘可能出现精度误差,属于JS正常现象 |
| / | 除法 | 10 / 5 | 2 | 除数不能为0,否则结果为Infinity(无穷大) |
| % | 取余(模运算) | 10 % 3 | 1 | 常用于判断奇偶性、循环取数、数值取模 |
| ** | 幂运算 | 2 ** 3 | 8 | 等价于Math.pow(2,3),用于计算一个数的次方 |
用于数值的加减乘除等数学计算,注意字符串拼接、取余、浮点数精度等新手常见坑。
| 运算符 | 含义 | 代码示例 | 执行结果 | 新手必避坑 |
|---|---|---|---|---|
| + | 加法/字符串拼接 | 10+5 / '10'+5 | 15 / '105' | 一边是字符串,就会变成拼接而非相加 |
| - | 减法 | 10-5 | 5 | 纯数字运算,无特殊坑点 |
| * | 乘法 | 10*5 | 50 | 浮点数运算可能有精度误差 |
| / | 除法 | 10/5 | 2 | 除数不能为0,否则结果为Infinity |
| % | 取余(模运算) | 10%3 | 1 | 常用于判断奇偶、循环取数 |
| ** | 幂运算 | 2**3 | 8 | 等价于Math.pow(2,3) |
-
++a:前置自增,先+1,再使用变量值
-
a++:后置自增,先使用变量值,再+1
-
--a:前置自减,先-1,再使用变量值
-
a--:后置自减,先使用变量值,再-1
javascript
// 后置自增(常用)
let a = 10;
console.log(a++); // 10(先取值)
console.log(a); // 11(后+1)
// 前置自增
let b = 10;
console.log(++b); // 11(先+1,再取值)
console.log(b); // 11
javascript
// 算术运算实操示例
// 数值加法
console.log(20 + 8);
// 字符串拼接
console.log('20' + 8);
// 减法运算
console.log(20 - 8);
// 乘法运算
console.log(20 * 8);
// 除法运算
console.log(20 / 5);
// 取余运算
console.log(20 % 3);
// 幂运算
console.log(3 ** 3);
3.3.2 赋值操作符
核心作用是给变量存储数据,同时简化"运算+赋值"的复合写法,避免重复书写变量名,提升代码简洁度。
| 运算符 | 含义 | 等价普通写法 | 完整代码示例 |
|---|---|---|---|
| = | 基础赋值 | - | let num = 10; // 直接给变量赋值10 |
| += | 加后赋值 | num = num + 5 | let num = 10; num += 5; // 最终num=15 |
| -= | 减后赋值 | num = num - 5 | let num = 10; num -= 5; // 最终num=5 |
| *= | 乘后赋值 | num = num * 5 | let num = 10; num *= 5; // 最终num=50 |
| /= | 除后赋值 | num = num / 5 | let num = 10; num /= 5; // 最终num=2 |
| %= | 取余后赋值 | num = num % 3 | let num = 10; num %= 3; // 最终num=1 |
javascript
// 赋值操作符实操示例
// 基础赋值
let score = 90;
console.log('基础赋值:', score);
// 复合赋值:加后赋值
score += 10;
console.log('加后赋值:', score);
// 复合赋值:乘后赋值
score *= 2;
console.log('乘后赋值:', score);
3.3.3 两类操作符核心注意事项
- 算术操作符优先处理纯数值运算,遇到字符串优先拼接,如需数值相加,需提前用Number()转换数据类型;
- 赋值操作符中,**=**是基础核心,其余均为复合简写,执行顺序是先算右侧运算,再赋值给左侧变量;
- 避免对非数值变量直接使用算术操作符,容易得到NaN(非数字)结果,导致代码异常。
3.3.4 三元运算符(简化if-else)
语法:条件 ? 条件成立执行 : 条件不成立执行,是if-else的简写形式,适合简单判断。
javascript
let age = 20;
// 常规if-else
let result;
if(age >= 18){
result = '成年';
} else {
result = '未成年';
}
// 三元运算符简写
let result2 = age >= 18 ? '成年' : '未成年';
console.log(result2); // '成年'
3.3.5 其他常用运算符
(1) typeof 类型判断
判断数据类型,返回字符串结果
javascript
console.log(typeof 10); // 'number'
console.log(typeof '10'); // 'string'
console.log(typeof true); // 'boolean'
(2)空值合并运算符 ??
判断值是否为null/undefined,是则取默认值
javascript
let name = null;
console.log(name ?? '匿名用户'); // '匿名用户'
(3)可选链操作符 ?.
防止对象属性不存在报错,新手必备
javascript
let user = { name: '张三' };
console.log(user?.age); // undefined,不报错
(4)运算符优先级(新手速记)
优先级从高到低,不确定时用**小括号()**包裹,小括号优先级最高,避免运算顺序出错:
小括号() > 自增自减 ++/-- > 算术运算 */% > 算术运算 ± > 比较运算 > 逻辑运算 > 赋值运算
3.4 JS数据类型
JS中的数据类型都是 弱类型,无需像 java、C++等强制声明, **"var"**解决一切。
3.4.1 数据类型简介

值类型(基本类型): 字符串(String)、数字(Number)、布尔(Boolean)、空(Null)、未定义(Undefined)、Symbol。
引用数据类型(对象类型): 对象(Object)、数组(Array)、函数(Function),还有两个特殊的对象:正则(RegExp)和日期(Date)。
3.4.2 JS动态类型
JavaScript拥有动态类型,这会使得相同的变量在不同的地方可作为不同的类型。
JS数据类型分为基本数据类型(值类型)和引用数据类型两大类,是JS编程的核心基础,决定了数据存储、传递和使用的方式,新手需重点区分两类数据的核心差异,避免隐式转换、赋值报错等常见问题。
3.4.3 数据类型核心分类
基本数据类型 :存储在栈内存,数据本身直接存储,赋值时拷贝完整数据,修改副本不影响原数据,共7种,是日常开发最常用的基础类型。
引用数据类型 :存储在堆内存,栈中只存内存地址,赋值时拷贝地址,多个变量指向同一数据,修改一个会影响全部,核心为对象、数组、函数。
3.4.4 基本数据类型(7种,必掌握)
| 数据类型 | 类型名称 | 写法示例 | 核心说明 | 新手避坑 |
|---|---|---|---|---|
| number | 数字类型 | 18、3.14、-5、0、NaN | 包含整数、小数、负数,特殊值NaN代表非数字 | 算术运算出错会得到NaN,typeof NaN结果为number |
| string | 字符串类型 | '你好'、"JS"、模板字符串 |
文本数据,必须用单引号、双引号或反引号包裹 | 和数字混用+号会拼接,而非相加,需提前转换类型 |
| boolean | 布尔类型 | true、false | 只有两个值,用于条件判断、逻辑运算 | 0、''、null、undefined会自动转为false |
| undefined | 未定义类型 | undefined | 变量声明但未赋值,默认值 | 不是手动赋值,是系统默认状态 |
| null | 空值类型 | null | 手动设置的空对象指针,表示空值 | typeof null结果为object,属于历史bug |
| symbol | 符号类型 | Symbol('唯一标识') | 生成唯一值,避免对象属性重名冲突 | 日常基础开发少用,多用于框架、复杂对象 |
| bigint | 大整数类型 | 100n、BigInt(999) | 存储超大整数,解决普通数字溢出问题 | 不能和普通number直接运算,需统一类型 |
3.4.5 引用数据类型(核心3种)
| 数据类型 | 类型名称 | 写法示例 | 核心说明 |
|---|---|---|---|
| object | 对象类型 | {name:'张三', age:18} | 存储键值对数据,描述事物属性 |
| array | 数组类型 | [1,2,3, '前端', true] | 有序数据集合,按索引取值,属于特殊对象 |
| function | 函数类型 | function fn(){} | 封装可重复执行的代码块,typeof结果为function |
3.4.6 数据类型判断方法(新手首选)
(1) typeof 运算符(最常用,适合基本类型)
javascript
// 基本类型判断
console.log(typeof 18); // number
console.log(typeof '前端'); // string
console.log(typeof true); // boolean
console.log(typeof undefined); // undefined
console.log(typeof null); // object(历史bug,特殊记忆)
// 引用类型判断
console.log(typeof {}); // object
console.log(typeof []); // object
console.log(typeof function(){}); // function
(2) Array.isArray() 专门判断数组
javascript
console.log(Array.isArray([1,2,3])); // true
console.log(Array.isArray({})); // false
3.4.7 两类数据类型核心区别(必记)
基本数据类型 :栈内存存储,赋值是深拷贝 ,修改新变量,原变量不变
引用数据类型 :堆内存存储,赋值是浅拷贝,修改新变量,原变量同步改变
javascript
// 基本类型:拷贝数据,互不影响
let a = 10;
let b = a;
b = 20;
console.log(a); // 10,原数据不变
// 引用类型:拷贝地址,互相影响
let obj1 = {name:'张三'};
let obj2 = obj1;
obj2.name = '李四';
console.log(obj1.name); // 李四,原数据被修改
3.4.8 新手常见数据类型误区
-
误区1 :null和undefined一样
解答:undefined是变量声明未赋值,null是手动设置的空值
-
误区2 :数组是独立基本类型
解答:数组属于引用类型,是特殊的对象
-
误区3 :字符串用+数字是加法
解答:只要一侧是字符串,+号执行拼接操作
-
误区4 :typeof能区分对象和数组
解答:typeof判断对象和数组都返回object,需用Array.isArray()区分
3.4.9 数据类型速记口诀
基本类型七兄弟,数串布尔空未定,符号大整数补上;
引用类型对象组,函数也是其中属;
栈存值来堆存址,拷贝方式要记熟。
3.5 JS注释
3.5.1. 单行注释(最常用)
语法 :用双斜杠 // 开头,只作用于当前行,双斜杠后面的内容全部为注释。
使用场景:简短说明、行内解释、临时屏蔽单行代码
javascript
// 定义变量存储用户名
let username = "前端小白";
// 这行代码临时屏蔽,不执行
// let age = 18;
console.log(username); // 输出用户名,行尾注释
使用技巧:可以放在代码行开头,也可以放在代码末尾,不影响代码执行,简洁高效。
3.5.2. 多行注释(块注释)
语法 :以 /* 开头,*/ 结尾,中间可跨多行书写。
使用场景:长文本说明、批量屏蔽多行代码、文件头部标注
javascript
/*
这是一个计算两数之和的函数
参数:num1-第一个数字,num2-第二个数字
返回值:两数相加的结果
适用场景:基础数值运算
*/
function add(num1, num2) {
return num1 + num2;
}
/*
批量屏蔽以下代码
console.log(123);
let test = "测试";
*/
注意 :多行注释不支持嵌套 ,内部不能再写 /* */,否则会提前结束注释,导致语法报错。
3.5.3. 文档注释(JSDoc规范)
语法 :以 /** 开头,*/ 结尾,属于特殊多行注释,常用于函数、类、变量的标准化说明,支持VS Code等编辑器智能提示。
使用场景:团队协作、函数/组件说明、生成API文档
javascript
/**
* 计算商品总价
* @param {number} price - 商品单价
* @param {number} count - 商品数量
* @param {boolean} isDiscount - 是否打折
* @returns {number} 最终总价
*/
function calcTotalPrice(price, count, isDiscount) {
let total = price * count;
return isDiscount ? total * 0.9 : total;
}
3.6 JS语句
JS语句是代码执行的最小逻辑单元,用于控制代码的执行顺序、分支选择、循环重复 ,实现各类业务逻辑。语句通常以分号
;结尾(可省略但建议规范书写),按功能分为表达式语句、分支语句、循环语句、跳转语句、异常处理语句五大类,本篇聚焦日常开发高频使用的核心语句,搭配实例与注意事项,零基础也能快速掌握。
3.6.1 表达式语句(基础语句)
由表达式构成,执行后会返回一个值,是最基础、最常用的语句,几乎所有代码都离不开这类语句。
核心类型与示例
-
赋值语句 :给变量赋值,最常用的表达式语句
let num = 10; // 声明+赋值语句 num = num + 5; // 重新赋值语句 -
算术运算语句 :执行数学计算,可单独执行或结合赋值
10 + 20; // 单纯运算,无实际业务意义 let total = 10 * 20; // 运算+赋值,常用写法 -
函数调用语句 :调用已定义的函数,执行函数内部逻辑
console.log('Hello JS'); // 控制台输出语句 alert('提示框'); // 浏览器弹窗语句
注意事项
-
单独的运算表达式无实际作用,需结合赋值或输出才有意义
-
建议每条语句结尾加分号,避免代码压缩或合并时出现语法错误
3.6.2 分支语句(条件判断,选执行)
根据指定条件的真假,选择执行不同的代码块,实现多路径逻辑选择,是实现业务判断的核心语句。
(1)if 语句(单分支)
满足条件时,执行对应代码块,不满足则跳过。
javascript
// 语法
if (条件表达式) {
// 条件为true时执行的代码
}
// 示例:判断成年
let age = 20;
if (age >= 18) {
console.log('已成年,具备完全民事行为能力');
}
(2) if...else 语句(双分支)
条件为true执行if代码块,为false执行else代码块,二选一执行。
javascript
// 示例:判断成年/未成年
let age = 16;
if (age >= 18) {
console.log('已成年');
} else {
console.log('未成年');
}
(3)if...else if...else 语句(多分支)
多个条件依次判断,满足哪个条件就执行对应代码块,都不满足则执行else代码块。
javascript
// 示例:判断成绩等级
let score = 85;
if (score >= 90) {
console.log('优秀');
} else if (score >= 70) {
console.log('良好');
} else if (score >= 60) {
console.log('及格');
} else {
console.log('不及格');
}
(4) switch 语句(精准多分支)
针对一个变量的固定值做精准匹配,适合多固定值判断,比多分支if更简洁。
javascript
// 语法:break用于跳出switch,避免穿透;default为默认匹配项
switch (变量/表达式) {
case 值1:
代码块1;
break;
case 值2:
代码块2;
break;
default:
默认代码块;
}
// 示例:判断星期
let week = 3;
switch (week) {
case 1:
console.log('星期一');
break;
case 2:
console.log('星期二');
break;
case 3:
console.log('星期三');
break;
default:
console.log('输入无效');
}
分支语句注意事项
-
if条件表达式会自动转为布尔值,0、''、null、undefined、NaN会转为false,其余为true
-
switch匹配是严格相等(===),值和类型都要一致
-
switch的case必须加break,否则会出现"穿透现象",继续执行后续case代码
-
多分支逻辑优先用switch,范围判断优先用if
3.6.3 循环语句(重复执行,省代码)
满足循环条件时,重复执行一段代码,用于批量处理数据、遍历数组/对象、重复逻辑,避免冗余代码。
(1) for 循环(最常用,已知循环次数)
适合循环次数固定的场景,语法紧凑,三步控制循环(初始化、条件、更新)。
javascript
// 语法
for (初始化变量; 循环条件; 变量更新) {
循环体代码(重复执行)
}
// 示例:循环输出1-10
for (let i = 1; i <= 10; i++) {
console.log(i);
}
// 示例:遍历数组
let arr = [10, 20, 30];
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
(2)while 循环(未知循环次数)
先判断条件,条件为true再执行循环体,适合循环次数不确定的场景。
javascript
// 语法
初始化变量;
while (循环条件) {
循环体代码;
变量更新;
}
// 示例:循环输出1-5
let i = 1;
while (i <= 5) {
console.log(i);
i++; // 必须更新变量,否则死循环
}
(3)do...while 循环(先执行后判断)
先执行一次循环体,再判断条件,至少执行一次。
javascript
// 示例
let i = 1;
do {
console.log(i);
i++;
} while (i <= 5);
(4) for...in 循环(遍历对象/数组)
专门用于遍历对象的属性,也可遍历数组,循环变量为键名/索引。
javascript
let obj = { name: '张三', age: 20 };
for (let key in obj) {
console.log('属性名:', key, '属性值:', obj[key]);
}
循环语句注意事项
-
必须设置循环终止条件,否则会造成死循环,导致页面卡顿崩溃
-
for循环适合固定次数,while适合不确定次数,do...while适合至少执行一次
-
遍历数组优先用for循环,遍历对象优先用for...in
-
避免在循环体内修改循环变量,防止循环逻辑混乱
3.6.4 跳转语句(改变执行流程)
强制改变代码执行顺序,跳出循环或跳过当前循环,配合分支、循环语句使用。
(1) break 语句
立即跳出当前循环/switch,终止整个循环或switch语句。
javascript
// 示例:循环到5时终止
for (let i = 1; i <= 10; i++) {
if (i === 5) {
break; // 跳出整个for循环
}
console.log(i); // 输出1-4
}
(2)continue 语句
跳过当前一次循环,直接进入下一次循环,不终止整个循环。
javascript
// 示例:跳过5,输出其余数字
for (let i = 1; i <= 10; i++) {
if (i === 5) {
continue; // 跳过本次循环,不输出5
}
console.log(i);
}
跳转语句注意事项
-
break只能用于循环和switch,不能用于普通if语句
-
continue只能用于循环,不能用于switch语句
-
合理使用跳转语句,避免代码逻辑过于复杂难以维护
3.6.5 异常处理语句(容错,防代码崩溃)
捕获代码执行中的错误,避免因报错导致整个代码停止运行,提升代码稳定性。
javascript
// 语法
try {
// 可能报错的代码
let num = 10;
console.log(num.toUpperCase()); // 数字无此方法,会报错
} catch (err) {
// 捕获错误,执行容错代码,不影响后续代码
console.log('代码出错了:', err.message);
} finally {
// 无论是否报错,都会执行的代码(可选)
console.log('执行完毕');
}
注意事项
-
try包裹易报错代码,catch捕获错误信息,finally用于收尾操作
-
不要滥用try...catch,优先提前判断避免错误,而非捕获错误
四、 JS对象
对象是JS引用数据类型的核心,是存储复杂数据、描述事物特征与行为的载体,属于**键值对(key-value)**结构,也是前端开发中最常用的数据结构之一,本篇覆盖基础到核心考点,全是实用知识点。
4.1 对象基础概念
-
定义:对象是无序的键值对集合,每个键(属性名)对应一个值(属性值),键默认是字符串类型,值可以是任意数据类型(数字、字符串、函数、数组甚至另一个对象)。
-
作用:描述一个具体事物(比如人、商品、用户),存储事物的属性(特征)和方法(行为)。
-
存储特点:属于引用数据类型,存储在堆内存,变量保存的是内存地址,赋值时传递地址,修改会影响原对象。
4.2 对象的两种创建方式
4.2.1 字面量创建(最常用、推荐)
语法简洁,代码直观,日常开发首选,直接用大括号包裹键值对,键值之间用冒号分隔,多个键值对用逗号分隔。
javascript
// 基础对象创建
let person = {
// 属性:特征描述
name: "张三",
age: 20,
gender: "男",
// 方法:行为描述
sayHello: function() {
console.log("大家好,我是" + this.name);
}
};
// 调用属性和方法
console.log(person.name); // 张三
person.sayHello(); // 大家好,我是张三
4.2.2 构造函数创建
通过new Object()创建,适合动态添加属性的场景,写法相对繁琐,日常使用少于字面量。
javascript
let person = new Object();
// 动态添加属性
person.name = "李四";
person.age = 22;
// 动态添加方法
person.sayHi = function() {
console.log("Hi~");
};
console.log(person.age); // 22
4.3 对象的核心操作(增删改查)
对象操作主要针对属性和方法,分为点语法 和方括号语法两种操作方式,适用场景不同。
4.3.1 查(获取属性/方法)
| 操作方式 | 语法 | 适用场景 | 示例 |
|---|---|---|---|
| 点语法 | 对象.属性名 | 属性名是合法标识符(无空格、无特殊字符、非数字开头) | person.name |
| 方括号语法 | 对象['属性名'] | 属性名含空格、特殊字符、数字开头,或属性名是变量 | person['user name']、let key='age'; person[key] |
4.3.2 改(修改属性)
直接给已有属性重新赋值,即可修改原有属性值,方法也可通过同样方式修改。
javascript
let person = { name: "张三", age: 20 };
// 点语法修改
person.age = 21;
// 方括号语法修改
person['name'] = "张三三";
console.log(person); // {name: '张三三', age: 21}
4.3.3 增(添加属性/方法)
直接给对象赋值一个不存在的属性名,即可动态新增属性,无需提前声明。
javascript
let person = { name: "张三" };
// 新增属性
person.address = "北京";
// 新增方法
person.run = function() {
console.log("跑步");
};
4.3.4 删(删除属性)
使用delete关键字删除对象的属性或方法,删除后属性彻底消失。
javascript
let person = { name: "张三", age: 20 };
delete person.age;
console.log(person.age); // undefined
4.4 对象中的this关键字
核心含义 :对象方法中的this,指向当前调用方法的对象本身,可以通过this访问对象自身的其他属性和方法。
javascript
let person = {
name: "张三",
sayName: function() {
// this指向person对象
console.log(this.name);
}
};
person.sayName(); // 张三
注意:普通函数中的this指向全局对象(window),对象方法中的this指向当前对象,切勿混淆。
4.5 对象的遍历(循环获取所有属性)
遍历对象常用for...in循环,专门用于循环对象的可枚举属性,获取所有键名,再通过键名取值。
javascript
let person = { name: "张三", age: 20, gender: "男" };
// for...in遍历对象
for(let key in person) {
// key是属性名,person[key]是属性值
console.log("属性名:" + key + ",属性值:" + person[key]);
}
4.6 对象的核心特性
-
引用类型特性 :对象赋值是传递内存地址,不是拷贝数据,修改新对象会影响原对象。
let obj1 = { name: "张三" }; let obj2 = obj1; // 传递地址 obj2.name = "李四"; console.log(obj1.name); // 李四(原对象被修改) -
属性名特性:对象的键(属性名)会自动转为字符串,数字键也会转为字符串类型。
-
动态特性:对象的属性和方法可以随时增删改,无需提前固定结构。
-
方法简写 :ES6支持对象方法简写,省略function关键字,写法更简洁。
let person = { sayHello() { // 方法简写 console.log("你好"); } };
4.7 新手常见注意事项与避坑
1. 访问不存在的属性 :不会报错,返回undefined,可用于判断属性是否存在。
2. 点语法和方括号语法不可混用 :属性名含特殊字符、变量做属性名,必须用方括号语法。
3. 对象赋值易踩坑 :想要拷贝对象而非地址,需用浅拷贝/深拷贝(基础阶段可手动遍历赋值)。
4. 属性名重复 :同一对象中属性名重复,后定义的会覆盖先定义的。
5. this指向误区:对象方法单独调用时,this会丢失指向,不再指向原对象。
五、JS作用域
作用域是JS中变量、函数、对象的可访问范围 ,决定了代码块中变量和其他资源的可见性,是JS核心基础知识点,也是避免变量污染、排查变量报错的关键。作用域的核心作用是隔离变量,不同作用域下的同名变量不会冲突,同时控制变量的生命周期。
5.1 作用域基础核心概念
-
全局作用域:代码最外层的作用域,整个脚本文件、浏览器窗口或Node环境中都能访问,生命周期伴随页面/进程始终。
-
局部作用域:也叫函数作用域,仅在函数内部可访问,函数外部无法直接使用,函数执行完毕后变量会被销毁。
-
块级作用域 :ES6新增,由
let、const声明产生,在{}包裹的代码块(if、for、while等)内有效,代码块执行完毕变量销毁。 -
作用域链 :内部函数访问外部函数变量时,会逐级向上查找作用域,直到全局作用域,这个层级关系就是作用域链,查找顺序是局部→外层→全局。
核心注意事项 :JS采用词法作用域(静态作用域),作用域在代码定义时就已确定,而非执行时,这是作用域最核心的规则,切勿混淆。
5.2 作用域分类与详细用法
5.2.1 全局作用域
在任何地方都能访问的变量/函数,属于全局作用域,全局变量会挂载到浏览器的window对象(Node中挂载到global)。
产生场景
-
代码最外层直接声明的变量、函数
-
未使用
var/let/const声明的变量(隐式全局变量,严禁使用) -
浏览器内置对象(window、document、console等)
javascript
// 全局变量,全局可访问
let globalNum = 100;
// 全局函数
function globalFn() {
console.log(globalNum); // 函数内部可访问全局变量
}
globalFn();
console.log(globalNum); // 函数外部也可访问
// 隐式全局变量(无声明关键字)
function test() {
hiddenVar = 200; // 未声明,自动变为全局变量
}
test();
console.log(hiddenVar); // 可访问,不推荐
注意事项 :尽量减少全局变量的使用,过多全局变量会造成变量污染,不同代码块的同名变量互相覆盖,引发难以排查的bug;严禁使用无声明关键字的隐式全局变量。
5.2.2 函数作用域(局部作用域)
变量/函数仅在当前函数内部可访问,函数外部无法访问,函数执行结束后,内部变量会被垃圾回收机制销毁。
javascript
function outer() {
// 函数内部变量,局部作用域
let innerNum = 300;
console.log(innerNum); // 内部可访问
// 嵌套函数,内层可访问外层函数作用域
function inner() {
console.log(innerNum); // 访问外层函数变量
}
inner();
}
outer();
// 外部无法访问函数内部变量
console.log(innerNum); // 报错:innerNum is not defined
注意事项:函数作用域内的变量,优先级高于同名全局变量,就近访问;函数参数也属于当前函数的局部作用域,外部无法访问。
5.2.3 块级作用域(ES6新增)
由let、const声明的变量,在{}包裹的代码块(if、for、while、switch等)内有效,代码块执行完毕,变量立即销毁,解决了ES5中变量提升、循环变量污染的问题。
javascript
// if代码块,块级作用域
if(true) {
let blockVar = 400; // 块级变量
const blockConst = 500; // 块级常量
console.log(blockVar); // 内部可访问
}
// 代码块外部无法访问
console.log(blockVar); // 报错:blockVar is not defined
// for循环块级作用域
for(let i = 0; i < 3; i++) {
console.log(i); // 每次循环都是独立块级作用域
}
console.log(i); // 报错,i仅在循环内有效
注意事项 :var声明的变量没有块级作用域 ,只有函数作用域和全局作用域,会穿透代码块;日常开发优先使用let、const,避免var带来的作用域问题。
5.3 作用域链核心原理
当在当前作用域查找一个变量时,会先在自身作用域内查找,找不到就向上一级作用域查找,逐级追溯,直到全局作用域,若全程都找不到,直接抛出is not defined错误。
javascript
// 全局作用域
let a = 1;
function fn1() {
// fn1作用域
let b = 2;
function fn2() {
// fn2作用域
let c = 3;
// 查找变量:先fn2→再fn1→最后全局
console.log(a + b + c); // 1+2+3=6
}
fn2();
}
fn1();
注意事项 :作用域链是单向向上查找,外层作用域无法访问内层作用域的变量,只有内层能访问外层,这是作用域隔离的核心规则。
5.4 变量提升与作用域的关系
5.4.1 var声明的变量提升
var声明的变量存在变量提升,会提升到当前作用域顶部,声明提前、赋值不提前,容易导致变量未赋值就使用的问题。
javascript
console.log(num); // undefined(变量提升,未赋值)
var num = 10;
// 实际执行顺序:var num; console.log(num); num=10;
5.4.2 let/const声明的变量提升
let、const存在暂时性死区(TDZ),变量会提升但不能在声明前使用,从代码块开始到声明位置,都属于暂时性死区,使用会直接报错。
javascript
console.log(letNum); // 报错:Cannot access 'letNum' before initialization
let letNum = 20;
注意事项:暂时性死区是块级作用域的重要特性,严禁在let/const声明前使用对应变量,避免语法报错;var的变量提升容易引发逻辑错误,优先弃用var。
5.5 闭包与作用域(核心延伸)
闭包:函数嵌套时,内层函数访问外层函数作用域的变量,且内层函数被外部调用,导致外层函数作用域变量无法被销毁,这种现象就是闭包。
javascript
function outer() {
let count = 0;
// 内层函数,形成闭包
return function inner() {
count++;
console.log(count);
}
}
const fn = outer();
fn(); // 1
fn(); // 2
// 外层函数执行完毕,count变量因闭包未被销毁
注意事项 :闭包可以延长变量生命周期,但过度使用会造成内存泄漏,因为闭包占用的内存不会被自动回收,使用完毕后需手动解除引用(fn=null);闭包是作用域链的典型应用,也是高频考点。
5.6 作用域核心易错点总结
-
var没有块级作用域,let/const有块级作用域,这是三者最核心区别
-
局部作用域变量优先级高于同名全局变量,就近访问,不会覆盖全局变量
-
作用域在代码定义时确定,而非执行时,词法作用域规则牢记
-
外层作用域无法访问内层作用域变量,内层可访问外层,单向查找
-
闭包虽好用,但需注意内存泄漏问题,及时清理引用
-
避免隐式全局变量,所有变量必须用var/let/const声明
六、JS事件
JS事件是前端实现交互效果的核心,指页面中元素发生的特定动作(比如点击、输入、鼠标移动等),通过编写事件处理函数,在动作触发时执行对应代码,实现网页与用户的双向互动,是前端开发必备核心知识点。
6.1 事件基础概念
事件 :用户或浏览器执行的动作,如点击click、输入input、页面加载load等。
事件源 :触发事件的元素,如按钮、输入框、div等。
事件处理函数 :事件触发后要执行的代码逻辑,也叫事件回调。
事件绑定:将事件源、事件类型、处理函数关联起来,让元素监听对应动作。
所有事件绑定的核心目的,是让页面监听用户操作,实时做出反馈,完成表单验证、按钮交互、动态渲染等功能。
6.2 JS事件三种绑定方式
6.2.1 行内事件绑定(HTML属性绑定)
直接在HTML标签内通过on+事件名的属性绑定,写法简单,适合简单交互和临时测试,不适合复杂项目。
html
<!-- 点击按钮触发alert事件 -->
<button onclick="alert('行内绑定触发')">点我</button>
<!-- 绑定自定义函数 -->
<button onclick="handleClick()">点我执行函数</button>
<script>
function handleClick() {
console.log('行内绑定,调用自定义函数');
}
</script>
这种方式耦合了HTML和JS代码,不利于代码维护,大型项目不推荐使用。
6.2.2 DOM对象事件绑定(on+事件名)
通过JS获取DOM元素,给元素绑定on+事件名属性,实现JS与HTML分离,是常用基础绑定方式,同一元素同一事件只能绑定一个处理函数。
javascript
// 获取按钮元素
const btn = document.querySelector('button');
// 绑定点击事件
btn.onclick = function() {
console.log('DOM事件绑定,点击触发');
};
// 解绑事件
// btn.onclick = null;
同一元素重复绑定同一事件,后绑定的会覆盖先绑定的,无法实现多事件处理。
6.2.3 事件监听绑定(addEventListener,推荐)
通过addEventListener方法绑定事件,支持同一元素同一事件绑定多个处理函数,支持控制事件流,是现代前端标准绑定方式,兼容性好且功能强大。
javascript
const btn = document.querySelector('button');
// 语法:元素.addEventListener('事件名', 处理函数, 布尔值/配置)
btn.addEventListener('click', function() {
console.log('事件监听绑定,第一个处理函数');
});
// 同一事件绑定多个函数,都会执行
btn.addEventListener('click', function() {
console.log('事件监听绑定,第二个处理函数');
});
解绑需使用removeEventListener,且处理函数不能是匿名函数,需单独定义;第三个参数默认false,代表冒泡阶段触发,true代表捕获阶段触发。
6.3 JS常用事件类型分类
6.3.1 鼠标事件(用户鼠标操作)
| 事件名 | 触发时机 | 适用场景 |
|---|---|---|
| click | 鼠标左键单击 | 按钮点击、菜单切换 |
| dblclick | 鼠标左键双击 | 双击编辑、打开弹窗 |
| mouseover | 鼠标移入元素(含子元素) | 悬浮提示、下拉菜单 |
| mouseout | 鼠标移出元素(含子元素) | 隐藏悬浮内容 |
| mouseenter | 鼠标移入元素(不含子元素) | 悬浮交互,无冒泡干扰 |
| mouseleave | 鼠标移出元素(不含子元素) | 配合mouseenter使用 |
| mousemove | 鼠标在元素内移动 | 鼠标跟随、拖拽效果 |
| mouseenter和mouseleave不会触发事件冒泡,交互更稳定,优先使用;mouseover和mouseout会受子元素影响,容易重复触发。 |
6.3.2 表单事件(输入框、表单操作)
| 事件名 | 触发时机 | 适用场景 |
|---|---|---|
| input | 输入框内容实时变化 | 实时搜索、字数统计 |
| change | 输入框失去焦点且内容改变 | 表单验证、下拉框选择 |
| focus | 输入框获得焦点 | 输入框高亮、提示文字 |
| blur | 输入框失去焦点 | 表单字段验证 |
| submit | 表单提交时 | 表单提交拦截、验证 |
| submit事件默认会刷新页面,需要阻止默认行为才能实现异步提交;input事件实时触发,比change更灵敏。 |
6.3.3 键盘事件(键盘按键操作)
-
keydown:键盘按键按下时触发,持续按住会重复触发
-
keyup:键盘按键抬起时触发,适合获取最终输入值
键盘事件通常绑定在document或输入框上,可通过事件对象获取按键编码。
6.3.4 浏览器/页面事件
-
load:页面所有资源加载完成后触发
-
DOMContentLoaded:DOM结构加载完成就触发,无需等待图片等资源
-
resize:浏览器窗口大小改变时触发
-
scroll:页面滚动时触发
DOMContentLoaded比load触发更快,适合DOM操作初始化;scroll事件触发频率高,建议做节流优化。
6.4 事件流(事件执行顺序)
事件流描述事件在页面中传播的顺序,分为三个阶段,是理解事件冒泡和捕获的核心。
-
捕获阶段:从最外层document向内层事件源传递,从上到下
-
目标阶段:到达触发事件的元素本身
-
冒泡阶段:从内层事件源向外层document传递,从下到上(默认执行阶段)
默认情况下,事件监听在冒泡阶段执行,addEventListener第三个参数设为true,可切换为捕获阶段执行。
6.5 事件冒泡与事件捕获
6.5.1 事件冒泡(默认)
子元素事件触发后,会逐级向上触发父元素、祖先元素的同名事件,直到document。
html
<div class="parent">
<button class="child">点我</button>
</div>
<script>
const parent = document.querySelector('.parent');
const child = document.querySelector('.child');
// 子元素点击
child.addEventListener('click', function() {
console.log('子元素点击');
});
// 父元素点击,会被子元素冒泡触发
parent.addEventListener('click', function() {
console.log('父元素点击(冒泡触发)');
});
</script>
事件冒泡会导致意外触发父元素事件,影响交互逻辑,需要手动阻止。
6.5.2 阻止事件冒泡
通过事件对象的stopPropagation方法,阻止事件继续向上传播。
javascript
child.addEventListener('click', function(e) {
// 阻止冒泡
e.stopPropagation();
console.log('子元素点击,不触发父元素');
});
stopPropagation只阻止冒泡,不影响当前元素自身的事件执行。
6.5.3 事件捕获
addEventListener第三个参数设为true,事件在捕获阶段执行,先触发父元素,再触发子元素,日常开发使用较少,多用于特殊交互场景。
6.6 事件对象(e/event)
事件触发时,浏览器会自动生成事件对象,包含事件相关信息,作为参数传递给处理函数,通常用e或event表示。
常用属性和方法
-
e.target:实际触发事件的元素(事件源)
-
e.currentTarget:绑定事件的元素
-
e.preventDefault():阻止元素默认行为
-
e.stopPropagation():阻止事件冒泡
-
e.key:键盘事件中,获取按下的按键
javascript
// 阻止a标签默认跳转
const link = document.querySelector('a');
link.addEventListener('click', function(e) {
e.preventDefault();
console.log('阻止a标签默认跳转');
});
所有事件处理函数都能接收事件对象,匿名函数和具名函数都可直接获取;preventDefault常用于阻止表单提交、a标签跳转等默认行为。
6.7 事件委托(事件代理,重点)
利用事件冒泡原理,将子元素的事件绑定到父元素上,通过e.target判断实际触发的子元素,实现事件批量绑定,适合动态生成的元素。
html
<ul class="list">
<li>列表项1</li>
<li>列表项2</li>
<!-- 动态新增的li也能触发事件 -->
</ul>
<script>
const list = document.querySelector('.list');
// 父元素委托事件
list.addEventListener('click', function(e) {
// 判断触发元素是否为li
if(e.target.tagName === 'LI') {
console.log('点击了列表项', e.target.innerText);
}
});
</script>
事件委托减少事件绑定数量,提升性能,动态添加的元素无需重新绑定事件,是前端高频实用技巧。
6.8 JS事件核心注意事项
-
行内事件绑定耦合HTML与JS,项目开发优先用事件监听addEventListener
-
同一元素用on+事件名绑定,重复绑定会覆盖,addEventListener可绑定多个处理函数
-
事件冒泡是默认机制,容易引发交互bug,不确定时可主动阻止冒泡
-
事件对象e是自动传递的,无需手动传参,直接在函数内接收即可
-
动态生成的元素,无法直接绑定事件,必须用事件委托解决
-
preventDefault阻止默认行为,stopPropagation阻止冒泡,二者功能不同,不可混用
-
高频触发事件(scroll、mousemove),建议做节流或防抖,避免性能损耗
七、JS比较、逻辑运算符【操作符补充1】
比较运算符和逻辑运算符是JS中实现条件判断、逻辑分支 的核心工具,二者常配合if语句、循环语句使用,运算结果均为布尔值(true 或 false)。
7.1 比较运算符
比较运算符用于对两个数据进行大小、相等性对比,执行后返回唯一布尔值结果,是所有条件判断的基础,分为相等判断 和大小比较两大类,使用时需重点区分宽松对比和严格对比的差异。
7.1.1 相等性比较运算符(核心高频)
| 运算符 | 名称 | 核心规则 | 代码示例 | 执行结果 |
|---|---|---|---|---|
== |
宽松相等 | 仅对比数据的值,自动进行隐式类型转换,不校验数据类型 | 10 == '10' | true |
=== |
严格相等 | 同时对比值和数据类型,不做任何隐式转换,完全一致才返回true | 10 === '10' | false |
!= |
宽松不等 | 宽松相等的反向,值不同则返回true,支持隐式类型转换 | 10 != '10' | false |
!== |
严格不等 | 严格相等的反向,值或类型任意一项不同,返回true | 10 !== '10' | true |
关键注意事项 :日常开发中**===!==** 必须优先使用严格相等和严格不等 ,宽松相等==的隐式类型转换极易引发逻辑bug,例如0 == ''、null == undefined均返回true,不符合常规判断逻辑,需坚决避免使用。 |
7.1.2 大小比较运算符
| 运算符 | 含义 | 代码示例 | 执行结果 |
|---|---|---|---|
> |
大于 | 15 > 8 | true |
< |
小于 | 15 < 8 | false |
>= |
大于等于 | 20 >= 20 | true |
<= |
小于等于 | 20 <= 18 | false |
关键注意事项 :大小比较默认适用于数值类型,若对比字符串,会按照字符Unicode编码 逐位对比,而非数值大小,例如'9' > '10'返回true,因为字符'9'的编码大于'1',此类场景需先将字符串转为数值再对比;对比NaN时,所有比较结果均为false,判断NaN需用isNaN()方法。 |
7.2 逻辑运算符
逻辑运算符用于连接多个比较条件,实现复杂逻辑判断,同样返回布尔值,也可用于布尔值取反、短路运算等场景,包含逻辑与、逻辑或、逻辑非三种,是组合条件的核心工具。
7.2.1 三大逻辑运算符详情
| 运算符 | 名称 | 逻辑规则 | 代码示例 | 执行结果 |
|---|---|---|---|---|
& |
逻辑与(且) | 所有条件全为true,结果才为true;一假则假 | 18 > 10 && 5 < 8 | true |
| ` | ` | 逻辑或(或) | 任意一个条件为true,结果就为true;一真则真 | |
! |
逻辑非(取反) | 单目运算符,直接对布尔值取反,true变false,false变true | !(18 > 10) | false |
7.2.2 实际业务场景示例
javascript
let age = 22;
let score = 85;
// 逻辑与:两个条件同时满足
console.log(age >= 18 && score >= 60); // true
// 逻辑或:满足任一条件
console.log(age < 18 || score < 60); // false
// 逻辑非:取反判断
console.log(!(score >= 90)); // true
7.2.3 核心注意事项
逻辑运算符存在短路运算 特性,可提升代码执行效率:逻辑与&&遇到第一个false条件,直接返回false,后续条件不再执行;逻辑或||遇到第一个true条件,直接返回true,后续条件不再执行。非布尔值参与逻辑运算时,会先转为布尔值再判断,0、''、null、undefined、NaN会转为false,其余数据转为true;连续使用逻辑非(!!)可快速将任意数据转为布尔值,例如!!'hello'返回true。
7.3 两类运算符综合速记
-
比较运算符最终返回true/false,相等判断**
===!==** 只用和,杜绝隐式转换风险 -
字符串做大小对比,务必先通过
Number()转换为数值类型 -
逻辑与
&&需全满足,逻辑或||满足其一即可,逻辑非!直接取反 -
短路运算可优化代码执行,避免无效条件判断
-
两类运算符常配合使用,实现复杂多条件分支逻辑
八、JS this关键字
this是JavaScript中的关键字,指向一个运行时确定的对象 ,并非定义时确定,核心作用是简化对象调用、实现函数复用,是JS核心难点也是高频考点。this的指向完全取决于函数的调用方式,而非函数声明位置,牢记绑定规则即可精准判断指向。
8.1 this核心基础认知
this是函数执行时,自动生成的一个内置对象,指向当前函数执行的上下文对象;普通函数独立调用时,this默认指向全局对象(浏览器中为window,Node环境中为globalThis/undefined),严格模式下普通函数独立调用this指向undefined,避免全局污染。
所有判断this指向的核心原则:谁调用函数,this就指向谁,结合调用场景逐一对应规则,即可快速锁定指向。
8.2 this五大绑定规则(按优先级排序)
8.2.1 new绑定(优先级最高)
使用new关键字调用构造函数时,this指向new创建的实例对象,new会强制改变this指向,优先级高于其他所有绑定规则。
javascript
function Person(name) {
// this指向new创建的实例对象
this.name = name;
}
const p = new Person('张三');
console.log(p.name); // 张三
注意:构造函数中无需手动return对象,new会自动返回实例,若手动return普通对象,this会指向该返回对象,return基本数据类型则不影响this指向。
8.2.2 显式绑定(call/apply/bind)
通过call()、apply()、bind()方法手动指定this指向,优先级高于隐式绑定和默认绑定,三者均可强制改变this指向,区别仅在于传参和执行方式。
-
call():立即执行函数,参数逐个传递
-
apply():立即执行函数,参数以数组形式传递
-
bind():不立即执行,返回一个绑定好this的新函数,永久绑定this,后续无法更改
javascript
const obj = { name: '李四' };
function sayHi(age, gender) {
console.log(this.name, age, gender);
}
// call传参
sayHi.call(obj, 20, '男');
// apply传参
sayHi.apply(obj, [20, '男']);
// bind返回新函数
const newFn = sayHi.bind(obj, 20, '男');
newFn();
注意:call/apply/bind第一个参数为null/undefined时,默认绑定失效,this指向全局对象(非严格模式),严格模式下仍指向传入的null/undefined。
8.2.3 隐式绑定
函数作为对象的方法调用时,this指向调用该方法的对象,也就是方法前面的对象,多层对象嵌套时,this指向直接调用的对象。
javascript
const user = {
name: '王五',
sayName: function() {
console.log(this.name); // this指向user对象
}
};
// 对象调用方法,隐式绑定
user.sayName(); // 王五
// 多层对象嵌套
const parent = {
name: '父对象',
child: {
name: '子对象',
fn: function() {
console.log(this.name); // this指向child子对象
}
}
};
parent.child.fn(); // 子对象
注意:隐式绑定容易出现this丢失问题,将对象方法赋值给独立变量后调用,会触发默认绑定,this指向全局对象。
8.2.4 默认绑定
函数独立调用(无对象调用、无new、无显式绑定)时,触发默认绑定,非严格模式下this指向全局window,严格模式下指向undefined。
javascript
// 独立调用函数
function test() {
console.log(this); // 非严格模式:window
}
test();
// 隐式丢失后独立调用
const user = { name: '赵六', fn: function() { console.log(this.name); } };
const fn1 = user.fn;
fn1(); // undefined,this指向window,无name属性
注意:全局作用域下的函数声明,独立调用一律触发默认绑定,避免在全局函数中随意使用this,防止全局变量污染。
8.2.5. 箭头函数绑定(优先级仅次于new)
ES6箭头函数没有自己的this,继承外层作用域的this,不遵循以上四条规则,无法通过call/apply/bind改变this指向,也不能作为构造函数使用new调用。
javascript
const obj = { name: '箭头函数' };
// 普通函数this指向obj
const fn2 = function() {
// 箭头函数继承外层fn2的this
return () => {
console.log(this.name);
};
};
const arrowFn = fn2.call(obj);
arrowFn(); // 箭头函数
注意:箭头函数适合回调场景(定时器、事件监听),解决普通函数this指向混乱问题,但不适合作为对象方法和构造函数,避免this指向异常。
8.3 this绑定优先级速记
new绑定 > 显式绑定(call/apply/bind) > 隐式绑定 > 默认绑定;箭头函数无自身this,继承外层this,优先级高于普通函数的默认、隐式绑定,低于new绑定。
8.4 高频特殊场景与this指向
8.4.1 定时器中的this
setTimeout/setInterval中的普通回调函数,独立执行触发默认绑定,this指向window;箭头函数则继承外层作用域this。
javascript
const obj = { name: '定时器', fn: function() {
setTimeout(() => {
console.log(this.name); // 继承fn的this,指向obj
}, 1000);
}};
obj.fn(); // 定时器
8.4.2 事件监听中的this
DOM事件监听中,普通回调函数的this指向绑定事件的DOM元素,箭头函数继承外层this,不指向DOM元素,事件监听不推荐使用箭头函数。
8.4.3 闭包中的this
闭包内的普通函数独立调用,this默认指向window,需通过缓存外层this(const that = this)或箭头函数解决指向问题。
8.5 高频易错点总结
-
this指向只和调用方式有关,和函数声明位置、是否嵌套无关
-
严格模式下默认绑定this为undefined,可有效减少全局污染问题
-
bind是永久绑定this,返回的新函数无法通过call/apply再次改变this指向
-
箭头函数没有arguments、没有自己的this,不能用作构造函数,无法使用new
-
对象方法赋值给变量后调用,会丢失隐式绑定,触发默认绑定,可通过bind固定this
-
判断this指向,先看是否为箭头函数,再按优先级排查new、显式、隐式、默认绑定
九、JS HTML DOM
DOM全称文档对象模型(Document Object Model) ,是HTML和XML文档的编程接口,它将整个网页文档解析成树状结构(DOM树),让JavaScript可以动态访问、修改网页的内容、结构和样式,实现网页的动态交互效果,是前端JS操作网页的核心基础。
9.1 DOM核心基础概念
-
文档 :整个HTML网页文档,对应DOM中的
document对象,是DOM树的根节点,所有DOM操作都从document开始。 -
元素:HTML中的各类标签,如
、
、,是DOM操作的主要对象。
-
节点 :DOM树中最小单元,包含元素节点、文本节点、属性节点、注释节点等,其中元素节点是最常用操作对象。
-
DOM树:网页标签按照嵌套关系形成的层级树结构,父子节点、兄弟节点关系明确,方便层级查找。
9.2 DOM元素获取(核心操作第一步)
想要操作网页元素,首先要通过JS获取到对应的DOM元素,以下是高频常用的获取方法,覆盖绝大多数场景:
| 获取方法 | 语法格式 | 返回结果 | 适用场景 |
|---|---|---|---|
| 根据ID获取 | document.getElementById('id名') | 单个元素对象/null | 获取唯一元素,ID具有唯一性,效率最高 |
| 根据类名获取 | document.getElementsByClassName('类名') | 类数组对象(HTMLCollection) | 获取多个同类名元素 |
| 根据标签名获取 | document.getElementsByTagName('标签名') | 类数组对象(HTMLCollection) | 获取同类标签元素,如所有p、div |
| 选择器获取单个 | document.querySelector('CSS选择器') | 单个元素对象/null | 支持CSS选择器,灵活获取单个元素,推荐使用 |
| 选择器获取全部 | document.querySelectorAll('CSS选择器') | 类数组对象(NodeList) | 支持CSS选择器,批量获取多个元素,推荐使用 |
关键注意 :通过类数组获取的元素,不能直接操作,需通过下标索引 取出单个元素后再操作,例如document.getElementsByClassName('box')[0]。 |
9.3 DOM元素内容操作
获取元素后,可动态修改元素内部的文本、HTML结构,实现内容实时更新,常用三种操作属性:
-
innerText :仅操作元素内的纯文本内容 ,不解析HTML标签,安全性高,无法识别标签。
const box = document.querySelector('.box'); // 设置纯文本 box.innerText = '更新后的文本内容'; -
innerHTML :操作元素内的HTML结构+文本 ,可解析HTML标签,适合动态插入标签,注意防范XSS攻击。
// 插入HTML结构 box.innerHTML = '<h3>动态标题</h3><p>动态段落</p>'; -
textContent:类似innerText,会保留文本格式和隐藏文本,兼容性更好,推荐优先使用。
9.4 DOM元素属性操作
9.4.1 原生属性操作(自带属性,如id、src、href、title)
直接通过元素.属性名获取或修改,语法简洁,适配元素自带属性。
javascript
const img = document.querySelector('img');
// 修改src属性
img.src = 'images/new.jpg';
// 修改title属性
img.title = '新图片描述';
9.4.2 自定义属性操作
针对自定义属性(如data-开头的自定义属性),使用getAttribute()、setAttribute()、removeAttribute()方法操作。
javascript
const div = document.querySelector('div');
// 设置自定义属性
div.setAttribute('data-index', '1');
// 获取自定义属性
console.log(div.getAttribute('data-index'));
// 删除自定义属性
div.removeAttribute('data-index');
9.4.3 类名操作(样式控制核心)
-
元素.className:直接覆盖或设置类名,会替换原有类名
-
元素.classList.add('类名'):添加类名,不覆盖原有
-
元素.classList.remove('类名'):移除指定类名
-
元素.classList.toggle('类名'):切换类名(有则删,无则加)
-
元素.classList.contains('类名'):判断是否包含该类名,返回布尔值
9.4.4 样式操作(行内样式)
通过元素.style.样式属性修改行内样式,属性名采用驼峰命名(如backgroundColor、fontSize)。
javascript
const box = document.querySelector('.box');
box.style.width = '200px';
box.style.backgroundColor = '#ccc';
box.style.fontSize = '16px';
9.5 DOM节点操作(增删改查节点)
9.5.1 创建新节点
javascript
// 创建元素节点
const newDiv = document.createElement('div');
// 创建文本节点
const textNode = document.createTextNode('新建文本');
9.5.2 添加节点到页面
-
父元素.appendChild(子元素):追加到父元素末尾
-
父元素.insertBefore(新元素, 参考元素):插入到参考元素前面
9.5.3 删除节点
javascript
// 父元素删除子节点
父元素.removeChild(要删除的子元素);
9.5.4 替换节点
javascript
父元素.replaceChild(新元素, 被替换元素);
9.5.5 节点层级关系查找
-
父元素:元素.parentNode
-
子元素:元素.children(仅获取元素子节点,推荐)、元素.childNodes(含所有节点)
-
第一个子元素:元素.firstElementChild
-
最后一个子元素:元素.lastElementChild
-
上一个兄弟元素:元素.previousElementSibling
-
下一个兄弟元素:元素.nextElementSibling
9.6 DOM事件基础(交互核心)
DOM操作常配合事件实现交互,给元素绑定事件,触发后执行对应操作,常用绑定方式:
-
DOM0级:元素.on+事件名 = 函数(覆盖式绑定)
const btn = document.querySelector('button'); btn.onclick = function(){ alert('点击触发') }; -
DOM2级:元素.addEventListener('事件名', 函数)(可绑定多个事件,推荐)
btn.addEventListener('click', function(){ console.log('点击触发') });
常用DOM事件:click(点击)、input(输入)、change(内容改变)、mouseenter(鼠标移入)、mouseleave(鼠标移出)、submit(表单提交)。
9.7 DOM操作高频注意事项
-
DOM操作必须等待HTML文档加载完成后执行,否则获取不到元素,可将script标签放在body末尾,或用DOMContentLoaded事件监听。
-
类数组对象不能直接调用数组方法,如需遍历可转为数组或用for循环。
-
innerHTML插入外部内容时,注意防范XSS攻击,避免插入不可信的HTML代码。
-
频繁DOM操作会影响页面性能,建议先拼接好内容,再一次性插入页面。
-
获取不存在的元素,返回null,操作null会报错,获取后建议先判断是否存在。
- 使用 document.write() 可以向文档写入内容。如果在文档已完成加载后执行 document.write,整个 HTML 页面将被覆盖。 ↩︎