编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • C语言入门精通

  • Cpp入门到精通

  • Java入门精通

  • Go入门到精通

  • JavaScript入门

    • 基础入门

      • README
      • 入门介绍
        • 1.1 导论前沿
          • 1.1.1 JavaScript语言
          • 1.1.2 核心语法
          • 1.1.3 JavaScript特点
        • 1.2 为何学JavaScript
          • 1.2.1 操控浏览器能力
          • 1.2.2 广泛的使用领域
          • 1.2.3 易学性
          • 1.2.4 强大的性能
          • 1.2.5 开放性
          • 1.2.7 实验环境
          • 1.2.8 JavaScript发展简史与ECMAScript标准演进
        • 1.3 基本语法
          • 1.3.1 语句
          • 1.3.2 字面量和变量
          • 1.3.3 变量提升
          • 1.3.4 标识符
          • 1.3.5 注释
          • 1.3.6 区块
          • 1.3.7 var、let、const 的深度对比
        • 1.4 条件语句
          • 1.4.1 if 结构
          • 1.4.2 if...else结构
          • 1.4.3 switch结构
          • 1.4.4 三元运算符 ?:
          • 1.4.5 条件语句的设计原理与性能
        • 1.5 循环语句
          • 1.5.1 while循环
          • 1.5.2 for循环
          • 1.5.3 do...while循环
          • 1.5.4 break和continue
          • 1.5.5 标签(label)
          • 1.5.6 ES6+的循环增强
        • 1.6 console
          • 1.6.1 console.log()
          • 1.6.2 console.info()
          • 1.6.3 console.warn()
          • 1.6.4 console.error()
          • 1.6.5 console.debug()
          • 1.6.6 console.table()
          • 1.6.7 console.time()
          • 1.6.8 console.assert()
          • 1.6.9 console.trace()
          • 1.6.10 格式化输出
          • 1.6.11 console高级用法与调试技巧
      • 数据类型
      • 运算符
      • 函数
      • 面向对象
      • 标准库
      • 异步操作
      • 事件设计
      • 错误机制
      • 模块开发
      • 字符串处理
      • 迭代器与生成器
      • Symbol
      • DOM操作
      • 网络请求
    • 综合案例

    • 专栏博客

  • CodeX
  • JavaScript入门
  • 基础入门
杨充
2025-08-25
目录

入门介绍

# 01.入门介绍

# 目录介绍

  • 1.1 导论前沿
    • 1.1.1 JavaScript语言
    • 1.1.2 核心语法
    • 1.1.3 JavaScript特点
  • 1.2 为何学JavaScript
    • 1.2.1 操控浏览器能力
    • 1.2.2 广泛的使用领域
    • 1.2.3 易学性
    • 1.2.4 强大的性能
    • 1.2.5 开放性
    • 1.2.6 实验环境
  • 1.3 基本语法
    • 1.3.1 语句
    • 1.3.2 字面量和变量
    • 1.3.3 变量提升
    • 1.3.4 标识符
    • 1.3.5 注释
    • 1.3.6 区块
  • 1.4 条件语句
    • 1.4.1 if结构
    • 1.4.2 if...else结构
    • 1.4.3 switch结构
    • 1.4.4 三元运算符 ?:
  • 1.5 循环语句
    • 1.5.1 while循环
    • 1.5.2 for循环
    • 1.5.3 do...while循环
    • 1.5.4 break和continue
    • 1.5.5 标签(label)
  • 1.6 console
    • 1.6.1 console.log()
    • 1.6.2 console.info()
    • 1.6.3 console.warn()
    • 1.6.4 console.error()
    • 1.6.5 console.debug()
    • 1.6.6 console.table()
    • 1.6.7 console.time()
    • 1.6.8 console.assert()
    • 1.6.9 console.trace()
    • 1.6.10 格式化输出

# 1.1 导论前沿

# 1.1.1 JavaScript语言

JavaScript 是一种轻量级的脚本语言。所谓“脚本语言”(script language),指的是它不具备开发操作系统的能力,而是只用来编写控制其他大型应用程序(比如浏览器)的“脚本”。

JavaScript 也是一种嵌入式(embedded)语言。它本身提供的核心语法不算很多,只能用来做一些数学和逻辑运算。

JavaScript 引擎的底层原理:现代 JavaScript 引擎(如 V8、SpiderMonkey)采用即时编译(JIT, Just-In-Time Compilation) 技术。代码首先被解析为抽象语法树(AST),然后经过基线编译器快速生成字节码执行;对于"热点代码"(频繁执行的代码),优化编译器会将其编译为高度优化的机器码。V8 引擎的执行流程为:源码 → Parser → AST → Ignition(字节码解释器)→ TurboFan(优化编译器)→ 机器码。如果优化假设失败(如变量类型突然改变),引擎会进行去优化(Deoptimization),回退到字节码执行。

