JavaScript_基础教程_自学笔记

文章目录

  • 一、概述
  • 二、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 两类操作符核心注意事项

  1. 算术操作符优先处理纯数值运算,遇到字符串优先拼接,如需数值相加,需提前用Number()转换数据类型;
  2. 赋值操作符中,**=**是基础核心,其余均为复合简写,执行顺序是先算右侧运算,再赋值给左侧变量;
  3. 避免对非数值变量直接使用算术操作符,容易得到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. 误区1 :null和undefined一样

    解答:undefined是变量声明未赋值,null是手动设置的空值

  2. 误区2 :数组是独立基本类型

    解答:数组属于引用类型,是特殊的对象

  3. 误区3 :字符串用+数字是加法

    解答:只要一侧是字符串,+号执行拼接操作

  4. 误区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 对象的核心特性

  1. 引用类型特性 :对象赋值是传递内存地址,不是拷贝数据,修改新对象会影响原对象。
    let obj1 = { name: "张三" }; let obj2 = obj1; // 传递地址 obj2.name = "李四"; console.log(obj1.name); // 李四(原对象被修改)

  2. 属性名特性:对象的键(属性名)会自动转为字符串,数字键也会转为字符串类型。

  3. 动态特性:对象的属性和方法可以随时增删改,无需提前固定结构。

  4. 方法简写 :ES6支持对象方法简写,省略function关键字,写法更简洁。
    let person = { sayHello() { // 方法简写 console.log("你好"); } };


4.7 新手常见注意事项与避坑

1. 访问不存在的属性 :不会报错,返回undefined,可用于判断属性是否存在。
2. 点语法和方括号语法不可混用 :属性名含特殊字符、变量做属性名,必须用方括号语法。
3. 对象赋值易踩坑 :想要拷贝对象而非地址,需用浅拷贝/深拷贝(基础阶段可手动遍历赋值)。
4. 属性名重复 :同一对象中属性名重复,后定义的会覆盖先定义的。
5. this指向误区:对象方法单独调用时,this会丢失指向,不再指向原对象。


五、JS作用域

作用域是JS中变量、函数、对象的可访问范围 ,决定了代码块中变量和其他资源的可见性,是JS核心基础知识点,也是避免变量污染、排查变量报错的关键。作用域的核心作用是隔离变量,不同作用域下的同名变量不会冲突,同时控制变量的生命周期。


5.1 作用域基础核心概念

  1. 全局作用域:代码最外层的作用域,整个脚本文件、浏览器窗口或Node环境中都能访问,生命周期伴随页面/进程始终。

  2. 局部作用域:也叫函数作用域,仅在函数内部可访问,函数外部无法直接使用,函数执行完毕后变量会被销毁。

  3. 块级作用域 :ES6新增,由letconst声明产生,在{}包裹的代码块(if、for、while等)内有效,代码块执行完毕变量销毁。

  4. 作用域链 :内部函数访问外部函数变量时,会逐级向上查找作用域,直到全局作用域,这个层级关系就是作用域链,查找顺序是局部→外层→全局

核心注意事项 :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新增)

letconst声明的变量,在{}包裹的代码块(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声明的变量没有块级作用域 ,只有函数作用域和全局作用域,会穿透代码块;日常开发优先使用letconst,避免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声明的变量提升

letconst存在暂时性死区(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 作用域核心易错点总结

  1. var没有块级作用域,let/const有块级作用域,这是三者最核心区别

  2. 局部作用域变量优先级高于同名全局变量,就近访问,不会覆盖全局变量

  3. 作用域在代码定义时确定,而非执行时,词法作用域规则牢记

  4. 外层作用域无法访问内层作用域变量,内层可访问外层,单向查找

  5. 闭包虽好用,但需注意内存泄漏问题,及时清理引用

  6. 避免隐式全局变量,所有变量必须用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 事件流(事件执行顺序)

事件流描述事件在页面中传播的顺序,分为三个阶段,是理解事件冒泡和捕获的核心。

  1. 捕获阶段:从最外层document向内层事件源传递,从上到下

  2. 目标阶段:到达触发事件的元素本身

  3. 冒泡阶段:从内层事件源向外层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事件核心注意事项

  1. 行内事件绑定耦合HTML与JS,项目开发优先用事件监听addEventListener

  2. 同一元素用on+事件名绑定,重复绑定会覆盖,addEventListener可绑定多个处理函数

  3. 事件冒泡是默认机制,容易引发交互bug,不确定时可主动阻止冒泡

  4. 事件对象e是自动传递的,无需手动传参,直接在函数内接收即可

  5. 动态生成的元素,无法直接绑定事件,必须用事件委托解决

  6. preventDefault阻止默认行为,stopPropagation阻止冒泡,二者功能不同,不可混用

  7. 高频触发事件(scroll、mousemove),建议做节流或防抖,避免性能损耗

七、JS比较、逻辑运算符【操作符补充1】

比较运算符和逻辑运算符是JS中实现条件判断、逻辑分支 的核心工具,二者常配合if语句、循环语句使用,运算结果均为布尔值(truefalse)。


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 两类运算符综合速记

  1. 比较运算符最终返回true/false,相等判断**===!==** 只用和,杜绝隐式转换风险

  2. 字符串做大小对比,务必先通过Number()转换为数值类型

  3. 逻辑与&amp;&amp;需全满足,逻辑或||满足其一即可,逻辑非!直接取反

  4. 短路运算可优化代码执行,避免无效条件判断

  5. 两类运算符常配合使用,实现复杂多条件分支逻辑

八、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 高频易错点总结

  1. this指向只和调用方式有关,和函数声明位置、是否嵌套无关

  2. 严格模式下默认绑定this为undefined,可有效减少全局污染问题

  3. bind是永久绑定this,返回的新函数无法通过call/apply再次改变this指向

  4. 箭头函数没有arguments、没有自己的this,不能用作构造函数,无法使用new

  5. 对象方法赋值给变量后调用,会丢失隐式绑定,触发默认绑定,可通过bind固定this

  6. 判断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操作常配合事件实现交互,给元素绑定事件,触发后执行对应操作,常用绑定方式:

  1. DOM0级:元素.on+事件名 = 函数(覆盖式绑定)
    const btn = document.querySelector('button'); btn.onclick = function(){ alert('点击触发') };

  2. DOM2级:元素.addEventListener('事件名', 函数)(可绑定多个事件,推荐)
    btn.addEventListener('click', function(){ console.log('点击触发') });

常用DOM事件:click(点击)、input(输入)、change(内容改变)、mouseenter(鼠标移入)、mouseleave(鼠标移出)、submit(表单提交)。


9.7 DOM操作高频注意事项

  1. DOM操作必须等待HTML文档加载完成后执行,否则获取不到元素,可将script标签放在body末尾,或用DOMContentLoaded事件监听。

  2. 类数组对象不能直接调用数组方法,如需遍历可转为数组或用for循环。

  3. innerHTML插入外部内容时,注意防范XSS攻击,避免插入不可信的HTML代码。

  4. 频繁DOM操作会影响页面性能,建议先拼接好内容,再一次性插入页面。

  5. 获取不存在的元素,返回null,操作null会报错,获取后建议先判断是否存在。



  1. 使用 document.write() 可以向文档写入内容。如果在文档已完成加载后执行 document.write,整个 HTML 页面将被覆盖。 ↩︎
相关推荐
angerdream2 小时前
最新版vue3+TypeScript开发入门到实战教程之生命周期函数
javascript·vue.js
我叫黑大帅2 小时前
🚀 JS 最常用的性能优化 防抖和节流
前端·javascript·面试
難釋懷2 小时前
Lua语法入门-变量和循环
开发语言·junit·lua
csbysj20202 小时前
CSS 颜色
开发语言
图扑软件2 小时前
图扑 HT 帧动画 | 3D 动态渲染设计与实现
前端·javascript·3d·动画·数字孪生
2401_833197732 小时前
C++代码切片分析
开发语言·c++·算法
是翔仔呐2 小时前
第13章 超声波测距传感器驱动:HC-SR04底层原理与C语言实现
c语言·开发语言·单片机·嵌入式硬件·gitee
m0_621438522 小时前
实时音频处理C++实现
开发语言·c++·算法
weixin_421922692 小时前
模板代码性能测试
开发语言·c++·算法