在数字化时代,JavaScript 是全球热门编程语言,应用覆盖前端交互、Node.js 后端服务、移动应用开发等领域。其无需编译、逐行解释执行的特性,既适合初学者入门,也能满足大型复杂项目需求。本文将系统梳理 JavaScript 从基础概念到流程控制的核心知识,助力读者构建完整知识框架、掌握实用技能。


1. JavaScript 核心认知

JavaScript 本质是一种客户端脚本语言,无需编译,运行时由 JavaScript 引擎(解释器)逐行解释执行。随着 Node.js 技术的出现,JavaScript 突破了“仅客户端运行”的限制,现已广泛应用于服务器端编程,实现了“前后端统一语言”的开发模式。

1.1 浏览器与 JavaScript 的关系

浏览器的核心功能由两部分实现,二者协同完成网页的渲染与交互:

  • 渲染引擎(内核):负责解析 HTML 和 CSS,将代码转换为可视化的网页界面(如 Chrome 的 Blink 引擎、Firefox 的 Gecko 引擎)。
  • JS 引擎:独立于渲染引擎,专门读取并执行 JavaScript 代码(如 Chrome 的 V8 引擎,也是 Node.js 的底层引擎)。

关键说明:浏览器本身不直接执行 JS 代码,而是通过内置的 JS 引擎将源码逐行转换为机器语言,再交由计算机执行。这也是 JavaScript 被归为“脚本语言”的核心原因。

浏览器引擎结构

1.2 JavaScript 的三大核心组成

JavaScript 的功能覆盖“语法基础”“网页操作”“浏览器控制”,具体由以下三部分构成:

JavaScript 三大组成

  • ECMAScript:JavaScript 的语法核心
    定义了 JavaScript 的基础语法规则,包括变量声明、数据类型、运算符、语句(if-else、for 等)、函数、对象等,是所有浏览器厂商共同遵守的工业标准

  • DOM(文档对象模型):网页内容的操作接口
    将 HTML/XML 文档抽象为树形结构(DOM 树),树中的每个节点对应网页中的一个元素(如 <div><p>)。通过 DOM 提供的 API,JavaScript 可动态修改网页的内容、结构和样式(如添加元素、修改文本、改变颜色)。

  • BOM(浏览器对象模型):浏览器窗口的控制接口
    提供与浏览器窗口交互的对象和方法,允许 JavaScript 控制浏览器的行为,如打开/关闭窗口、调整窗口大小、获取浏览器版本、操作地址栏等。

1.3 JavaScript 引入

在 HTML 中引入 JavaScript 有三种方式,需根据场景选择,同时需注意代码的可维护性和性能:

1.3.1 内联脚本(事件属性绑定)

将 JS 代码直接嵌入 HTML 标签的事件属性中(如 onclickoninput),事件触发时执行代码。

适用于极简单的交互(如点击提示、单步表单验证),不建议用于复杂逻辑。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>内联脚本示例</title>
</head>
<body>
    <!-- 点击按钮弹出提示(注意单引号包裹字符串,避免与 HTML 双引号冲突) -->
    <button onclick="alert('点击成功!')">点击我</button>
    <!-- 输入内容实时打印到控制台(this 指向当前输入框元素) -->
    <input type="text" oninput="console.log('当前输入:' + this.value)">
</body>
</html>

优缺点

  • ✅ 优点:简单直接,无需额外代码块。
  • ❌ 缺点:JS 与 HTML 强耦合(代码分散在标签中),难以维护;无法实现复杂逻辑。

1.3.2 内部脚本(<script> 标签嵌入)

将 JS 代码放在 HTML 文件的 <script> 标签内,可位于 <head><body> 中。 若脚本需操作 DOM(如获取 <body> 中的元素),需确保脚本在 DOM 加载完成后执行(可通过 window.onload 或将 <script> 放在 <body> 末尾实现)。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>内部脚本示例</title>
    <script>
        // 等待页面所有资源(DOM、图片等)加载完成后执行
        window.onload = function () {
            // 1. 创建按钮元素
            const button = document.createElement('button');
            button.textContent = '动态创建的按钮';
            // 2. 为按钮绑定点击事件
            button.onclick = function () {
                alert('动态按钮被点击了!');
            };
            // 3. 将按钮添加到页面中
            document.body.appendChild(button);
        };
    </script>
</head>
<body>
    <!-- 脚本通过 window.onload 等待 DOM 加载,此处无需额外操作 -->
</body>
</html>

优缺点

  • ✅ 优点:JS 代码集中管理,便于维护;可直接操作 DOM(需注意加载顺序)。
  • ❌ 缺点:若代码量大,会增加 HTML 文件体积;无法在多个页面复用。

1.3.3 外部脚本(独立 .js 文件引入)

将 JS 代码单独存放在 .js 文件中,通过 <script> 标签的 src 属性引入 HTML。