JavaScript 本身不提供任何与 I/O(输入/输出)相关的 API,都要靠宿主环境提供,所以 JavaScript 只合适嵌入更大型的应用程序环境,去调用宿主环境提供的底层 API。

从语法角度看,JavaScript 语言是一种“对象模型”语言。各种宿主环境通过这个模型,描述自己的功能和操作接口,从而通过 JavaScript 控制这些功能。但是,JavaScript 并不是纯粹的“面向对象语言”,还支持其他编程范式(比如函数式编程)。这导致几乎任何一个问题,JavaScript 都有多种解决方法。阅读本书的过程中,你会诧异于 JavaScript 语法的灵活性。

# 1.1.2 核心语法

JavaScript 的核心语法部分相当精简,只包括两个部分:基本的语法构造(比如操作符、控制结构、语句)和标准库(就是一系列具有各种功能的对象比如Array、Date、Math等)。除此之外,各种宿主环境提供额外的 API(即只能在该环境使用的接口),以便 JavaScript 调用。以浏览器为例,它提供的额外 API 可以分成三大类。

  • 浏览器控制类:操作浏览器
  • DOM 类:操作网页的各种元素
  • Web 类:实现互联网的各种功能

# 1.1.3 JavaScript特点

JavaScript 程序设计语言有如下几个特点:

  • ● 解释型语言:解释型语言直接在运行环境中执行代码,所以一般来说,与编译型语言相比,解释型语言的开发更为容易。
  • ● 类似于 C 和 Java 的语法结构:JavaScript 的语法结构与 C 和 Java 相似。
  • ● 动态语言:态语言的变量和函数是不指定返回值类型的。JavaScript 之所以被设计成动态语言,和选择将其设计为解释型语言的理由一样,都是优先考虑了开发难易度的结果。
  • ● 基于原型的面向对象:目前,被称为面向对象语言的程序设计语言,大多提供了基于类的面向对象语言功能。
  • ● 字面量的表现能力:字面量的表现能力是 JavaScript 开发生产力得以提高的一个重要原因。极大地简化了代码的编写,提升了开发效率和代码的可读性。
  • ● 函数式编程:

# 1.2 为何学JavaScript

JavaScript 语言有一些显著特点,使得它非常值得学习。它既适合作为学习编程的入门语言,也适合当作日常开发的工作语言。它是目前最有希望、前途最光明的计算机语言之一。

# 1.2.1 操控浏览器能力

JavaScript 的发明目的,就是作为浏览器的内置脚本语言,为网页开发者提供操控浏览器的能力。它是目前唯一一种通用的浏览器脚本语言,所有浏览器都支持。它可以让网页呈现各种特殊效果,为用户提供良好的互动体验。

对于一个互联网开发者来说,如果你想提供漂亮的网页、令用户满意的上网体验、各种基于浏览器的便捷功能、前后端之间紧密高效的联系,JavaScript 是必不可少的工具。

# 1.2.2 广泛的使用领域

近年来,JavaScript 的使用范围,慢慢超越了浏览器,正在向通用的系统语言发展。

(1)浏览器的平台化

随着 HTML5 的出现,浏览器本身的功能越来越强,不再仅仅能浏览网页,而是越来越像一个平台,JavaScript 因此得以调用许多系统功能,比如操作本地文件、操作图片、调用摄像头和麦克风等等。这使得 JavaScript 可以完成许多以前无法想象的事情。

(2)Node

Node 项目使得 JavaScript 可以用于开发服务器端的大型项目,网站的前后端都用 JavaScript 开发已经成为了现实。有些嵌入式平台(Raspberry Pi)能够安装 Node,于是 JavaScript 就能为这些平台开发应用程序。

(3)数据库操作

JavaScript 甚至也可以用来操作数据库。NoSQL 数据库这个概念,本身就是在 JSON(JavaScript Object Notation)格式的基础上诞生的,大部分 NoSQL 数据库允许 JavaScript 直接操作。基于 SQL 语言的开源数据库 PostgreSQL 支持 JavaScript 作为操作语言,可以部分取代 SQL 查询语言。

(4)移动平台开发

JavaScript 也正在成为手机应用的开发语言。一般来说,安卓平台使用 Java 语言开发,iOS 平台使用 Objective-C 或 Swift 语言开发。许多人正在努力,让 JavaScript 成为各个平台的通用开发语言。

PhoneGap 项目就是将 JavaScript 和 HTML5 打包在一个容器之中,使得它能同时在 iOS 和安卓上运行。Facebook 公司的 React Native 项目则是将 JavaScript 写的组件,编译成原生组件,从而使它们具备优秀的性能。

(5)内嵌脚本语言

越来越多的应用程序,将 JavaScript 作为内嵌的脚本语言,比如 Adobe 公司的著名 PDF 阅读器 Acrobat、Linux 桌面环境 GNOME 3。

(6)跨平台的桌面应用程序

Chromium OS、Windows 8 等操作系统直接支持 JavaScript 编写应用程序。Mozilla 的 Open Web Apps 项目、Google 的 Chrome App 项目 (opens new window)、GitHub 的 Electron 项目 (opens new window)、以及 TideSDK 项目 (opens new window),都可以用来编写运行于 Windows、Mac OS 和 Android 等多个桌面平台的程序,不依赖浏览器。

“所有可以用 JavaScript 编写的程序,最终都会出现 JavaScript 的版本。”(Any application that can be written in JavaScript will eventually be written in JavaScript.)

# 1.2.3 易学性

相比学习其他语言,学习 JavaScript 有一些有利条件。

(1)学习环境无处不在

只要有浏览器,就能运行 JavaScript 程序;只要有文本编辑器,就能编写 JavaScript 程序。这意味着,几乎所有电脑都原生提供 JavaScript 学习环境,不用另行安装复杂的 IDE(集成开发环境)和编译器。

(2)简单性

相比其他脚本语言(比如 Python 或 Ruby),JavaScript 的语法相对简单一些,本身的语法特性并不是特别多。而且,那些语法中的复杂部分,也不是必需要学会。你完全可以只用简单命令,完成大部分的操作。

(3)与主流语言的相似性

JavaScript 的语法很类似 C/C++ 和 Java,如果学过这些语言(事实上大多数学校都教),JavaScript 的入门会非常容易。

必须说明的是,虽然核心语法不难,但是 JavaScript 的复杂性体现在另外两个方面。

首先,它涉及大量的外部 API。JavaScript 要发挥作用,必须与其他组件配合,这些外部组件五花八门,数量极其庞大,几乎涉及网络应用的各个方面,掌握它们绝非易事。

其次,JavaScript 语言有一些设计缺陷。某些地方相当不合理,另一些地方则会出现怪异的运行结果。学习 JavaScript,很大一部分时间是用来搞清楚哪些地方有陷阱。另外一些程序员则感到,为了更合理地编写 JavaScript 程序,就不能用 JavaScript 来写,而必须发明新的语言,比如 CoffeeScript、TypeScript、Dart 这些新语言的发明目的,多多少少都有这个因素。

尽管如此,目前看来,JavaScript 的地位还是无法动摇。加之,语言标准的快速进化,使得 JavaScript 功能日益增强,而语法缺陷和怪异之处得到了弥补。所以,JavaScript 还是值得学习,况且它的入门真的不难。

# 1.2.4 强大的性能

JavaScript 的性能优势体现在以下方面。

(1)灵活的语法,表达力强。

JavaScript 既支持类似 C 语言清晰的过程式编程,也支持灵活的函数式编程,可以用来写并发处理(concurrent)。这些语法特性已经被证明非常强大,可以用于许多场合,尤其适用异步编程。

JavaScript 的所有值都是对象,这为程序员提供了灵活性和便利性。因为你可以很方便地、按照需要随时创造数据结构,不用进行麻烦的预定义。

(2)支持编译运行。

JavaScript 语言本身,虽然是一种解释型语言,但是在现代浏览器中,JavaScript 都是编译后运行。程序会被高度优化,运行效率接近二进制程序。而且,JavaScript 引擎正在快速发展,性能将越来越好。

此外,还有一种 WebAssembly 格式,它是 JavaScript 引擎的中间码格式,全部都是二进制代码。由于跳过了编译步骤,可以达到接近原生二进制代码的运行速度。各种语言(主要是 C 和 C++)通过编译成 WebAssembly,就可以在浏览器里面运行。

(3)事件驱动和非阻塞式设计。

JavaScript 程序可以采用事件驱动(event-driven)和非阻塞式(non-blocking)设计,在服务器端适合高并发环境,普通的硬件就可以承受很大的访问量。

# 1.2.5 开放性

JavaScript 是一种开放的语言。它的标准 ECMA-262 是 ISO 国际标准,写得非常详尽明确;该标准的主要实现(比如 V8 和 SpiderMonkey 引擎)都是开放的,而且质量很高。这保证了这门语言不属于任何公司或个人,不存在版权和专利的问题。

语言标准由 TC39 委员会负责制定,该委员会的运作是透明的,所有讨论都是开放的,会议记录都会对外公布。

# 1.2.7 实验环境

推荐安装 Chrome 浏览器,它的“开发者工具”(Developer Tools)里面的“控制台”(console),就是运行 JavaScript 代码的理想环境。