适用于中大型项目、多页面复用相同逻辑(如公共组件、工具函数)。

  1. 创建外部 JS 文件(script.js
// script.js
window.onload = function () {
    const button = document.createElement('button');
    button.textContent = '外部脚本创建的按钮';
    button.onclick = function () {
        alert('外部脚本生效!');
    };
    document.body.appendChild(button);
};
  1. 在 HTML 中引入
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>外部脚本示例</title>
    <!-- 引入外部 JS 文件(src 属性指定文件路径) -->
    <script src="script.js"></script>
    <!-- 若需引入多个脚本,可多次使用 <script> 标签 -->
    <!-- <script src="utils.js"></script> -->
</head>
<body>
</body>
</html>

大型项目中,可添加 asyncdefer 属性实现脚本异步加载(避免阻塞 HTML 解析):

  • async:脚本加载完成后立即执行(顺序不保证);
  • defer:脚本加载完成后,等待 HTML 解析完毕再执行(顺序与引入顺序一致)。
<script src="script.js" defer></script>

优缺点

  • ✅ 优点:代码与 HTML 完全分离,可维护性高;支持多页面复用;便于团队协作(分工编写 JS 和 HTML)。
  • ❌ 缺点:需额外发起 HTTP 请求加载文件(可通过打包工具合并文件优化)。

1.4 JavaScript 注释

注释是代码的“说明书”,用于解释逻辑、标注功能,不会被 JS 引擎执行。规范的注释可大幅提升代码可读性和可维护性,分为以下三类:

  1. 单行注释:以 // 开头,注释范围仅限当前行,适用于简短说明(如变量含义、代码功能)。
// 声明用户年龄变量(单行注释)
let userAge = 18;
console.log(userAge); // 打印用户年龄(行尾注释)
  1. 多行注释:以 /* 开头、*/ 结尾,注释范围可跨多行,适用于复杂逻辑的说明(如函数功能、代码块用途)。
/*
 * 功能:计算两个数的和
 * 输入:num1(第一个数)、num2(第二个数)
 * 输出:两数之和
 */
function add(num1, num2) {
    return num1 + num2;
}
  1. 文档注释(JSDoc 规范):以 /** 开头、*/ 结尾,是一种标准化的多行注释,可通过工具(如 JSDoc、VS Code 插件)自动生成 HTML 文档,适用于函数、类、模块的说明。
/**
 * 计算两个数的乘积
 * @param {number} num1 - 第一个乘数(必须为数字)
 * @param {number} num2 - 第二个乘数(必须为数字)
 * @returns {number} 两个数的乘积
 * @example multiply(2, 3) // 返回 6
 */
function multiply(num1, num2) {
    return num1 * num2;
}

1.5 JavaScript 输入输出语句

输入输出是与用户交互、调试代码的核心手段,浏览器提供了以下常用方法:

方法 功能描述 归属 数据流向 适用场景
alert(message) 弹出警示框,显示文本信息 浏览器 输出 向用户展示简单提示(如操作结果)
console.log(msg) 在浏览器控制台打印信息(F12 打开控制台) 浏览器 输出 开发者调试代码(查看变量值、执行流程)
prompt(info, def) 弹出输入框,接收用户输入(def 为默认值) 浏览器 输入 收集用户简单输入(如用户名、年龄)
confirm(msg) 弹出确认框(含“确定”“取消”),返回布尔值 浏览器 输入+输出 确认用户操作(如删除数据、退出登录)
// 1. 弹出提示
alert('欢迎使用用户信息收集工具!');

// 2. 接收用户输入(默认值为“请输入姓名”)
let userName = prompt('请输入您的姓名:', '请输入姓名');

// 3. 确认用户输入
let isConfirm = confirm(`您输入的姓名是:${userName},是否确认?`);

// 4. 打印结果到控制台(调试用)
if (isConfirm) {
    console.log(`用户确认姓名:${userName}`);
    alert(`欢迎您,${userName}`);
} else {
    console.log('用户取消确认');
    alert('请重新输入姓名!');
}

2. 变量

变量是程序在内存中申请的一块存储数据的空间,通过“变量名”可快速访问或修改其中的数据。

2.1 变量的声明与赋值(ES5 vs ES6)

JavaScript 中声明变量的方式有三种,需注意各自的特性(尤其是作用域和可修改性):

声明方式 作用域 可重复声明 可重新赋值 初始化要求 适用场景
var 函数级作用域 ES5 及以前代码(不推荐)
let 块级作用域 需修改的变量(推荐)
const 块级作用域 常量(如 PI、配置项,推荐)
// 1. var 声明(ES5,存在“变量提升”和“全局污染”问题)
var age = 18;
var age = 20; // 允许重复声明,覆盖原有值
console.log(age); // 输出 20

// 2. let 声明(ES6,块级作用域,无重复声明)
let name = 'Alice';
// let name = 'Bob'; // 报错:Identifier 'name' has already been declared
name = 'Bob'; // 允许重新赋值
console.log(name); // 输出 Bob

// 3. const 声明(ES6,常量,不可修改)
const PI = 3.14159;
// PI = 3.14; // 报错:Assignment to constant variable
const user = { name: 'Charlie' };
user.name = 'David'; // 允许修改对象内部属性(const 仅限制变量指向的地址,不限制对象内容)
console.log(user.name); // 输出 David
  • 初始化:声明变量的同时赋值(推荐,避免 undefined):
    let score = 90; // 初始化变量
    
  • 值覆盖:变量重新赋值后,原有值被覆盖,以最后一次赋值为准:
    let score = 90;
    score = 95; // 覆盖原有值
    console.log(score); // 输出 95
    
  • 批量声明:使用 varlet 批量声明变量(用逗号分隔):
    let name = 'Eve', age = 22, isStudent = true;
    

变量声明的特殊情况

场景 说明 结果 风险
var a; console.log(a); 只声明,不赋值 undefined(未定义) 可能导致逻辑错误(如 a + 1 结果为 NaN
console.log(b); 不声明,不赋值,直接使用 报错(ReferenceError: b is not defined 语法错误,代码终止执行
c = 10; console.log(c); 不声明,只赋值(隐式全局变量) 10 污染全局作用域,易与其他变量冲突(严格模式下报错)

严格模式('use strict'):在脚本开头添加该语句,可禁止隐式全局变量、禁止重复声明等,提升代码规范性:

'use strict';
c = 10; // 报错:ReferenceError: c is not defined

2.2 变量命名规范(行业标准)

变量名需遵循“合法、易读、语义化”原则,具体规则如下:

2.2.1 语法规则(必须遵守)

  1. 可包含:字母(A-Z, a-z)、数字(0-9)、下划线(_)、美元符号($);
  2. 不能以数字开头(如 123name 非法);
  3. 区分大小写(userNameusername 是两个不同变量);
  4. 不能使用 JavaScript 关键字(如 varletfunction)和保留字(如 classenum)。

2.2.2 风格规范(推荐遵守)

  1. 小驼峰命名法(变量、函数):首字母小写,后续单词首字母大写(如 userNamegetUserInfo);
  2. 语义化命名:变量名需体现数据含义(如 age 而非 atotalScore 而非 ts);
  3. 避免缩写:除非是通用缩写(如 idurl),否则不使用模糊缩写(如 usr 不如 user 清晰)。
  • 规范与不规范命名对比
不规范命名 问题 规范命名
a 语义不清晰 age
user_name 不符合小驼峰 userName
123score 以数字开头 score123
let 使用关键字 letValue

2.3 变量数据类型

JavaScript 是弱类型(动态类型)语言:无需提前声明变量类型,变量类型由赋值的值自动决定,且可动态改变。

2.3.1 数据类型分类

JavaScript 数据类型分为:简单数据类型(基本类型)和 复杂数据类型(引用类型):

类型分类 具体类型 说明 示例
简单数据类型 Number 数字(整数、小数、特殊值) 103.14Infinity
String 字符串(文本,需用引号包裹) "Hello"'JavaScript'
Boolean 布尔值(逻辑判断,仅 true/false truefalse
Undefined 未定义(变量声明未赋值) let a;aUndefined
Null 空值(变量指向“无”) let b = null;
复杂数据类型 Object 对象(键值对集合,如数组、函数、DOM 元素) { name: 'Alice' }[1,2,3]

2.3.2 简单数据类型详解

1. 数字型(Number)
  • 包含范围:整数(如 10)、小数(如 3.14)、特殊值(Infinity-InfinityNaN);

  • 进制表示

    • 十进制:默认(如 10);
    • 八进制:以 0 开头(如 012 等于十进制 10);
    • 十六进制:以 0x 开头(如 0xA 等于十进制 10);
  • 特殊值说明

    • Infinity:无穷大(如 1 / 0);
    • -Infinity:无穷小(如 -1 / 0);
    • NaN:非数字(Not a Number,如 'abc' - 1),且 NaN !== NaN(唯一不等于自身的值)。
  • 判断是否为数字isNaN(value) 函数,返回 true 表示“非数字”,false 表示“数字”:

    console.log(isNaN(123)); // false(123 是数字)
    console.log(isNaN('123')); // false('123' 可隐式转换为数字)
    console.log(isNaN('abc')); // true('abc' 无法转换为数字)
    
  • 浮点数精度问题:JavaScript 浮点数精度有限(最高 17 位小数),避免直接比较浮点数:

    console.log(0.1 + 0.2); // 输出 0.30000000000000004(非 0.3)
    // 正确比较方式:判断差值的绝对值是否小于极小值(如 1e-10)
    console.log(Math.abs(0.1 + 0.2 - 0.3) < 1e-10); // true
    
2. 字符串型(String)
  • 语法规则:需用单引号(')、双引号(")或反引号(`,ES6 新增)包裹,推荐单引号或反引号;

  • 引号嵌套:遵循“外单内双”或“外双内单”,避免冲突:

    let str1 = 'He said: "Hello!"'; // 外单内双
    let str2 = "She's a student"; // 外双内单
    
  • 反引号特性(ES6 模板字符串):

    • 支持多行字符串(无需 \n);
    • 支持变量插值(${变量名});
    let name = 'Alice';
    let age = 18;
    // 多行字符串 + 变量插值
    let info = `Name: ${name}
    Age: ${age}`;
    console.log(info);
    // 输出:
    // Name: Alice
    // Age: 18
    
  • 字符串转义符:以 \ 开头,用于表示特殊字符:

    转义符 说明 示例
    \n 换行 "Line1\nLine2"
    \\ 反斜杠 "C:\\Users"
    \' 单引号 'He\'s here'
    \" 双引号 "She said \"Hi\""
    \t 制表符(Tab 缩进) "Name:\tAlice"
  • 字符串长度:通过 length 属性获取字符串的字符个数(包含空格和转义符):

    let str = 'Hello World!';
    console.log(str.length); // 输出 12(空格和感叹号均算一个字符)
    
  • 字符串拼接:使用 + 运算符,规则为“字符串 + 任何类型 = 新字符串”(非字符串会先转为字符串):

    console.log('Age: ' + 18); // 输出 "Age: 18"(数字 18 转为字符串)
    console.log('Hello' + ' ' + 'World'); // 输出 "Hello World"(字符串拼接)
    console.log('100' + 20); // 输出 "10020"(数字 20 转为字符串)
    

    口诀:“数值相加,字符相连”——若 + 两边均为数字,则执行加法;否则执行字符串拼接。

3. 布尔型(Boolean)
  • 取值:仅 true(真,等价于数字 1)和 false(假,等价于数字 0);
  • 与其他类型的转换
    • 布尔值与数字相加时,true1 计算,false0 计算:
      console.log(true + 1); // 输出 2
      console.log(false + 1); // 输出 1
      
    • 其他类型转为布尔值的规则:
      类型 转为 false 的情况 转为 true 的情况
      Number 0NaN 0 且非 NaN(如 13.14
      String 空字符串("" 非空字符串(如 "abc"" "
      Undefined undefined -
      Null null -
      Object - 所有对象(如 {}[]
4. Undefined 与 Null
  • Undefined:变量声明未赋值时的默认值,或函数无返回值时的返回值:

    let a;
    console.log(a); // 输出 undefined
    function noReturn() {}
    console.log(noReturn()); // 输出 undefined
    
    • 与其他类型运算的结果:
      console.log('Hello' + undefined); // 输出 "Helloundefined"
      console.log(1 + undefined); // 输出 NaN
      console.log(true + undefined); // 输出 NaN
      
  • Null:表示“空值”,通常用于主动清空变量(如释放对象引用):

    let obj = { name: 'Alice' };
    obj = null; // 清空 obj 的引用,释放内存
    
    • 与其他类型运算的结果:
      console.log('Hello' + null); // 输出 "Hellonull"
      console.log(1 + null); // 输出 1(null 等价于 0)
      console.log(true + null); // 输出 1(null 等价于 0)
      
  • 区别undefined 表示“未定义”(变量未赋值),null 表示“空”(变量主动清空);typeof null 返回 object(历史遗留问题),typeof undefined 返回 undefined

2.4 获取变量类型:typeof 运算符

typeof 用于检测变量的数据类型,返回值为字符串(如 "number""string"),语法为 typeof 变量名typeof(变量名)

let num = 10;
let str = 'Hello';
let bool = true;
let undef;
let nul = null;
let obj = { name: 'Alice' };

console.log(typeof num); // 输出 "number"
console.log(typeof str); // 输出 "string"
console.log(typeof bool); // 输出 "boolean"
console.log(typeof undef); // 输出 "undefined"
console.log(typeof nul); // 输出 "object"(历史遗留问题,需注意)
console.log(typeof obj); // 输出 "object"
  • 注意:typeof null 的特殊情况

typeof null 返回 object 是 JavaScript 的历史 bug(早期实现中,null 被视为空对象指针),需通过其他方式判断 null:

let nul = null;
console.log(nul === null); // 输出 true(直接比较值)

2.5 数据类型转换

在实际开发中,经常需要将一种数据类型转换为另一种(如将表单输入的字符串转为数字进行计算),主要分为“显式转换”和“隐式转换”。

2.5.1 转换为字符串(3种方式)

方式 语法 说明 示例
toString() 方法 变量.toString() 适用于 Number、Boolean 等类型,nullundefined 无此方法 let num = 10; num.toString(); // "10"
String() 函数 String(变量) 通用方法,可转换所有类型 String(null); // "null"
字符串拼接(+ "" 变量 + "" 隐式转换,简单高效 let num = 10; num + ""; // "10"
let num = 10;
let bool = true;
let nul = null;

// 1. toString() 方法
console.log(num.toString()); // "10"
console.log(bool.toString()); // "true"
// console.log(nul.toString()); // 报错(null 无此方法)

// 2. String() 函数
console.log(String(num)); // "10"
console.log(String(nul)); // "null"

// 3. 字符串拼接
console.log(num + ""); // "10"
console.log(bool + ""); // "true"

2.5.2 转换为数字型(4种方式)

方式 语法 说明 示例
parseInt() 函数 parseInt(字符串) 提取整数(忽略非数字字符,从左到右),不处理小数 parseInt("123abc"); // 123 parseInt("12.34"); // 12
parseFloat() 函数 parseFloat(字符串) 提取浮点数(支持小数),同样忽略非数字字符 parseFloat("12.34abc"); // 12.34
Number() 函数 Number(变量) 严格转换,仅纯数字字符串可转为数字,否则为 NaN Number("123"); // 123 Number("12a"); // NaN
隐式转换(- * / 字符串 - 0 / 字符串 * 1 利用算术运算符触发隐式转换,效果同 Number() "123" - 0; // 123 "12a" * 1; // NaN
<input type="text" id="scoreInput" placeholder="请输入成绩">
<button onclick="calculate()">计算成绩等级</button>

<script>
function calculate() {
    // 1. 获取表单输入(默认是字符串)
    let scoreStr = document.getElementById("scoreInput").value;
    
    // 2. 转换为数字(使用 parseInt(),成绩为整数)
    let score = parseInt(scoreStr);
    
    // 3. 判断是否为有效数字
    if (isNaN(score) || score < 0 || score > 100) {
        alert("请输入 0-100 之间的有效数字!");
        return;
    }
    
    // 4. 计算等级
    let grade = score >= 90 ? "A" : (score >= 80 ? "B" : "C");
    alert(`您的成绩等级是:${grade}`);
}
</script>

2.5.3 转换为布尔型(2种方式)

方式 语法 说明 示例
Boolean() 函数 Boolean(变量) 显式转换,遵循“假值规则” Boolean(""); // false Boolean(1); // true
逻辑非(!! !!变量 隐式转换,两次取反(第一次转为布尔值,第二次恢复原布尔值) !!0; // false !!"abc"; // true
let emptyStr = "";
let num = 10;
let undef;

console.log(Boolean(emptyStr)); // false
console.log(Boolean(num)); // true
console.log(Boolean(undef)); // false

console.log(!!emptyStr); // false
console.log(!!num); // true
console.log(!!undef); // false

假值规则:以下值会转为 false,其余均转为 true

  • 空字符串(""
  • 数字 0NaN
  • undefinednull

3. 运算符

运算符(Operator)是用于操作变量或值的符号,实现赋值、算术计算、比较判断等功能。JavaScript 中的运算符种类繁多,需重点掌握常用类型的用法和优先级。

3.1 算术运算符

用于执行基本的数学运算,包括加、减、乘、除、取余(模)。

运算符 名称 功能 示例 结果
+ 加法 两数相加 / 字符串拼接 10 + 20 / "10" + 20 30 / "1020"
- 减法 两数相减 20 - 10 10
* 乘法 两数相乘 10 * 20 200
/ 除法 两数相除(结果为浮点数) 20 / 10 / 20 / 3 2 / 6.666...
% 取余(模) 返回除法的余数(符号与被除数一致) 9 % 2 / -9 % 2 1 / -1
  1. 加法的双重含义:若 + 两边均为数字,执行加法;否则执行字符串拼接(见 2.3.2 字符串拼接)。
  2. 除法的浮点数结果:即使整除,结果也为浮点数(如 10 / 2 = 5,但类型为 Number)。
  3. 取余的应用场景:判断奇偶数(num % 2 === 0 为偶数)、循环周期(如每隔 3 次执行一次操作)。

示例:取余判断奇偶数

function isEven(num) {
    // 若 num 为偶数,返回 true;否则返回 false
    return num % 2 === 0;
}

console.log(isEven(10)); // true
console.log(isEven(11)); // false

3.2 递增/递减运算符

用于实现变量“自加 1”或“自减 1”,是 num = num + 1 / num = num - 1 的简写,分为“前置”和“后置”两种形式。

运算符 名称 语法 执行顺序 示例 结果
++ 前置递增 ++num 先自加,后使用变量值 let num = 10; console.log(++num); 11(num 变为 11)
++ 后置递增 num++ 先使用变量值,后自加 let num = 10; console.log(num++); 10(num 变为 11)
-- 前置递减 --num 先自减,后使用变量值 let num = 10; console.log(--num); 9(num 变为 9)
-- 后置递减 num-- 先使用变量值,后自减 let num = 10; console.log(num--); 10(num 变为 9)

示例:前置与后置的区别

// 1. 前置递增
let a = 10;
let b = ++a; // a 先自加为 11,再赋值给 b
console.log(a); // 11
console.log(b); // 11

// 2. 后置递增
let c = 10;
let d = c++; // c 先赋值给 d(d=10),再自加为 11
console.log(c); // 11
console.log(d); // 10

应用:循环计数器

// 使用后置递增实现循环(i 从 0 到 4)
for (let i = 0; i < 5; i++) {
    console.log(i); // 输出 0,1,2,3,4
}

3.3 比较运算符

用于比较两个值的关系,返回结果为布尔值(truefalse),常用于分支判断(if)和循环条件(for/while)。

运算符 名称 功能 示例 结果
< 小于 左值 < 右值? 10 < 20 true
> 大于 左值 > 右值? 10 > 20 false
<= 小于等于 左值 ≤ 右值? 10 <= 10 true
>= 大于等于 左值 ≥ 右值? 10 >= 20 false
== 相等(松散比较) 左值 == 右值?(忽略类型,先隐式转换) 10 == "10" true
!= 不相等(松散比较) 左值 != 右值?(忽略类型) 10 != "10" false
=== 全等(严格比较) 左值 === 右值?(要求值和类型均一致) 10 === "10" false
!== 不全等(严格比较) 左值 !== 右值?(值或类型不一致) 10 !== "10" true

关键注意:===== 的区别(重点)

  • ==(松散比较):会先将两边的值转为同一类型(隐式转换),再比较值是否相等;
  • ===(严格比较):不进行类型转换,直接比较值和类型是否均一致(推荐使用,避免隐式转换导致的意外结果)。

示例:== 的隐式转换问题

console.log(0 == ""); // true(0 转为 false,"" 转为 false,false == false)
console.log(null == undefined); // true(特殊规则)
console.log(1 == true); // true(1 转为 true)

// 严格比较可避免这些问题
console.log(0 === ""); // false(类型不同)
console.log(null === undefined); // false(类型不同)
console.log(1 === true); // false(类型不同)

3.4 逻辑运算符

用于组合多个布尔值条件,返回结果仍为布尔值,主要有“与”“或”“非”三种,常用于复杂条件判断。

运算符 名称 语法 逻辑规则 示例 结果
&& 逻辑与(AND) 条件1 && 条件2 两边均为 true,结果才为 true(一假则假) true && false false
` ` 逻辑或(OR) `条件1
! 逻辑非(NOT) !条件 取反(truefalsefalsetrue !true false

示例:组合条件判断

let age = 18;
let hasId = true;

// 逻辑与:同时满足“年龄≥18”和“有身份证”,才能进入
let canEnter = age >= 18 && hasId;
console.log(canEnter); // true

// 逻辑或:满足“年龄≥60”或“有老年证”,即可享受优惠
let hasDiscount = age >= 60 || hasId === false;
console.log(hasDiscount); // false

// 逻辑非:取反“能进入”的结果
let cannotEnter = !canEnter;
console.log(cannotEnter); // false

短路运算(逻辑中断)

逻辑运算符具有“短路特性”:若左边的条件已能确定最终结果,右边的条件将不再执行(提升性能)。

  1. 逻辑与(&&)的短路规则
  • 条件1false,则无需判断 条件2,直接返回 条件1 的值;
  • 条件1true,则返回 条件2 的值(需判断 条件2)。
// 条件1 为 false,短路(不执行条件2)
console.log(false && console.log("条件2")); // 输出 false(条件2 未执行)

// 条件1 为 true,执行条件2 并返回其值
console.log(true && "条件2结果"); // 输出 "条件2结果"
  1. 逻辑或(||)的短路规则
  • 条件1true,则无需判断 条件2,直接返回 条件1 的值;
  • 条件1false,则返回 条件2 的值(需判断 条件2)。
// 若 userName 未赋值(undefined),则使用默认值 "Guest"
let userName = undefined;
let displayName = userName || "Guest";
console.log(displayName); // 输出 "Guest"

// 若 userName 已赋值,则使用该值
userName = "Alice";
displayName = userName || "Guest";
console.log(displayName); // 输出 "Alice"

3.5 赋值运算符

用于将值赋给变量,或在赋值的同时执行算术运算(复合赋值)。

运算符 名称 语法 等价于 示例 结果
= 简单赋值 num = 10 - let num; num = 10 num = 10
+= 加后赋值 num += 5 num = num + 5 let num = 10; num += 5 num = 15
-= 减后赋值 num -= 5 num = num - 5 let num = 10; num -= 5 num = 5
*= 乘后赋值 num *= 5 num = num * 5 let num = 10; num *= 5 num = 50
/= 除后赋值 num /= 5 num = num / 5 let num = 10; num /= 5 num = 2
%= 取余后赋值 num %= 5 num = num % 5 let num = 10; num %= 3 num = 1

示例:复合赋值的应用

let score = 80;
score += 5; // 等价于 score = score + 5 → 85
score *= 0.8; // 等价于 score = score * 0.8 → 68
console.log(score); // 输出 68

3.6 运算符优先级

当多个运算符同时出现时,遵循“优先级高的先执行”的规则,若优先级相同则从左到右执行。以下是常用运算符的优先级(从高到低):

优先级 运算符类型 具体运算符 说明
1 括号 () 改变运算顺序,优先级最高
2 一元运算符 ++--! 自增/自减、逻辑非
3 算术运算符 */%+- 先乘除取余,后加减
4 关系运算符 >>=<<===!====!== 先比较大小,后判断相等
5 逻辑运算符 && → `
6 赋值运算符 =+=-= 优先级最低
7 逗号运算符 , 分隔多个表达式,优先级最低

示例:复杂表达式的执行顺序

// 计算:(3 + 2) * 4 > 10 && !false
let result = (3 + 2) * 4 > 10 && !false;

// 执行步骤:
// 1. 括号:3 + 2 = 5
// 2. 算术运算:5 * 4 = 20
// 3. 关系运算:20 > 10 = true
// 4. 一元运算符:!false = true
// 5. 逻辑与:true && true = true

console.log(result); // 输出 true

口诀:“括号优先,一元其次,算术关系,逻辑赋值,逗号最后”——不确定时,直接用括号明确顺序(可读性优先)。

4. 流程控制

默认情况下,JavaScript 代码按“从上到下、从左到右”的顺序执行(顺序结构)。但实际开发中,需根据条件执行不同代码(分支结构)或重复执行某段代码(循环结构),这就是流程控制。

4.1 流程控制的三种结构

流程控制三种结构

  1. 顺序结构:代码默认执行方式,从上到下依次执行(如变量声明、赋值);
  2. 分支结构:根据条件判断,选择执行不同代码块(如 if、switch);
  3. 循环结构:根据条件,重复执行某段代码块(如 for、while)。

4.2 分支结构(条件判断)

分支结构用于“根据不同情况做不同事情”,JavaScript 提供两种分支语句:if 语句和 switch 语句。

4.2.1 if 语句

if 语句根据条件表达式的布尔值(true/false)决定是否执行代码块,分为“单分支”“双分支”“多分支”三种形式。

  1. 单分支 if 语句(满足条件才执行)
if (条件表达式) {
    // 条件为 true 时执行的代码块(若只有一行代码,可省略大括号,但不推荐)
}

// 判断年龄是否成年
let age = 18;
if (age >= 18) {
    console.log("您已成年,可独立办理业务!");
}
  1. 双分支 if-else 语句(二选一)
if (条件表达式) {
    // 条件为 true 时执行的代码块
} else {
    // 条件为 false 时执行的代码块
}

// 判断成绩是否及格
let score = 75;
if (score >= 60) {
    console.log("成绩及格,继续加油!");
} else {
    console.log("成绩不及格,需要补考!");
}
  1. 多分支 if-else if 语句(多选一)
if (条件表达式1) {
    // 条件1 为 true 时执行
} else if (条件表达式2) {
    // 条件1 为 false,条件2 为 true 时执行
} else if (条件表达式3) {
    // 条件1、2 为 false,条件3 为 true 时执行
} else {
    // 所有条件均为 false 时执行(可选)
}

// 根据成绩划分等级
let score = 85;
if (score >= 90) {
    console.log("等级:A(优秀)");
} else if (score >= 80) {
    console.log("等级:B(良好)");
} else if (score >= 60) {
    console.log("等级:C(及格)");
} else {
    console.log("等级:D(不及格)");
}

注意:多分支语句中,只要有一个条件满足,后续条件将不再判断(避免冗余),因此需合理安排条件顺序(如先判断范围小的条件)。

4.2.2 三元表达式

三元表达式是 if-else 双分支语句的简写形式,语法简洁,适用于简单的二选一场景。

条件表达式 ? 表达式1(条件为 true 时执行) : 表达式2(条件为 false 时执行);

// 判断奇偶性
let num = 10;
let result = num % 2 === 0 ? "偶数" : "奇数";
console.log(result); // 输出 "偶数"

应用场景:变量赋值、简单逻辑判断(不推荐用于复杂代码块,可读性差)。

4.2.3 switch 语句

switch 语句根据“表达式的值”与“case 后的值”是否全等(===),选择执行对应的代码块,适用于“固定值匹配”场景(如根据状态码、类型值判断)。

switch (表达式) {
    case1:
        // 表达式 === 值1 时执行的代码
        break; // 跳出 switch 语句(避免继续执行后续 case)
    case2:
        // 表达式 === 值2 时执行的代码
        break;
    case3:
        // 表达式 === 值3 时执行的代码
        break;
    default:
        // 表达式与所有 case 值均不全等时执行的代码(可选)
        break;
}

// 根据星期几显示对应活动
let week = 3; // 1=周一,2=周二,...,7=周日

switch (week) {
    case 1:
        console.log("周一:开会");
        break;
    case 2:
        console.log("周二:培训");
        break;
    case 3:
        console.log("周三:项目开发");
        break;
    case 4:
        console.log("周四:测试");
        break;
    case 5:
        console.log("周五:总结");
        break;
    case 6:
    case 7: // 多个 case 共享同一代码块(周六和周日均休息)
        console.log("周末:休息");
        break;
    default:
        console.log("无效的星期数");
        break;
}
  1. break 的作用:执行完某个 case 后,必须用 break 跳出 switch 语句,否则会继续执行后续 case(“穿透现象”);
  2. 全等比较switch 比较的是“全等(===)”,需确保表达式和 case 值的类型一致;
  3. default 的位置default 可放在任意位置(通常在最后),若放在前面,需加 break,否则会继续执行后续 case。

4.2.4 if 与 switch 的区别

维度 if 语句 switch 语句
适用场景 范围判断(如 score >= 60)、复杂条件组合 固定值匹配(如 week === 3status === "success"
比较方式 支持各种比较运算符(>, <, === 等) 仅支持全等(===
可读性 分支少(1-3个)时清晰,分支多(>3个)时冗余 分支多(>3个)时结构清晰,分支少(<3个)时冗余
执行效率 分支越多,判断次数越多 直接匹配 case 值,效率较高(部分引擎优化)

选择建议

  • 范围判断、复杂条件 → 用 if 语句;
  • 固定值匹配(如状态码、类型、枚举值) → 用 switch 语句。

4.3 循环结构(重复执行)

循环结构用于“重复执行某段代码,直到满足退出条件”,JavaScript 提供四种循环语句:forfor...inwhiledo...while,其中 forwhile 最常用。

4.3.1 for 循环

for 循环是最常用的循环语句,适用于“已知循环次数”的场景(如遍历数组、重复执行 N 次)。

for (初始化变量; 条件表达式; 操作表达式) {
    // 循环体:条件表达式为 true 时执行的代码块
}
部分 作用 示例
初始化变量 声明循环计数器(仅执行一次) let i = 0
条件表达式 判断是否继续循环(每次循环前执行) i < 5
操作表达式 更新循环计数器(每次循环体执行后执行) i++
  1. 执行“初始化变量”(仅一次);
  2. 执行“条件表达式”:
    • 若为 true → 执行循环体 → 执行“操作表达式” → 回到步骤 2;
    • 若为 false → 退出循环,执行后续代码。

示例 1:打印 1-5 的数字

// 循环 5 次(i 从 0 到 4)
for (let i = 0; i < 5; i++) {
    console.log(i + 1); // 输出 1,2,3,4,5
}

示例 2:计算 1-100 的总和

let sum = 0;
// i 从 1 到 100,累加每个 i 到 sum
for (let i = 1; i <= 100; i++) {
    sum += i; // 等价于 sum = sum + i
}
console.log("1-100 的总和:", sum); // 输出 5050

4.3.2 双重 for 循环

双重 for 循环是“在一个 for 循环内部再嵌套一个 for 循环”,外层循环执行一次,内层循环执行全部次数,适用于“二维数据遍历”(如矩阵、表格)。

for (外层初始化; 外层条件; 外层操作) {
    // 外层循环体
    for (内层初始化; 内层条件; 内层操作) {
        // 内层循环体(外层执行一次,内层执行全部次数)
    }
}

示例:打印 5 行 5 列的星号(*)矩阵

// 外层循环:控制行数(5 行)
for (let row = 0; row < 5; row++) {
    let line = ""; // 存储当前行的星号
    // 内层循环:控制每行的列数(5 列)
    for (let col = 0; col < 5; col++) {
        line += "* "; // 每行添加一个星号和空格
    }
    console.log(line); // 打印当前行
}

// 输出结果:
// * * * * * 
// * * * * * 
// * * * * * 
// * * * * * 
// * * * * * 
  1. 变量命名:外层和内层的循环变量需区分(如 rowcol),避免变量污染;
  2. 执行顺序:外层循环执行一次,内层循环执行完所有次数(如上述示例中,外层 row=0 时,内层 col 从 0 到 4 执行 5 次);
  3. 性能优化:避免不必要的循环(如内层循环的条件尽量与外层无关,减少计算量)。

4.3.3 while 循环

while 循环适用于“未知循环次数”的场景(如等待用户输入正确值、处理动态数据),仅需指定“循环条件”,不直接指定次数。

while (条件表达式) {
    // 循环体:条件为 true 时执行的代码块
    // (必须包含更新条件的代码,否则会导致死循环)
}
  1. 执行“条件表达式”:
    • 若为 true → 执行循环体 → 回到步骤 1;
    • 若为 false → 退出循环。

示例:等待用户输入正确的年龄(1-120)

let age;
// 循环条件:age 未定义 或 不在 1-120 范围内
while (!age || age < 1 || age > 120) {
    // 接收用户输入(转为数字)
    age = parseInt(prompt("请输入您的年龄(1-120):"));
    // 判断输入是否有效
    if (!age || age < 1 || age > 120) {
        alert("输入无效,请重新输入!");
    }
}
console.log("您的年龄是:", age);

关键注意:避免死循环

while 循环必须在循环体内更新“影响条件表达式的变量”(如上述示例中的 age),否则条件将永远为 true,导致死循环(浏览器会卡顿甚至崩溃)。

  • 死循环示例(错误)
let i = 0;
while (i < 5) {
    console.log(i); // 未更新 i,i 永远为 0,条件永远为 true
}

4.3.4 do…while 循环

do...while 循环与 while 循环类似,但先执行一次循环体,再判断条件,适用于“循环体至少需要执行一次”的场景(如菜单选择、表单提交验证)。

do {
    // 循环体:至少执行一次的代码块
    // (必须包含更新条件的代码)
} while (条件表达式); // 注意末尾的分号
  1. 执行“循环体”(至少一次);
  2. 执行“条件表达式”:
    • 若为 true → 回到步骤 1,继续执行循环体;
    • 若为 false → 退出循环。

示例:菜单选择(至少显示一次菜单)

let choice;
do {
    // 显示菜单(循环体至少执行一次)
    choice = prompt(`请选择操作:
1. 查看信息
2. 修改信息
3. 退出
请输入选项(1-3):`);
    // 根据选择执行操作
    switch (choice) {
        case "1":
            alert("查看信息...");
            break;
        case "2":
            alert("修改信息...");
            break;
        case "3":
            alert("退出程序!");
            break;
        default:
            alert("选项无效,请重新选择!");
            break;
    }
// 循环条件:choice 不是 "3"(未选择退出)
} while (choice !== "3");

与 while 循环的区别

循环类型 执行顺序 适用场景
while 先判断条件,再执行循环体 可能一次都不执行(条件初始为 false)
do…while 先执行循环体,再判断条件 至少执行一次(如菜单、表单验证)

4.3.5 循环控制:continue 与 break

continuebreak 用于控制循环的执行流程,可在循环体内根据条件提前跳过或终止循环。

  1. continue(跳过本次循环)

continue 用于“立即跳出当前次循环”,直接进入下一次循环的条件判断(后续循环体代码不再执行)。

示例:打印 1-10 中的奇数

for (let i = 1; i <= 10; i++) {
    // 若 i 为偶数,跳过本次循环(不执行 console.log)
    if (i % 2 === 0) {
        continue;
    }
    console.log(i); // 输出 1,3,5,7,9
}
  1. break(终止整个循环)

break 用于“立即终止整个循环”,直接跳出循环体,执行循环后的代码(后续所有循环均不再执行)。

示例:查找数组中的第一个偶数

let arr = [1, 3, 5, 4, 7, 8];
let firstEven;

for (let i = 0; i < arr.length; i++) {
    if (arr[i] % 2 === 0) {
        firstEven = arr[i];
        break; // 找到后立即终止循环(无需继续查找)
    }
}

console.log("第一个偶数:", firstEven); // 输出 4

注意:在嵌套循环中的作用范围

continuebreak 仅作用于“当前所在的循环”(内层循环中的 break 不会终止外层循环):

// 外层循环(3 次)
for (let outer = 1; outer <= 3; outer++) {
    console.log("外层循环:", outer);
    // 内层循环(3 次)
    for (let inner = 1; inner <= 3; inner++) {
        if (inner === 2) {
            break; // 仅终止内层循环,外层循环继续
        }
        console.log("  内层循环:", inner);
    }
}

// 输出结果:
// 外层循环:1
//   内层循环:1
// 外层循环:2
//   内层循环:1
// 外层循环:3
//   内层循环:1

JavaScript 是前端开发的基石,也是全栈开发的重要工具,掌握基础后持续实践,才能在编程之路上走得更远。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