进入 Chrome 浏览器的“控制台”,有两种方法。

  • 直接进入:按下Option + Command + J(Mac)或者Ctrl + Shift + J(Windows / Linux)
  • 开发者工具进入:开发者工具的快捷键是 F12,或者Option + Command + I(Mac)以及Ctrl + Shift + I(Windows / Linux),然后选择 Console 面板

进入控制台以后,就可以在提示符后输入代码,然后按Enter键,代码就会执行。如果按Shift + Enter键,就是代码换行,不会触发执行。建议阅读本教程时,将代码复制到控制台进行实验。

作为尝试,你可以将下面的程序复制到“控制台”,按下回车后,就可以看到运行结果。

function greetMe(yourName) {
  console.log('Hello ' + yourName);
}

greetMe('World')
// Hello World
1
2
3
4
5
6

# 1.2.8 JavaScript发展简史与ECMAScript标准演进

了解 JavaScript 的发展历程有助于理解语言设计的很多"历史遗留"问题:

年份 事件
1995 Brendan Eich 用10天设计出 Mocha(后改名 LiveScript → JavaScript)
1997 ECMAScript 1 标准发布(ECMA-262)
1999 ECMAScript 3 发布(正则、try/catch、更好的字符串处理)
2009 ECMAScript 5 发布(严格模式、JSON、Array 方法)
2009 Node.js 发布,JavaScript 进入服务器端
2015 ECMAScript 6(ES2015)发布(let/const、箭头函数、Promise、class、模块)
2016+ 每年发布一个新版本(ES2016、ES2017...),采用 Stage 提案流程
2017 async/await 进入标准(ES2017)
2020 可选链 ?.、空值合并 ?? 进入标准(ES2020)
2022 私有字段 #、Top-level await 进入标准(ES2022)

疑惑:为什么 JavaScript 有那么多"怪异"的行为(如 typeof null === "object"、0.1 + 0.2 !== 0.3)?

答疑:这些大多是1995年最初10天设计中遗留的问题。typeof null 的 bug 源于第一版实现中用低位标记类型——对象的标记是 000,而 null 在内部表示为空指针(全0),所以被误判为对象。浮点数问题则是 IEEE 754 双精度浮点标准的固有限制,并非 JavaScript 独有。

结论:理解历史能帮助你避免陷阱,也能理解为什么现代 JavaScript(ES6+)引入了大量新特性来修补早期设计的不足。


# 1.3 基本语法

# 1.3.1 语句

JavaScript 程序的执行单位为行(line),也就是一行一行地执行。一般情况下,每一行就是一个语句。

var a = 1 + 3;
1

这条语句先用var命令,声明了变量a,然后将1 + 3的运算结果赋值给变量a。

1 + 3叫做表达式(expression),指一个为了得到返回值的计算式。语句和表达式的区别在于,前者主要为了进行某种操作,一般情况下不需要返回值;后者则是为了得到返回值,一定会返回一个值。凡是 JavaScript 语言中预期为值的地方,都可以使用表达式。比如,赋值语句的等号右边,预期是一个值,因此可以放置各种表达式。

语句以分号结尾,一个分号就表示一个语句结束。多个语句可以写在一行内。

var a = 1 + 3 ; var b = 'abc';
1

# 1.3.2 字面量和变量

字面量允许开发者直接定义数据结构,而无需调用构造函数或复杂的初始化逻辑。

// 对象字面量:
const user = { name: "Alice", age: 25 };
// 数组字面量:
const numbers = [1, 2, 3, 4];
// 字符串字面量:
const message = "Hello, World!";
const greeting = `Hello, ${name}!`;
// 其他等
1
2
3
4
5
6
7
8

变量:变量是对“值”的具名引用。变量就是为“值”起名,然后引用这个名字,就等同于引用这个值。变量的名字就是变量名。

变量的声明: 使用var关键字声明一个变量。

var a;
1

变量的赋值: 使用=为变量赋值。

a = 123;
1

声明和赋值同时进行:

var a = 123;
1

# 1.3.3 变量提升

JavaScript 引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。这造成的结果,就是所有的变量的声明语句,都会被提升到代码的头部,这就叫做变量提升(hoisting)。

变量提升的底层原理:JavaScript 引擎在执行代码前会经历两个阶段——编译阶段和执行阶段。在编译阶段,引擎会创建执行上下文(Execution Context),其中包含变量环境(Variable Environment)。引擎扫描代码中所有的 var 声明和函数声明,将它们注册到变量环境中(var 初始化为 undefined,函数声明则直接存储函数体)。这就是"提升"的本质——并非代码被物理移动,而是在执行前就已经在内存中分配了空间。let 和 const 也会被提升,但在声明语句执行前处于暂时性死区(TDZ, Temporal Dead Zone),访问会抛出 ReferenceError。

console.log(a);
var a = 1;
1
2

上面代码首先使用console.log方法,在控制台(console)显示变量a的值。这时变量a还没有声明和赋值,所以这是一种错误的做法,但是实际上不会报错。因为存在变量提升,真正运行的是下面的代码。

var a;
console.log(a);
a = 1;
1
2
3

最后的结果是显示undefined,表示变量a已声明,但还未赋值。

# 1.3.4 标识符

标识符(identifier)指的是用来识别各种值的合法名称。最常见的标识符就是变量名,以及后面要提到的函数名。JavaScript 语言的标识符对大小写敏感,所以a和A是两个不同的标识符。

所谓标识符,就是指给变量、函数、属性或函数的参数起名字。标识符可以是按照下列格式规则组合起来的一或多个字符:

  1. 第一个字符必须是一个字母、下划线( _ )或一个美元符号( $ )。
  2. 其它字符可以是字母、下划线、美元符号或数字。
  3. 按照惯例,ECMAScript 标识符采用驼峰命名法。
  4. 标识符不能是关键字和保留字符。

下面这些都是合法的标识符。

arg0
_tmp
$elem
π
1
2
3
4

下面这些则是不合法的标识符。

1a  // 第一个字符不能是数字
23  // 同上
***  // 标识符不能包含星号
a+b  // 标识符不能包含加号
-d  // 标识符不能包含减号或连词线
1
2
3
4
5

JavaScript 有一些保留字,不能用作标识符:arguments、break、case、catch、class、const、continue、debugger、default、delete、do、else、enum、eval、export、extends、false、finally、for、function、if、implements、import、in、instanceof、interface、let、new、null、package、private、protected、public、return、static、super、switch、this、throw、true、try、typeof、var、void、while、with、yield。

# 1.3.5 注释

源码中被 JavaScript 引擎忽略的部分就叫做注释,它的作用是对代码进行解释。JavaScript 提供两种注释的写法:一种是单行注释,用//起头;另一种是多行注释,放在/*和*/之间。

// 这是单行注释

/*
 这是
 多行
 注释
*/
1
2
3
4
5
6
7

需要注意的是,-->只有在行首,才会被当成单行注释,否则会当作正常的运算。

function countdown(n) {
  while (n --> 0) console.log(n);
}
countdown(3)
// 2
// 1
// 0
1
2
3
4
5
6
7

上面代码中,n --> 0实际上会当作n-- > 0,因此输出2、1、0。

# 1.3.6 区块

JavaScript 使用大括号,将多个相关的语句组合在一起,称为“区块”(block)。

对于var命令来说,JavaScript 的区块不构成单独的作用域(scope)。

{
  var a = 1;
}

a // 1
1
2
3
4
5

上面代码在区块内部,使用var命令声明并赋值了变量a,然后在区块外部,变量a依然有效,区块对于var命令不构成单独的作用域,与不使用区块的情况没有任何区别。在 JavaScript 语言中,单独使用区块并不常见,区块往往用来构成其他更复杂的语法结构,比如for、if、while、function等。

# 1.3.7 var、let、const 的深度对比

ES6 引入 let 和 const 是为了解决 var 的设计缺陷。三者的核心区别:

// 1. 作用域差异
if (true) {
    var a = 1;    // 函数作用域(泄漏到外部)
    let b = 2;    // 块级作用域
    const c = 3;  // 块级作用域
}
console.log(a);   // 1(var泄漏)
// console.log(b); // ReferenceError
// console.log(c); // ReferenceError

// 2. 变量提升 vs 暂时性死区(TDZ)
console.log(x);   // undefined(var提升,初始化为undefined)
var x = 1;

// console.log(y); // ReferenceError(let提升但进入TDZ)
let y = 2;

// 3. 重复声明
var d = 1;
var d = 2;   // OK
// let d = 3; // SyntaxError: 已声明

// 4. const的"不可变"是引用不可变,不是值不可变
const obj = { name: 'Alice' };
obj.name = 'Bob';  // OK!对象内容可以修改
// obj = {};       // TypeError: 不能重新赋值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

底层原理:var 声明的变量在创建执行上下文时被添加到变量环境(Variable Environment)并初始化为 undefined;而 let/const 声明的变量被添加到词法环境(Lexical Environment),虽然也会被"提升",但在代码执行到声明语句之前处于暂时性死区(TDZ)——引擎知道这个变量存在,但禁止访问。

最佳实践:默认使用 const,需要重新赋值时使用 let,永远不要用 var。

# 1.4 条件语句

JavaScript 提供if结构和switch结构,完成条件判断,即只有满足预设的条件,才会执行相应的语句。

# 1.4.1 if 结构

if结构先判断一个表达式的布尔值,然后根据布尔值的真伪,执行不同的语句。所谓布尔值,指的是 JavaScript 的两个特殊值,true表示“真”,false表示“伪”。

if (布尔值)
  语句;

// 或者
if (布尔值) 语句;
1
2
3
4
5

上面是if结构的基本形式。需要注意的是,“布尔值”往往由一个条件表达式产生的,必须放在圆括号中,表示对表达式求值。如果表达式的求值结果为true,就执行紧跟在后面的语句;如果结果为false,则跳过紧跟在后面的语句。

if (m === 3) {
  m += 1;
}
1
2
3

建议总是在if语句中使用大括号,因为这样方便插入语句。

注意,if后面的表达式之中,不要混淆赋值表达式(=)、严格相等运算符(===)和相等运算符(==)。尤其是赋值表达式不具有比较作用。

var x = 1;
var y = 2;
if (x = y) {
  console.log(x);
}
// "2"
1
2
3
4
5
6

上面代码的原意是,当x等于y的时候,才执行相关语句。但是,不小心将严格相等运算符写成赋值表达式,结果变成了将y赋值给变量x,再判断变量x的值(等于2)的布尔值(结果为true)。

# 1.4.2 if...else结构

if代码块后面,还可以跟一个else代码块,表示不满足条件时,所要执行的代码。

if (m === 3) {
  // 满足条件时,执行的语句
} else {
  // 不满足条件时,执行的语句
}
1
2
3
4
5

上面代码判断变量m是否等于3,如果等于就执行if代码块,否则执行else代码块。

对同一个变量进行多次判断时,多个if...else语句可以连写在一起。

if (m === 0) {
  // ...
} else if (m === 1) {
  // ...
} else if (m === 2) {
  // ...
} else {
  // ...
}
1
2
3
4
5
6
7
8
9

# 1.4.3 switch结构

多个if...else连在一起使用的时候,可以转为使用更方便的switch结构。

switch (fruit) {
  case "banana":
    // ...
    break;
  case "apple":
    // ...
    break;
  default:
    // ...
}
1
2
3
4
5
6
7
8
9
10

上面代码根据变量fruit的值,选择执行相应的case。如果所有case都不符合,则执行最后的default部分。需要注意的是,每个case代码块内部的break语句不能少,否则会接下去执行下一个case代码块,而不是跳出switch结构。

var x = 1;

switch (x) {
  case 1:
    console.log('x 等于1');
  case 2:
    console.log('x 等于2');
  default:
    console.log('x 等于其他值');
}
// x等于1
// x等于2
// x等于其他值
1
2
3
4
5
6
7
8
9
10
11
12
13

上面代码中,case代码块之中没有break语句,导致不会跳出switch结构,而会一直执行下去。正确的写法是像下面这样。

# 1.4.4 三元运算符 ?:

JavaScript 还有一个三元运算符(即该运算符需要三个运算子)?:,也可以用于逻辑判断。

(条件) ? 表达式1 : 表达式2
1

上面代码中,如果“条件”为true,则返回“表达式1”的值,否则返回“表达式2”的值。

var even = (n % 2 === 0) ? true : false;
1

# 1.4.5 条件语句的设计原理与性能

疑惑:if...else 和 switch 在性能上有区别吗?什么时候用哪个更好?

答疑:从引擎优化角度看,switch 语句在分支较多且是常量比较时通常比 if...else 链快,因为 V8 可能将其优化为跳转表(Jump Table)——直接通过值计算跳转地址,时间复杂度为 O(1)。而 if...else 链是逐条件顺序判断,时间复杂度为 O(n)。

// switch 适合:离散值匹配
switch (statusCode) {
    case 200: handleSuccess(); break;
    case 404: handleNotFound(); break;
    case 500: handleServerError(); break;
}

// if...else 适合:范围判断、复杂条件
if (score >= 90) grade = 'A';
else if (score >= 80) grade = 'B';
else if (score >= 70) grade = 'C';
else grade = 'D';

// 对象映射(更优雅的替代方案)
const handlers = {
    200: handleSuccess,
    404: handleNotFound,
    500: handleServerError,
};
(handlers[statusCode] || handleDefault)();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

结论:小量分支差异可忽略;大量离散值匹配用 switch 或对象映射;复杂逻辑用 if...else。

# 1.5 循环语句

循环语句用于重复执行某个操作,它有多种形式。

# 1.5.1 while循环

while语句包括一个循环条件和一段代码块,只要条件为真,就不断循环执行代码块。

while (条件) {
  语句;
}
1
2
3

下面是while语句的一个例子。下面的代码将循环100次,直到i等于100为止。

var i = 0;

while (i < 100) {
  console.log('i 当前为:' + i);
  i = i + 1;
}
1
2
3
4
5
6

# 1.5.2 for循环

for语句是循环命令的另一种形式,可以指定循环的起点、终点和终止条件。它的格式如下。

// 或者
for (初始化表达式; 条件; 递增表达式) {
  语句
}
1
2
3
4

for语句后面的括号里面,有三个表达式。

  • 初始化表达式(initialize):确定循环变量的初始值,只在循环开始时执行一次。
  • 条件表达式(test):每轮循环开始时,都要执行这个条件表达式,只有值为真,才继续进行循环。
  • 递增表达式(increment):每轮循环的最后一个操作,通常用来递增循环变量。

下面是一个例子。

var x = 3;
for (var i = 0; i < x; i++) {
  console.log(i);
}
// 0
// 1
// 2
1
2
3
4
5
6
7

上面代码中,初始化表达式是var i = 0,即初始化一个变量i;测试表达式是i < x,即只要i小于x,就会执行循环;递增表达式是i++,即每次循环结束后,i增大1。

# 1.5.3 do...while循环

do...while循环与while循环类似,唯一的区别就是先运行一次循环体,然后判断循环条件。

// 或者
do {
  语句
} while (条件);
1
2
3
4

不管条件是否为真,do...while循环至少运行一次,这是这种结构最大的特点。另外,while语句后面的分号注意不要省略。

下面是一个例子。

var x = 3;
var i = 0;

do {
  console.log(i);
  i++;
} while(i < x);
1
2
3
4
5
6
7

# 1.5.4 break和continue

break语句和continue语句都具有跳转作用,可以让代码不按既有的顺序执行。

break语句用于跳出代码块或循环。

for (var i = 0; i < 5; i++) {
  console.log(i);
  if (i === 3)
    break;
}
// 0
// 1
// 2
// 3
1
2
3
4
5
6
7
8
9

上面代码执行到i等于3,就会跳出循环。

continue语句用于立即终止本轮循环,返回循环结构的头部,开始下一轮循环。

var i = 0;

while (i < 100){
  i++;
  if (i % 2 === 0) continue;
  console.log('i 当前为:' + i);
}
1
2
3
4
5
6
7

上面代码只有在i为奇数时,才会输出i的值。如果i为偶数,则直接进入下一轮循环。

如果存在多重循环,不带参数的break语句和continue语句都只针对最内层循环。

# 1.5.5 标签(label)

JavaScript 语言允许,语句的前面有标签(label),相当于定位符,用于跳转到程序的任意位置,标签的格式如下。

label:
  语句
1
2

标签可以是任意的标识符,但不能是保留字,语句部分可以是任意语句。

标签通常与break语句和continue语句配合使用,跳出特定的循环。

top:
  for (var i = 0; i < 3; i++){
    for (var j = 0; j < 3; j++){
      if (i === 1 && j === 1) break top;
      console.log('i=' + i + ', j=' + j);
    }
  }
// i=0, j=0
// i=0, j=1
// i=0, j=2
// i=1, j=0
1
2
3
4
5
6
7
8
9
10
11

上面代码为一个双重循环区块,break命令后面加上了top标签(注意,top不用加引号),满足条件时,直接跳出双层循环。如果break语句后面不使用标签,则只能跳出内层循环,进入下一次的外层循环。

标签也可以用于跳出代码块。

foo: {
  console.log(1);
  break foo;
  console.log('本行不会输出');
}
console.log(2);
// 1
// 2
1
2
3
4
5
6
7
8

上面代码执行到break foo,就会跳出区块。

continue语句也可以与标签配合使用。

top:
  for (var i = 0; i < 3; i++){
    for (var j = 0; j < 3; j++){
      if (i === 1 && j === 1) continue top;
      console.log('i=' + i + ', j=' + j);
    }
  }
// i=0, j=0
// i=0, j=1
// i=0, j=2
// i=1, j=0
// i=2, j=0
// i=2, j=1
// i=2, j=2
1
2
3
4
5
6
7
8
9
10
11
12
13
14

上面代码中,continue命令后面有一个标签名,满足条件时,会跳过当前循环,直接进入下一轮外层循环。如果continue语句后面不使用标签,则只能进入下一轮的内层循环。

# 1.5.6 ES6+的循环增强

ES6 引入了 for...of 循环,解决了 for...in 的诸多问题:

// for...in 遍历"键"(适用于对象,不推荐用于数组)
const arr = ['a', 'b', 'c'];
for (let key in arr) {
    console.log(key);      // '0', '1', '2'(注意是字符串!)
    console.log(typeof key); // 'string'
}

// for...of 遍历"值"(适用于可迭代对象:数组、字符串、Map、Set等)
for (let value of arr) {
    console.log(value);    // 'a', 'b', 'c'
}

// for...of 遍历字符串
for (let char of 'hello') {
    console.log(char);    // 'h', 'e', 'l', 'l', 'o'
}

// for...of 遍历 Map
const map = new Map([['name', 'Alice'], ['age', 25]]);
for (let [key, value] of map) {
    console.log(`${key}: ${value}`);
}

// for...of 配合 entries() 获取索引
for (let [index, value] of arr.entries()) {
    console.log(`${index}: ${value}`);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

底层原理:for...of 依赖迭代器协议(Iterator Protocol)。任何实现了 [Symbol.iterator]() 方法的对象都是可迭代的。该方法返回一个迭代器对象,迭代器必须有 next() 方法,每次调用返回 { value, done } 格式的结果。

// 自定义可迭代对象
const range = {
    from: 1,
    to: 5,
    [Symbol.iterator]() {
        let current = this.from;
        const last = this.to;
        return {
            next() {
                return current <= last
                    ? { value: current++, done: false }
                    : { done: true };
            }
        };
    }
};

for (let num of range) {
    console.log(num);  // 1, 2, 3, 4, 5
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 1.6 console

console 是 JavaScript 中用于调试和输出信息的内置对象。它提供了多种方法,可以帮助开发者在开发过程中查看变量值、调试代码、记录日志等。

# 1.6.1 console.log()

描述:输出普通日志信息。

console.log('Hello, World!'); // 输出: Hello, World!
console.log(42); // 输出: 42
console.log({ name: 'Alice', age: 25 }); // 输出: { name: 'Alice', age: 25 }
1
2
3

# 1.6.2 console.info()

描述:输出信息性消息(通常与 console.log() 相同,但在某些浏览器中会显示不同的图标)。

console.info('This is an info message.'); // 输出: This is an info message.
1

# 1.6.3 console.warn()

描述:输出警告信息(通常以黄色背景显示)。

console.warn('This is a warning!'); // 输出: This is a warning!
1

# 1.6.4 console.error()

描述:输出错误信息(通常以红色背景显示)。

console.error('This is an error!'); // 输出: This is an error!
1

# 1.6.5 console.debug()

描述:输出调试信息(通常与 console.log() 相同,但在某些浏览器中需要启用调试模式才能看到)。

console.debug('Debugging information.'); // 输出: Debugging information.
1

# 1.6.6 console.table()

描述:以表格形式输出数组或对象。

const users = [
  { name: 'Alice', age: 25 },
  { name: 'Bob', age: 30 },
];
console.table(users);
1
2
3
4
5

输出:

┌─────────┬─────────┬─────┐
│ (index) │  name   │ age │
├─────────┼─────────┼─────┤
│    0    │ 'Alice' │ 25  │
│    1    │  'Bob'  │ 30  │
└─────────┴─────────┴─────┘
1
2
3
4
5
6

# 1.6.7 console.time()

描述:用于计算代码执行时间。

console.time('Timer');
for (let i = 0; i < 1000000; i++) {}
console.timeEnd('Timer'); // 输出: Timer: 0.123ms
1
2
3

# 1.6.8 console.assert()

描述:如果断言为 false,则输出错误信息。

console.assert(2 + 2 === 5, 'Math is broken!'); // 输出: Assertion failed: Math is broken!
1

# 1.6.9 console.trace()

描述:输出调用堆栈。

function foo() {
  console.trace('Trace');
}
foo();
1
2
3
4

输出:

Trace
  at foo (script.js:2)
  at script.js:5
1
2
3

# 1.6.10 格式化输出

console.log() 支持格式化输出,使用占位符:

  • %s:字符串
  • %d 或 %i:整数
  • %f:浮点数
  • %o:对象
  • %c:CSS 样式

示例:

console.log('Name: %s, Age: %d', 'Alice', 25); // 输出: Name: Alice, Age: 25
console.log('%cStyled Text', 'color: red; font-size: 20px;'); // 输出红色大字
1
2

# 1.6.11 console高级用法与调试技巧

// 1. console.group / console.groupEnd — 分组输出
console.group('用户信息');
console.log('姓名: Alice');
console.log('年龄: 25');
console.groupEnd();

// 2. console.count — 计数器
function handleClick() {
    console.count('click');  // click: 1, click: 2, ...
}
handleClick();
handleClick();

// 3. console.dir — 以对象形式展示DOM元素
// console.dir(document.body);  // 在浏览器中运行

// 4. console.clear — 清空控制台
// console.clear();

// 5. 性能分析对比
console.time('Array.from');
Array.from({ length: 100000 }, (_, i) => i);
console.timeEnd('Array.from');

console.time('spread');
[...Array(100000).keys()];
console.timeEnd('spread');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

生产环境最佳实践:

  • 不要在生产代码中留下 console.log,可以使用构建工具(如 Terser)自动移除
  • 使用专业的日志库(如 winston、pino)替代 console
  • 使用 performance.now() 或 Performance API 进行精确性能测量
上次更新: 2026/06/10, 11:13:41
README
数据类型

← README 数据类型→

最近更新
01
信号崩溃快速排查
06-15
02
CoreDump破案
06-15
03
perf火焰图实战
06-15
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式