Day17(前端:JavaScript基础阶段)
接续上文:Day16(前端:JavaScript基础阶段)_前端题目 csdn-CSDN博客
点关注不迷路哟。你的点赞、收藏,一键三连,是我持续更新的动力哟!!!
主页:一位搞嵌入式的 genius-CSDN博客
系列文章专栏:
https://blog.csdn.net/m0_73589512/category_13011829.html
目录
Day17 (前端:JavaScript基础阶段)
1. 编程领域中 foo、bar、baz 的含义、由来与应用解析
1.1 foo、bar、baz 的核心含义与应用场景
1.2 foo、bar、baz 的起源假说与争议
2. JavaScript 函数基础:概念、使用步骤与核心作用解析
2.1 认识函数:定义、本质与常见内置函数
2.1.1 已接触的 JavaScript 内置函数
2.1.2 函数的核心本质特征
2.2 函数的使用步骤:声明与调用
2.2.1 第一步:声明函数
2.2.2 第二步:调用函数
2.3 函数的核心作用:与循环语句的对比
2.3.1 函数与循环语句的核心对比
2.3.2 函数的核心价值
3. JavaScript 函数基础:声明、调用与实战练习
3.1 函数的声明:语法、规则与注意事项
3.1.1 函数声明的基本语法与结构
3.1.2 函数声明的核心规则与建议
3.1.3 函数声明与调用的基础演练
3.2 函数的调用:方式、机制与位置
3.2.1 函数调用的基本方式
3.2.1 函数调用的核心机制
3.3 函数的实战练习:从固定逻辑到基础应用
3.3.1 练习 1:打印固定个人信息(printInfo 函数)
3.3.2 练习 2:计算并打印固定数字和(sum 函数)
3.4 知识小结:核心考点与难度梳理
4. JavaScript 函数进阶:参数的使用、实战练习与重构优化
4.1 函数参数的基础概念:形参与实参
4.1.1 形参:函数定义时的 “数据占位符”
4.1.2 实参:函数调用时的 “具体数据”
4.1.3 形参与实参的对应关系
4.2 函数参数的实战练习:从单参数到多参数
4.2.1 练习 1:单参数函数 —— 与人打招呼(sayHello 函数)
4.2.2 练习 2:单参数函数 —— 唱生日快乐歌(singBirthdaySong 函数)
4.2.3 练习 3:多参数函数 —— 计算两个数字的和(sum 函数)
4.2.4 练习 4:多参数函数 —— 打印个人信息(printInfo 函数重构)
4.3 知识小结:函数参数的核心考点与难度梳理
4.4 实战建议:函数参数的使用规范
5. JavaScript 函数返回值:概念、return 关键字与实战应用
5.1 函数返回值的基础概念
5.1.1 什么是函数返回值
5.1.2 函数的默认返回值:undefined
5.1.3 基础示例:返回值与外部接收
5.2 return 关键字的核心特性
5.2.1 return 的两大核心功能
5.2.2 return 的关键说明
5.3 函数返回值的三大注意事项
5.3.1 注意事项一:所有无显式 return 的函数均返回 undefined
5.3.2 注意事项二:显式返回 undefined 与默认行为等效
5.3.3 注意事项三:return 的中断效应会跳过后续代码
5.4 函数返回值的实战案例:sum 函数与作用域隔离
5.4.1 案例需求
5.4.2 代码实现
5.4.3 案例关键结论
5.5 知识小结:函数返回值的核心考点
6. JavaScript 函数实战:数字格式化练习与函数间调用解析
6.1 数字格式化的函数练习:从需求到实现
6.1.1 例题:数字格式化的应用场景与转换规则
6.1.2 数字格式化函数的实现与调试
6.2 函数之间的调用:协作逻辑与调试方法
6.2.1 函数之间的调用与调试方法
6.2.2 函数间调用的注意事项
6.3 知识小结:函数实战的核心考点与难度梳理
7. JavaScript 函数的 arguments 对象:特性、应用与 ES6 替代方案
7.1 arguments 参数的基础特性
7.1.1 核心特性解析
7.1.2 关键注意事项
7.2 函数中 arguments 变量的使用方法
7.2.1 基础使用:访问与遍历
7.2.2 核心特点总结
7.3 arguments 的实战案例:不定参求和
7.3.1 案例需求
7.3.2 实现步骤
7.3.3 完整代码
7.3.4 注意事项
7.4 知识小结:arguments 的核心考点与 ES6 过渡
7.5 实战建议:arguments 与剩余参数的选择
8. JavaScript 作用域基础:概念、类型与 ES5 特性解析
8.1 作用域的基本概念与全局作用域
8.1.1 作用域的定义与全局作用域
8.1.2 作用域的 “变量提升” 现象
8.2 ES5 之前的 “无块级作用域” 特性
8.2.1 块级作用域的缺失:var与代码块的关系
8.2.2 控制语句中的作用域表现
8.3 函数作用域:ES5 中唯一的 “局部作用域”
8.3.1 函数作用域的基本特性
8.3.2 嵌套函数的作用域规则
8.4 知识小结:ES5 作用域的核心考点
8.5 核心结论
9. JavaScript 变量类型与访问规则:全局、局部变量及作用域链
9.1 全局变量、局部变量与外部变量的概念
9.1.1 全局变量(Global Variable)
9.1.2 局部变量(Local Variable)
9.1.3 外部变量(Outer Variable)
9.1.4 三类变量的对比总结
9.2 变量的访问顺序:作用域链规则
9.2.1 基本访问规则
9.2.2 例题分析:变量访问顺序的具体表现
例题 1:访问当前函数作用域的变量
例题 2:访问上层作用域的变量
例题 3:访问全局作用域的变量
例题 4:访问 window 对象的变量(全局变量的特殊挂载)
9.3 总结:变量访问的核心逻辑
10. JavaScript 作用域与函数表达式:概念及差异解析
10.1 作用域与变量类型
10.1.1 作用域的核心定义
10.1.2 变量类型及特征
10.1.3 变量访问顺序
10.2 函数表达式:定义与特性
10.2.1 函数表达式的定义
10.2.2 函数声明与函数表达式的区别
10.2.3 开发实践建议
10.3 知识小结:核心考点与难度梳理
10.4 核心结论
11. JavaScript 中的头等函数与函数式编程
11.1 函数的头等公民特性
11.1.1 函数的表达式写法:赋值与传递
11.1.2 函数作为参数、返回值与存储载体
11.1.2.1 函数作为参数传递
11.1.2.2 函数作为返回值
11.1.2.3 函数存储在数据结构中
11.2 函数式编程:理念与语言对比
11.2.1 函数式编程的核心特征
11.2.2 不同语言对函数式编程的支持对比
11.2.3 函数式编程思想的起源与影响
11.3 知识小结:头等函数与函数式编程的核心考点
11.4 核心结论
12. JavaScript 函数式编程核心:头等函数与高阶函数应用
12.1 头等函数的特性与函数式编程基础
12.1.1 头等函数的核心特性
12.1.2 函数式编程的核心场景
12.2 回调函数:异步逻辑的核心机制
12.2.1 回调函数的定义与核心特征
12.2.2 回调函数的实践案例
案例 1:模拟网络请求的回调处理
案例 2:回调函数的正确传递方式
案例 3:匿名函数简化回调逻辑
12.3 高阶函数:接收或返回函数的函数
12.3.1 高阶函数的判定标准
12.3.2 典型示例
12.3.3 内置高阶函数(预告)
12.4 匿名函数:简化回调的临时函数
12.4.1 匿名函数的定义与特点
12.4.2 与函数声明的对比
12.5 知识小结:核心考点与难度梳理
12.6 核心结论
Day17 (前端:JavaScript基础阶段)
1. 编程领域中 foo、bar、baz 的含义、由来与应用解析
在编程实践与技术文档中,foo、bar、baz 是高频出现的占位符名词,广泛用于代码示例、教程及官方资料,尤其在英文技术生态中更为常见。其核心价值在于提供 “无特定含义的通用命名”,避免因具体业务命名引发歧义,确保示例能聚焦技术逻辑本身。后续课程案例中可能直接沿用这类术语,需先明确其本质是编程领域约定俗成的 “伪变量”,而非具有实际业务意义的名称。
1.1 foo、bar、baz 的核心含义与应用场景
foo、bar、baz 是编程领域的标准化占位符,主要用于为函数、变量、文件或类等元素临时命名,本身不承载任何特定功能或业务逻辑,仅作为 “通用代称” 存在。
-
典型应用示例:在技术问答平台(如 Stack Overflow)、官方文档中,常见类似代码:
var foo = 10;
(用 foo 作为变量名)、function bar() { return true; }
(用 bar 作为函数名),通过极简命名简化示例,让读者专注于代码逻辑而非命名含义; -
跨语言通用性:无论前端(JavaScript、HTML)、后端(Java、Python)还是移动端开发,这类占位符均适用,且中文技术文档也常沿用该习惯,需理解其 “约定俗成” 的属性,而非特定语言专属术语。
1.2 foo、bar、baz 的起源假说与争议
foo、bar、baz 的具体起源尚无定论,目前主流假说主要有三类,但均存在一定争议,实际开发中无需深究词源,仅需掌握其应用场景即可:
起源假说 | 核心依据 | 争议点 |
---|---|---|
迪吉多公司手册起源 | 1957 年美国数字设备公司(DEC)的技术手册中首次出现类似占位符 | 缺乏直接文献佐证,手册原文未明确关联 “foo/bar” |
电子工程领域衍生 | 可能源于电子学中 “反转信号” 的命名习惯(如 “foo 信号”) | 跨领域关联性存疑,无明确证据证明技术传承路径 |
美国漫画文化影响 | 早期美国漫画常用 “foo” 代指 “好运”,发音类似中文 “福”,后被引入编程领域 | 文化传播路径不清晰,缺乏行业共识支持 |
2. JavaScript 函数基础:概念、使用步骤与核心作用解析
函数是 JavaScript 编程中的核心组件,不仅包含浏览器与语言内置的预定义功能,还支持开发者自定义封装逻辑,是实现代码复用、模块化与灵活执行控制的关键工具。本节从 “认识函数” 入手,梳理函数的本质特征、使用步骤(声明与调用)及与循环语句的差异,建立对函数的基础认知。
2.1 认识函数:定义、本质与常见内置函数
函数本质是 “特定功能代码的封装体”,通过封装实现功能独立、代码可复用,在 JavaScript 中主要分为 “内置预定义函数” 与 “开发者自定义函数” 两类。
2.1.1 已接触的 JavaScript 内置函数
在基础学习阶段,已涉及多个 JavaScript 或浏览器提供的内置函数,其功能与调用方式如下:
-
alert 函数:浏览器内置函数,调用后弹出带有提示信息的弹窗,语法如
alert("提示内容")
; -
prompt 函数:浏览器内置函数,用于接收用户输入,返回用户输入的字符串(或 null),语法如
let input = prompt("请输入内容")
; -
console.log 函数:JavaScript 内置函数,需通过
console
对象调用,用于在控制台输出信息(调试常用),语法如console.log("控制台输出内容")
; -
类型转换函数:JavaScript 内置的基础函数,用于数据类型转换,包括
Number(数据)
(转为数字)、String(数据)
(转为字符串)、Boolean(数据)
(转为布尔值)等。
2.1.2 函数的核心本质特征
当讨论 “函数” 时,核心聚焦其以下四个关键特征,这些特征决定了函数的价值与用法:
-
功能单一性:每个函数专注实现一个独立功能(如
alert
仅负责弹窗,Number
仅负责类型转换),避免 “一个函数包含多类不相关逻辑”; -
代码封装性:函数内部包裹实现功能的完整代码块,外部调用时无需关注内部逻辑,只需通过 “函数名” 触发执行;
-
调用机制:函数不会自动执行,需通过 “函数名 + 括号”(如
foo()
)主动调用,支持在任意需要的时机触发; -
两类来源:既可以使用 JavaScript 或浏览器提供的 “内置函数”,也可以根据业务需求编写 “自定义函数”,灵活适配开发场景。
2.2 函数的使用步骤:声明与调用
使用函数需遵循 “先声明、后调用” 的基本流程(特殊情况如函数表达式可调整顺序,但基础阶段优先掌握 “声明在前”),两个步骤的核心要点与语法规则如下。
2.2.1 第一步:声明函数
函数声明是 “定义函数名称、封装功能代码” 的过程,核心是通过function
关键字明确函数的基本信息,关键要点包括:
-
语法结构:基础语法为
function 函数名(参数){ 函数体(功能代码) }
,例如function sayHello(){ console.log("Hello"); }
; -
命名规则:函数名需遵循 JavaScript 标识符规范(字母、数字、_、$ 组成,不能以数字开头,区分大小写),建议使用 “小驼峰命名法”(如
sayHello
); -
封装范围:函数体(
{}
内的代码)是实现功能的核心,需包含完整的逻辑(如变量定义、条件判断、循环等); -
声明位置:在基础阶段,建议将函数声明在调用代码之前,避免因 “变量提升” 机制导致的逻辑混淆;
-
术语等效:“函数声明”“函数定义”“函数创建” 表达相同含义,均指 “创建函数的过程”。
2.2.2 第二步:调用函数
函数调用是 “触发函数执行、执行封装逻辑” 的过程,只有调用后函数内的代码才会运行,关键特征包括:
-
执行方式:基础调用语法为 “函数名 + 括号”(如
sayHello()
),若函数需接收参数,需在括号内传入对应值(如sum(1,2)
); -
调用源:可调用的函数包括三类 ——JavaScript 内置函数(如
Number()
)、浏览器内置函数(如alert()
)、自定义函数(如sayHello()
); -
调用时机:支持多次重复调用同一函数(如
sayHello()
可调用 1 次或 100 次),实现代码复用; -
术语说明:“函数调用”“函数执行”“函数运行” 表达相同含义,均指 “触发函数代码执行的过程”;
-
后续扩展:在后续学习框架或第三方库时,会接触 “通过对象调用函数”(如
console.log()
)、“通过类调用函数” 等复杂方式,基础阶段先掌握 “直接调用自定义函数”。
2.3 函数的核心作用:与循环语句的对比
函数的核心价值在于解决 “代码复用”“模块化组织”“灵活执行控制” 等问题,与循环语句(处理固定重复操作)相比,优势显著,具体对比与核心作用如下。
2.3.1 函数与循环语句的核心对比
对比维度 | 循环语句 | 函数 |
---|---|---|
执行时机 | 代码执行到循环时立即顺序执行 | 需通过调用触发,可按需执行 |
执行复用方式 | 固定次数重复(如for(let i=0;i<5;i++) 执行 5 次) | 无次数限制,可在任意位置调用任意次 |
代码组织 | 线性流程控制,需嵌入主逻辑 | 模块化封装,独立于主逻辑 |
核心优势 | 高效处理 “固定次数的重复操作” | 提高代码复用率与维护性 |
2.3.2 函数的核心价值
-
代码复用:避免对 “相同逻辑” 的重复编写(如多次需要 “弹出欢迎语”,只需声明 1 个
sayWelcome
函数,多次调用即可); -
功能封装:将复杂逻辑封装为函数,实现 “高内聚、低耦合”(外部只需关注函数能做什么,无需关注内部如何实现);
-
执行控制:支持在 “需要时调用”(如用户点击按钮后调用函数),而非 “代码顺序执行”,灵活适配交互场景;
-
开发效率:通过模块化拆分代码(将复杂功能拆分为多个小函数),降低代码复杂度,提升开发与维护效率。
3. JavaScript 函数基础:声明、调用与实战练习
函数是 JavaScript 中实现代码复用与逻辑封装的核心工具,其使用需遵循 “先声明、后调用” 的基本流程。本节将从函数声明的语法规则、调用机制入手,结合 “打印个人信息”“计算数字和” 等实战案例,详解函数的基础用法,同时梳理核心知识点与常见易错点,帮助建立函数使用的规范认知。
3.1 函数的声明:语法、规则与注意事项
函数声明是 “定义函数名称、封装功能逻辑” 的第一步,需通过function
关键字明确函数结构,同时遵循命名规范与代码执行特点。
3.1.1 函数声明的基本语法与结构
函数声明的核心是通过function
关键字搭建框架,包含 “函数名、参数括号、函数体” 三部分,具体结构如下:
-
关键字:以
function
开头,明确当前代码块为函数定义; -
函数名:用于标识函数(如
sayHello
),需通过函数名调用函数,区别于for
/if
等无需命名的代码块; -
参数括号:紧跟函数名的
()
,用于后续接收外部传入的数据(即使暂时无参数,也必须保留空括号,不可省略); -
函数体:用
{}
包裹的代码块,内部是实现函数功能的具体逻辑,代码按自上而下顺序执行。 基础语法示例:
function 函数名() {// 函数体:功能实现代码代码逻辑1;代码逻辑2; }
3.1.2 函数声明的核心规则与建议
-
命名规则:
-
遵循 JavaScript 标识符规范:只能以字母、下划线(
_
)、美元符号($
)开头,后续可包含字母、数字、_
、$
,且区分大小写; -
禁止以数字开头(如
function 1sayHello(){}
为错误写法)。
-
-
命名建议:
-
使用 “动词 + 名词” 的组合(如
printInfo
“打印信息”、formatDate
“格式化日期”),做到 “见名知意”; -
采用小驼峰命名法(首字母小写,后续单词首字母大写,如
sayHelloWorld
),符合 JavaScript 开发习惯。
-
-
执行特点:
-
函数声明后不会自动执行,仅完成 “功能定义”(类似 “制作工具但不使用”),必须通过后续调用触发代码执行;
-
函数体内部代码在未调用时,不会参与页面的顺序执行。
-
-
代码规范:
-
函数名与参数括号之间建议保留 1 个空格(如
function sayHello ()
,而非function sayHello()
); -
参数括号与函数体大括号之间建议保留 1 个空格(如
function sayHello () {
,而非function sayHello (){
),提升代码可读性。
-
3.1.3 函数声明与调用的基础演练
以sayHello
函数为例,演示函数从声明到调用的完整流程:
// 1. 声明函数:定义“打印问候语”的功能 function sayHello () {console.log("Hello!");console.log("My name is Coderwhy!");console.log("how do you do!"); } // 2. 调用函数:通过“函数名+括号”触发执行(可多次调用) sayHello(); // 第一次调用:执行函数体,控制台输出3行问候语 sayHello(); // 第二次调用:再次执行函数体,控制台重复输出3行问候语
关键结论:同一函数可被多次调用,每次调用都会完整执行函数体内的所有代码,实现 “一次声明、多次复用”。
3.2 函数的调用:方式、机制与位置
函数调用是 “触发函数执行” 的关键步骤,只有通过调用,函数体内的逻辑才会运行,需掌握调用方式与执行机制。
3.2.1 函数调用的基本方式
函数调用的基础语法为 “函数名 + 空括号”(无参数时),具体格式:
函数名(); // 如sayHello();、printInfo();
-
即使函数体内部无任何参数依赖(如
sayHello
仅打印固定内容),括号也不可省略(sayHello
为函数本身,sayHello()
才是调用函数); -
调用时会按照函数体内部代码的顺序,依次执行每条语句(如
console.log
会按顺序输出)。
3.2.1 函数调用的核心机制
-
执行时机:函数仅在 “调用时” 执行,未调用时,函数体代码处于 “休眠状态”,不占用执行资源;
-
调用位置:
-
需在函数声明之后调用(基础阶段避免 “先调用、后声明”,防止因 “变量提升” 导致逻辑混淆);
-
需在函数的 “作用域范围内” 调用(如全局声明的函数可在全局任意位置调用,后续会学习局部函数的调用限制);
-
-
多次调用的效果:每次调用都是独立的执行过程,函数体内的临时变量(如后续案例中的
result
)会在每次调用时重新初始化,互不影响。
示例:多次调用sayHello
函数的执行效果
function sayHello () {let count = 1; // 每次调用时,count都会重新初始化为1console.log("调用次数标记:", count);count++; } sayHello(); // 输出“调用次数标记:1” sayHello(); // 仍输出“调用次数标记:1”(count重新初始化)
3.3 函数的实战练习:从固定逻辑到基础应用
通过两个基础案例,掌握函数在实际场景中的使用,理解 “封装固定逻辑、实现灵活复用” 的核心价值。
3.3.1 练习 1:打印固定个人信息(printInfo 函数)
需求:封装一个函数,调用后在控制台打印固定的个人信息(姓名、年龄、身高)。 实现代码:
// 声明函数:封装“打印个人信息”的逻辑 function printInfo () {console.log("my name is why");console.log("age is 18"); console.log("height is 1.88"); } // 调用函数:触发信息打印 printInfo(); // 控制台依次输出3行个人信息
优势分析: 相比直接在代码中写 3 条console.log
,函数封装的优势在于 “可复用”—— 若后续需要多次打印该信息,只需调用printInfo()
,无需重复编写 3 行输出代码,减少冗余。
3.3.2 练习 2:计算并打印固定数字和(sum 函数)
需求:封装一个函数,计算 10 与 20 的和,并在控制台打印结果。 实现代码:
// 声明函数:封装“计算固定值和并打印”的逻辑 function sum () {let number1 = 10; // 固定第一个数字let number2 = 20; // 固定第二个数字let result = number1 + number2; // 计算和console.log("result:", result); // 打印结果 } // 调用函数:触发计算与打印 sum(); // 控制台输出“result: 30”
局限性说明: 当前函数只能计算 “10+20” 的固定结果,无法动态接收外部传入的数字(如计算 “5+8”“20+30”),后续将通过 “函数参数” 解决该问题,让函数更灵活。
3.4 知识小结:核心考点与难度梳理
知识点 | 核心内容 | 考试重点 / 易混淆点 | 难度系数 |
---|---|---|---|
函数声明与调用 | 用function 关键字声明函数(含函数名、参数括号、代码块);通过 “函数名 ()`” 调用 | 函数名命名规则(与变量一致,但推荐动词开头);调用时不可省略括号(sayHello ≠sayHello() ) | ⭐⭐ |
函数命名规范 | 遵循标识符规则(不以数字开头);推荐 “动词 + 名词”“小驼峰” 写法 | 特殊函数名的例外情况(如isNaN ,虽以字母开头但为 “判断 + 名词” 结构) | ⭐ |
函数执行机制 | 声明后不自动执行,需显式调用;每次调用完整执行函数体 | 与for /if 代码块 “自动执行” 的区别(for 满足条件即执行,函数需主动调用) | ⭐⭐ |
参数与返回值(基础) | 小括号用于后续传递参数;当前案例无参数仍可运行;结果通过console.log 输出 | 无参数时必须保留空括号(不可省略);当前函数仅 “打印结果”,未实现 “返回结果” | ⭐⭐⭐ |
函数复用性 | 可多次调用同一函数,实现代码复用;调用次数无限制 | 需在函数作用域内调用(后续讲解作用域);多次调用时临时变量重新初始化 | ⭐⭐ |
练习案例 1(printInfo) | 打印固定个人信息;优势是减少重复输出代码 | 硬编码数据导致灵活性不足(无法动态修改姓名、年龄) | ⭐ |
练习案例 2(sum) | 计算固定值(10+20)并打印;体现函数封装逻辑的基础用法 | 缺乏参数传递,无法动态计算不同数字的和;需后续学习参数解决该问题 | ⭐⭐ |
4. JavaScript 函数进阶:参数的使用、实战练习与重构优化
函数参数是提升函数通用性的核心工具,通过将 “固定值” 替换为 “参数占位符”,可让函数灵活处理不同输入数据,适配更多业务场景。本节从函数参数的基础概念(形参、实参)入手,结合 “打印个人信息”“打招呼”“计算数字和” 等实战案例,详解参数化函数的改造与使用方法,同时梳理核心知识点与易错点,帮助掌握函数从 “固定逻辑” 到 “通用工具” 的进阶技巧。
4.1 函数参数的基础概念:形参与实参
函数参数分为 “形参(parameter)” 与 “实参(argument)”,二者分别对应 “函数定义时的占位符” 与 “函数调用时的具体数据”,是实现函数通用化的关键。
4.1.1 形参:函数定义时的 “数据占位符”
-
核心定义:在函数声明时,写在
()
内的变量名称,用于接收函数调用时传入的数据,本质是 “函数内部的临时变量”; -
作用:为函数预留 “数据入口”,避免函数内部硬编码固定值,使函数能处理不同输入;
-
语法示例:在
function printInfo(name, age, height) {}
中,name
、age
、height
均为形参,分别对应 “姓名”“年龄”“身高” 的占位符; -
注意事项:形参仅在函数内部有效,函数外部无法访问;若函数无需接收数据,
()
内可留空(但不可省略()
)。
4.1.2 实参:函数调用时的 “具体数据”
-
核心定义:在函数调用时,写在
()
内的具体值(如数字、字符串、变量等),用于传递给函数的形参; -
作用:为形参赋值,让函数根据实际输入执行逻辑;
-
语法示例:在
printInfo("why", 18, 1.88)
中,"why"
(字符串)、18
(数字)、1.88
(数字)均为实参,分别对应name
、age
、height
三个形参; -
注意事项:实参的数量、类型需与形参匹配(基础阶段),否则可能导致函数逻辑异常(如形参需数字,实参传字符串可能引发计算错误)。
4.1.3 形参与实参的对应关系
-
顺序对应:实参按 “从左到右” 的顺序依次赋值给形参,第一个实参对应第一个形参,第二个实参对应第二个形参,以此类推;
-
数量匹配:
-
若实参数量 = 形参数量:正常赋值,函数按预期执行;
-
若实参数量 < 形参数量:未匹配的形参值为
undefined
(如printInfo("why", 18)
中,height
为undefined
); -
若实参数量 > 形参数量:多余实参不赋值给形参,但可通过
arguments
对象(后续学习)访问;
-
-
示例验证:
// 声明函数:3个形参(name, age, height) function printInfo(name, age, height) {console.log("姓名:", name);console.log("年龄:", age);console.log("身高:", height); } // 调用函数:3个实参,与形参完全匹配 printInfo("why", 18, 1.88); // 输出:姓名:why → 年龄:18 → 身高:1.88 // 调用函数:2个实参,height未赋值(为undefined) printInfo("小明", 20); // 输出:姓名:小明 → 年龄:20 → 身高:undefined
4.2 函数参数的实战练习:从单参数到多参数
通过不同类型的实战案例,掌握 “单参数函数”“多参数函数” 的定义与调用,理解参数如何提升函数通用性。
4.2.1 练习 1:单参数函数 —— 与人打招呼(sayHello 函数)
需求:定义一个函数,传入一个 “姓名” 参数,调用后打印 “Hello, [姓名]!” 的问候语,实现对不同人的个性化问候。 实现步骤:
-
声明函数:定义形参
name
,接收传入的姓名; -
编写逻辑:在函数体内,通过模板字符串拼接姓名与问候语;
-
调用函数:传入不同姓名作为实参,验证函数通用性。 代码示例:
// 1. 声明函数:单形参name function sayHello(name) {// 使用模板字符串拼接动态姓名console.log(`Hello, ${name}!`); } // 2. 调用函数:传入不同实参,实现个性化问候 sayHello("why"); // 输出:Hello, why! sayHello("小明"); // 输出:Hello, 小明! sayHello("Alice"); // 输出:Hello, Alice!
核心价值:相比 “固定打印 Hello, why!” 的函数,通过参数name
,函数可灵活适配任意姓名,通用性显著提升。
4.2.2 练习 2:单参数函数 —— 唱生日快乐歌(singBirthdaySong 函数)
需求:定义一个函数,传入一个 “姓名” 参数,调用后打印包含该姓名的生日歌歌词,实现为不同人 “唱” 生日歌。 实现代码:
function singBirthdaySong(name) {console.log(`祝你生日快乐,${name}!`);console.log(`祝你生日快乐,${name}!`);console.log(`祝你幸福祝你健康,${name}!`);console.log(`祝你生日快乐!`); } // 调用函数:传入不同姓名 singBirthdaySong("why"); // 为why唱生日歌 singBirthdaySong("小红"); // 为小红唱生日歌
技巧:通过模板字符串(反引号+${变量}
)实现 “固定歌词模板 + 动态姓名” 的结合,既保留固定格式,又支持个性化输入。
4.2.3 练习 3:多参数函数 —— 计算两个数字的和(sum 函数)
需求:定义一个函数,传入两个 “数字” 参数,调用后计算并打印它们的和,替代之前 “只能计算 10+20” 的固定函数。 实现步骤:
-
声明函数:定义两个形参
num1
、num2
,分别接收两个待加数字; -
编写逻辑:在函数体内计算
num1 + num2
,并打印结果; -
调用函数:传入不同数字组合作为实参,验证函数通用性。 代码示例:
// 1. 声明函数:两个形参num1、num2 function sum(num1, num2) {const result = num1 + num2; // 计算和console.log(`${num1} + ${num2} = ${result}`); } // 2. 调用函数:传入不同数字组合 sum(10, 20); // 输出:10 + 20 = 30 sum(5, 8); // 输出:5 + 8 = 13 sum(100, 250); // 输出:100 + 250 = 350
重构价值:相比之前 “硬编码 num1=10、num2=20” 的函数,多参数设计让函数可计算任意两个数字的和,从 “专用工具” 变为 “通用加法器”。
4.2.4 练习 4:多参数函数 —— 打印个人信息(printInfo 函数重构)
需求:重构之前 “只能打印固定信息” 的printInfo
函数,通过传入 “姓名、年龄、身高” 三个参数,实现打印不同人的信息。 代码示例:
// 重构后的函数:三个形参,接收不同人的信息 function printInfo(name, age, height) {console.log("=== 个人信息 ===");console.log(`姓名:${name}`);console.log(`年龄:${age}岁`);console.log(`身高:${height}米`);console.log("================="); } // 调用函数:传入不同人的信息 printInfo("why", 18, 1.88); // 输出:=== 个人信息 === → 姓名:why → 年龄:18岁 → 身高:1.88米 → ================= printInfo("小明", 20, 1.75); // 输出:=== 个人信息 === → 姓名:小明 → 年龄:20岁 → 身高:1.75米 → =================
关键改进:通过参数替换硬编码值,函数从 “只能打印 why 的信息” 变为 “可打印任意人的信息”,复用性大幅提升,后续如需新增人物信息,只需新增一次调用即可。
4.3 知识小结:函数参数的核心考点与难度梳理
知识点 | 核心内容 | 考试重点 / 易混淆点 | 难度系数 | ||
---|---|---|---|---|---|
函数声明与调用 | 基础语法:function 函数名(形参){...} ;调用:函数名(实参) | 函数名命名规范(动词开头);调用时不可省略() ;形参未赋值时为undefined | ⭐⭐ | ||
函数参数概念 | 形参(定义时的占位符)与实参(调用时的具体值);按顺序匹配 | 形参与实参的数量不匹配问题(实参少则形参为undefined ,实参多则多余值不赋值);“parameter” 与 “argument” 的英文对应关系 | ⭐⭐⭐ | ||
参数化函数改造 | 将函数内的固定值抽取为形参,提升函数通用性;如 sum 函数从固定 10+20 改为通用加法 | 参数抽取的边界判断(哪些值适合作为参数,哪些适合保留为固定值);默认值的简要理解(后续深入) | ⭐⭐⭐⭐ | ||
字符串拼接技巧 | 使用模板字符串(${变量} )拼接动态内容,替代传统+ 拼接 | 模板字符串与传统拼接的区别(模板字符串支持换行、直接嵌入变量);性能差异(现代浏览器中差异极小,优先考虑可读性) | ⭐⭐ | ||
函数重构案例(printInfo) | 从固定输出改造为多参数动态输出,实现打印任意人的信息 | 多参数传递时的顺序对应(实参顺序必须与形参一致,否则信息错位);参数类型的隐性要求(如年龄需传数字) | ⭐⭐⭐ | ||
数学运算函数(sum) | 多参数实现通用加法,计算任意两个数字的和 | 参数类型校验(如实参传字符串 “10” 与数字 20,会触发字符串拼接而非加法);隐式类型转换对结果的影响 | ⭐⭐⭐ | ||
单参数函数(sayHello/singBirthdaySong) | 单形参实现个性化问候 / 祝福,适配不同输入 | 单参数的使用场景边界(适合仅需一个动态值的场景);参数名的语义化(如用name 而非x ) | ⭐⭐ | ||
ES6 默认参数(简要提及) | 基础语法:function fn(a=10){...} ,为形参设置默认值 | 兼容性考虑(IE 不支持,需用 `a = a | 10替代);默认值的生效时机(实参为 undefined` 时触发) | ⭐⭐⭐⭐ |
4.4 实战建议:函数参数的使用规范
-
参数名语义化:形参名需体现数据含义(如用
name
而非n
,age
而非a
),提升代码可读性; -
控制参数数量:单个函数的参数建议不超过 3-4 个,过多参数会导致调用时易混淆顺序,可通过对象(后续学习)整合多个相关参数;
-
明确参数类型:在函数内可添加简单的类型校验(如
if (typeof num1 !== "number") { alert("请传入数字"); }
),避免因参数类型错误导致逻辑异常; -
优先使用模板字符串:拼接动态内容时,优先用模板字符串(
${变量}
),替代传统+
拼接(如"Hello, " + name + "!"
),减少语法错误(如遗漏引号、加号)。
5. JavaScript 函数返回值:概念、return 关键字与实战应用
函数返回值是函数与外部代码交互的核心桥梁 —— 通过返回值,函数可将内部处理结果传递给外部,供后续逻辑使用(如计算结果、格式化数据)。默认情况下,函数执行后返回undefined
,若需返回特定结果,需通过return
关键字显式定义。本节将从返回值的基础概念入手,详解return
的核心特性、注意事项及实战案例,帮助掌握函数 “输出结果” 的关键逻辑。
5.1 函数返回值的基础概念
函数返回值指 “函数执行完成后,传递给调用者的结果”,是函数实现 “数据输出” 的唯一方式,需先明确其默认行为与基础特征。
5.1.1 什么是函数返回值
函数的核心作用分为 “处理逻辑” 与 “输出结果”:
-
“处理逻辑”:函数体内的代码(如计算、打印、数据转换);
-
“输出结果”:函数执行后对外提供的结果,即返回值。 例如浏览器内置的
prompt()
函数,执行后会返回用户输入的字符串;自定义的 “计算和” 函数,执行后可返回两个数字的和,供外部变量接收或进一步运算。
5.1.2 函数的默认返回值:undefined
若函数未通过return
显式指定返回值,执行后默认返回undefined
(无论函数体内是否有其他逻辑,如打印、变量定义)。 示例 1:无 return 的函数返回 undefined
// 函数1:仅打印内容,无return function sayHello() {console.log("Hello World"); } // 调用函数并接收返回值 const foo = sayHello(); console.log(foo); // 输出:undefined(默认返回值) // 函数2:空函数(无任何逻辑) function emptyFunc() {} const bar = emptyFunc(); console.log(bar); // 输出:undefined(默认返回值)
5.1.3 基础示例:返回值与外部接收
通过return
指定返回值后,外部可通过 “变量赋值” 接收结果,实现 “函数内部逻辑” 与 “外部使用” 的联动。 示例:模板字符串拼接与返回值
// 函数:接收姓名,返回拼接后的问候语 function getGreeting(name) {// 通过return返回模板字符串结果return `Hello ${name}`; } // 外部接收返回值 const greeting = getGreeting("why"); console.log(greeting); // 输出:Hello why(使用返回值) // 进一步使用返回值(如插入DOM) document.body.textContent = greeting;
5.2 return 关键字的核心特性
return
是控制函数返回值与执行流程的关键关键字,兼具 “返回结果” 与 “终止函数” 两大功能,需重点掌握其使用规则。
5.2.1 return 的两大核心功能
-
功能 1:返回指定结果
return
后可跟任意类型的值(数字、字符串、变量、对象等),该值会作为函数的返回值传递给外部。 示例:返回变量或字面量:// 示例1:返回变量(两数之和) function sum(num1, num2) {const result = num1 + num2;return result; // 返回变量result } const total = sum(10, 20); console.log(total); // 输出:30 // 示例2:返回字面量(直接返回固定值) function getFixedValue() {return 123; // 返回数字字面量 } console.log(getFixedValue()); // 输出:123
-
功能 2:终止函数执行 函数执行到
return
语句时,会立即停止后续代码的执行(无论return
后是否有其他逻辑),即 “return
后的代码不会运行”。 示例:return 的中断效应function baz() {console.log("执行到return前");return "终止信号"; // 执行到此处,函数立即终止console.log("执行到return后"); // 该代码不会执行(被中断) } const result = baz(); console.log(result); // 输出:执行到return前 → 终止信号(return后的打印未执行)
5.2.2 return 的关键说明
关键点 | 具体规则与示例 |
---|---|
无值返回的 return | 若return 后不跟任何值(仅return; ),等效于return undefined ,函数返回undefined 。 示例:function fn() { return; } console.log(fn()); // undefined |
return 的位置限制 | return 仅能在函数体内使用,在函数体外使用会触发语法错误。 错误示例:return 123; // Uncaught SyntaxError: Illegal return statement |
返回值的类型灵活性 | return 可返回任意 JavaScript 类型(数字、字符串、数组、对象、甚至函数),无类型限制。 示例:function getObj() { return { name: "why" }; } console.log(getObj().name); // why |
5.3 函数返回值的三大注意事项
在使用函数返回值时,需关注 “默认行为”“中断效应”“显式与隐式返回的等效性”,避免因理解偏差导致逻辑错误。
5.3.1 注意事项一:所有无显式 return 的函数均返回 undefined
无论函数体内包含多少逻辑(如循环、条件判断、打印),只要未通过return
显式指定返回值,最终返回结果均为undefined
。 示例验证:
// 函数:包含条件判断与打印,无return function checkAge(age) {if (age >= 18) {console.log("成年人");} else {console.log("未成年人");} } const result = checkAge(20); console.log(result); // 输出:成年人 → undefined(无显式return,默认返回undefined)
5.3.2 注意事项二:显式返回 undefined 与默认行为等效
以下三种情况,函数返回值均为undefined
,效果完全一致:
-
函数体内无
return
语句; -
函数体内仅写
return;
(无后续值); -
函数体内写
return undefined;
(显式返回undefined
)。 示例对比:
// 情况1:无return function fn1() {} // 情况2:return后无值 function fn2() { return; } // 情况3:显式return undefined function fn3() { return undefined; } console.log(fn1()); // undefined console.log(fn2()); // undefined console.log(fn3()); // undefined(三者效果一致)
5.3.3 注意事项三:return 的中断效应会跳过后续代码
函数执行到return
时会立即终止,无论return
后是否有重要逻辑(如数据修改、打印、函数调用),均不会执行。开发中需避免将关键逻辑写在return
之后。 错误示例与修正:
// 错误示例:return后的console.log不会执行 function calculate(num1, num2) {const sum = num1 + num2;return sum; // 函数在此终止console.log("计算完成"); // 永远不会执行 } // 正确示例:将关键逻辑放在return之前 function calculate(num1, num2) {const sum = num1 + num2;console.log("计算完成"); // 先执行打印return sum; // 再返回结果 } calculate(10, 20); // 输出:计算完成 → 返回30
5.4 函数返回值的实战案例:sum 函数与作用域隔离
通过 “计算两数之和” 的实战案例,进一步理解返回值的使用场景,以及函数 “作用域隔离” 的特性(函数内外同名变量互不影响)。
5.4.1 案例需求
定义sum
函数,接收两个数字参数,计算它们的和并通过return
返回结果;外部通过变量接收返回值,并打印结果。
5.4.2 代码实现
// 1. 定义sum函数:接收参数,返回两数之和 function sum(num1, num2) {// 函数内部变量result(局部变量)const result = num1 + num2;return result; // 返回计算结果 } // 2. 外部使用:接收返回值,定义同名变量result(全局变量) const result = sum(15, 25); // 接收返回值30 console.log("两数之和:", result); // 输出:两数之和:30 // 3. 验证作用域隔离:函数内外同名变量互不影响 function testScope() {const message = "函数内部变量";return message; } const message = "函数外部变量"; console.log(testScope()); // 输出:函数内部变量(返回内部变量) console.log(message); // 输出:函数外部变量(外部变量未被修改)
5.4.3 案例关键结论
-
返回值的接收与使用:外部通过 “变量 = 函数调用” 的形式接收返回值,后续可用于打印、运算、DOM 操作等场景;
-
作用域隔离:函数内部定义的变量(如
sum
中的result
)仅在函数体内有效,与外部同名变量(如全局result
)互不干扰,这是函数 “封装性” 的重要体现; -
返回值的灵活性:
return
可直接返回计算结果(如
return num1 + num2
),无需额外定义局部变量,简化代码:
// 简化版sum函数:直接返回计算结果 function sum(num1, num2) {return num1 + num2; // 无需定义result变量 }
5.5 知识小结:函数返回值的核心考点
知识点 | 核心内容 | 考试重点 / 易混淆点 | 难度系数 |
---|---|---|---|
函数返回值基础概念 | 函数执行后传递给外部的结果;默认返回undefined ,显式返回需用return | 无return 的函数与return; 的函数返回值一致(均为undefined );返回值与 “打印” 的区别(打印是输出到控制台,返回值是传递给外部) | ⭐⭐ |
return 关键字的功能 | 两大功能:返回指定值、终止函数执行;可返回任意类型数据 | return 后的代码不执行(中断效应);return 在函数体外使用会报错;return 后无值等效于return undefined | ⭐⭐⭐ |
函数返回值的注意事项 | 无显式 return 则返回undefined ;显式返回undefined 与默认行为等效;return 中断函数 | 误将关键逻辑写在return 之后;混淆 “返回值” 与 “控制台输出”(如认为console.log 会影响返回值) | ⭐⭐⭐ |
实战案例(sum 函数) | 接收参数计算和,返回结果;外部接收并使用;函数内外变量作用域隔离 | 作用域隔离特性(同名变量互不影响);简化写法(直接返回计算结果,无需局部变量) | ⭐⭐ |
返回值的应用场景 | 传递计算结果、格式化数据(如formatCount 函数)、提供判断依据 | 不同类型返回值的使用(如返回对象时,外部可直接访问对象属性);返回值的链式使用(如sum(10,20) + 5 ) | ⭐⭐⭐ |
6. JavaScript 函数实战:数字格式化练习与函数间调用解析
在实际开发中,函数不仅用于封装基础逻辑,还需解决具体业务场景(如数字格式化),同时涉及函数间的协作调用。本节将围绕 “数字格式化” 这一高频需求,详解函数的实战实现、调试技巧,同时梳理函数间调用的核心方法与注意事项,帮助将函数知识落地到实际业务中。
6.1 数字格式化的函数练习:从需求到实现
数字格式化是前端常见需求(如播放量、点赞数显示),核心是将大数字(如 5433322)转换为 “万 / 亿” 单位的易读文本,需通过函数封装转换逻辑,确保通用性与可维护性。
6.1.1 例题:数字格式化的应用场景与转换规则
数字格式化的核心目标是 “优化大数字的可读性”,需先明确需求背景、必要性与转换逻辑:
-
应用场景 常见于音乐平台(如网易云音乐播放量)、社交平台(点赞数)、电商平台(销量)等,例如将 “5433322 次播放” 显示为 “543 万次播放”。
-
必要性
-
直接显示大数字(如 8766633333)会导致界面拥挤,影响美观;
-
用户难以快速感知数字量级(如 “5433322” 需数位数判断是 “百万级”,而 “543 万” 可瞬间理解)。
-
-
核心转换规则
数字范围 转换方式 示例 小于 10 万(<100000) 直接显示原数字 13687 → 13687 10 万~10 亿(100000 ≤ 数字 < 1000000000) 转换为 “万” 单位,向下取整 5433322 → 543 万(5433322 ÷ 10000 = 543.3322,向下取整为 543) 大于等于 10 亿(≥1000000000) 转换为 “亿” 单位,向下取整 8766633333 → 87 亿(8766633333 ÷ 100000000 = 87.66633333,向下取整为 87)
6.1.2 数字格式化函数的实现与调试
基于转换规则,封装formatCount
函数,核心需解决 “量级判断”“单位计算”“结果拼接” 三大问题,同时通过调试技巧确保逻辑正确性。
-
函数实现步骤
-
步骤 1:处理数字量级判断:用
if-else
判断数字属于 “小于 10 万”“10 万~10 亿”“≥10 亿” 中的哪一类; -
步骤 2:单位计算与向下取整:用
Math.floor()
对 “数字 ÷ 单位基数”(10000 或 100000000)向下取整,避免小数; -
步骤 3:结果拼接:将取整后的数字与对应单位(空 /“万”/“亿”)拼接,返回最终字符串;
-
ES6 特性优化:用数字分隔符
_
(如10_0000
表示 10 万、10_0000_0000
表示 10 亿)提高代码可读性,避免因多位数导致的计数错误。
完整代码:
// 数字格式化函数:将大数字转换为"万/亿"单位的易读文本 function formatCount(count) {// 1. 小于10万:直接返回原数字(转为字符串,保持格式统一)if (count < 10_0000) {return count.toString();} // 2. 10万~10亿:转换为"万"单位else if (count < 10_0000_0000) {const result = Math.floor(count / 10_000); // 向下取整,避免小数return `${result}万`;} // 3. 大于等于10亿:转换为"亿"单位else {const result = Math.floor(count / 10_0000_0000);return `${result}亿`;} }
-
-
测试用例验证 通过典型案例测试函数逻辑,确保边界值(如 10 万、10 亿)处理正确:
// 测试用例1:小于10万 console.log(formatCount(13687)); // 输出:"13687" // 测试用例2:10万~10亿(边界值10万) console.log(formatCount(10_0000)); // 输出:"10万" console.log(formatCount(5433322)); // 输出:"543万" // 测试用例3:≥10亿(边界值10亿) console.log(formatCount(10_0000_0000)); // 输出:"10亿" console.log(formatCount(8766633333)); // 输出:"87亿"
-
调试技巧 若函数返回结果异常(如边界值处理错误),可通过以下方法排查:
-
断点调试:在函数内添加
debugger
语句,打开浏览器开发者工具(Sources 面板),逐步执行查看变量值变化:
function formatCount(count) {debugger; // 断点:执行到此时暂停,可查看count值、分支判断结果if (count < 10_0000) {return count.toString();} else if (count < 10_0000_0000) {const result = Math.floor(count / 10_000);return `${result}万`;} else {const result = Math.floor(count / 10_0000_0000);return `${result}亿`;} }
-
常见错误排查:
-
函数名拼写错误(如
formatCount
误写为fomartCount
),导致调用失败; -
单位基数计算错误(如将 “万” 的基数写为
1000
而非10000
); -
忘记返回结果(如漏写
return
,导致函数默认返回undefined
); -
未用
Math.floor()
取整,导致结果出现小数(如5433322 → 543.3322万
)。
-
-
6.2 函数之间的调用:协作逻辑与调试方法
在复杂业务中,单个函数无法完成所有需求,需多个函数协作(如 “获取原始数据→格式化数据→渲染界面”),需掌握函数间调用的执行顺序、调试技巧与注意事项。
6.2.1 函数之间的调用与调试方法
函数间调用的核心是 “一个函数内部调用另一个函数”,通过返回值传递数据,形成协作流程。以 “获取播放量→格式化→打印” 为例:
-
协作函数示例:
// 函数1:模拟从服务器获取原始播放量(模拟数据) function getRawPlayCount() {// 模拟服务器返回的大数字return 8766633333; } // 函数2:数字格式化函数(复用6.1.2中的formatCount) function formatCount(count) {if (count < 10_0000) {return count.toString();} else if (count < 10_0000_0000) {return `${Math.floor(count / 10_000)}万`;} else {return `${Math.floor(count / 10_0000_0000)}亿`;} } // 函数3:主函数(调用前两个函数,完成“获取→格式化→打印”流程) function showFormattedPlayCount() {// 步骤1:调用getRawPlayCount,获取原始数据const rawCount = getRawPlayCount();// 步骤2:调用formatCount,格式化数据(传递rawCount作为实参)const formattedCount = formatCount(rawCount);// 步骤3:打印结果console.log(`歌曲播放量:${formattedCount}次`); } // 执行主函数,触发整个流程 showFormattedPlayCount(); // 输出:"歌曲播放量:87亿次"
-
调试方法:查看调用栈(Call Stack) 函数间调用时,浏览器会记录 “函数调用的顺序”(即调用栈),可通过开发者工具排查执行顺序与变量值:
-
打开浏览器开发者工具(F12),切换到Sources面板;
-
在主函数
showFormattedPlayCount
内添加debugger
,执行函数; -
暂停后,查看右侧
Call Stack
面板,会显示函数调用顺序(从下到上为调用顺序):
-
showFormattedPlayCount
(主函数,当前执行); -
formatCount
(被showFormattedPlayCount
调用); -
getRawPlayCount
(被showFormattedPlayCount
调用);
-
-
点击调用栈中的函数,可查看该函数执行时的变量值(如
rawCount
、formattedCount
),快速定位错误。
-
6.2.2 函数间调用的注意事项
-
避免函数名与内部变量名重复 若函数名与内部变量名一致,会导致变量覆盖函数,无法正常调用。例如:
// 错误示例:函数名formatCount与内部变量名formatCount重复 function formatCount(count) {const formatCount = count / 10_000; // 变量覆盖函数return `${formatCount}万`; } formatCount(5433322); // 报错:formatCount is not a function(变量覆盖后,formatCount变为数字,非函数)
修正:变量名需与函数名区分(如
const formattedNum = count / 10_000
)。 -
确保所有分支都有返回值 若函数存在多分支(如
if-else
),需确保每个分支都有return
,避免部分场景下函数默认返回undefined
。例如:// 错误示例:缺少“小于10万”分支的return function formatCount(count) {if (count < 10_0000) {count.toString(); // 漏写return,该分支无返回值} else if (count < 10_0000_0000) {return `${Math.floor(count / 10_000)}万`;} } console.log(formatCount(13687)); // 输出:undefined(无返回值)
修正:所有分支必须添加
return
(如return count.toString()
)。 -
注意大型数字的范围限制 JavaScript 中数字有最大安全值(
Number.MAX_SAFE_INTEGER = 9007199254740991
,约 9007 亿),若处理的数字超过该值(如 1 万亿),会导致精度丢失,需用BigInt
类型处理:// 处理超大数字(如1.2万亿),用BigInt类型 function formatBigCount(count) {// 转换为BigInt,避免精度丢失const bigCount = BigInt(count);if (bigCount < 10_0000n) { // 数字后加n,表示BigIntreturn bigCount.toString();} else if (bigCount < 10_0000_0000n) {return `${bigCount / 10_000n}万`;} else {return `${bigCount / 10_0000_0000n}亿`;} } console.log(formatBigCount("1200000000000")); // 输出:"1200亿"
6.3 知识小结:函数实战的核心考点与难度梳理
知识点 | 核心内容 | 考试重点 / 易混淆点 | 难度系数 |
---|---|---|---|
数字格式化函数 | 封装formatCount 函数,将大数字按 “10 万 / 10 亿” 分界,转换为 “万 / 亿” 单位文本 | 边界值处理(如 10 万、10 亿的判断条件是否正确);Math.floor() 的作用(避免小数) | ⭐⭐ |
工具函数封装 | 按 “单一职责” 原则封装通用函数(如数字格式化),提高复用性与维护性 | 函数命名规范(如formatCount 体现 “功能 + 数据类型”);函数内外变量的作用域隔离 | ⭐⭐ |
数字类型处理 | 使用 ES6 数字分隔符_ 提高大数字可读性;超大数字用BigInt 避免精度丢失 | 数字分隔符的语法(如10_0000 而非100000 );Math.floor() 与Math.round() 的区别(向下取整 vs 四舍五入) | ⭐ |
实际应用场景 | 前端显示优化(播放量、销量等),将服务器返回的原始大数字转换为易读格式 | 服务器原始数据(数字类型)与前端显示格式(字符串类型)的转换逻辑;用户体验优先的设计思路 | ⭐⭐⭐ |
调试技巧 | 用debugger 断点调试,查看调用栈与变量值;排查函数名拼写、返回值缺失等错误 | 调用栈的解读(从下到上为调用顺序);变量覆盖函数的错误排查(函数名与变量名重复) | ⭐⭐⭐ |
工程化思维 | 通用工具函数的封装原则(单一职责、无副作用);函数间协作的流程设计 | 函数纯度(输入相同则输出相同,无副作用);避免函数间过度耦合(如减少硬编码依赖) | ⭐⭐⭐⭐ |
7. JavaScript 函数的 arguments 对象:特性、应用与 ES6 替代方案
在 JavaScript 函数中,arguments
是一个特殊的内置对象,用于存储函数调用时传入的所有实参,尤其适用于处理 “参数数量不确定” 的场景(如不定参求和)。但随着 ES6 的普及,arguments
逐渐被更灵活的剩余参数替代,需明确其特性、使用限制与过渡方案。本节将从arguments
的基础特性入手,详解其使用方法与实战案例,同时对比 ES6 替代方案,帮助掌握函数参数的灵活处理技巧。
7.1 arguments 参数的基础特性
arguments
并非普通变量,而是函数内部默认存在的 “类数组对象”,仅在非箭头函数中可用,核心特性围绕 “参数存储、类型本质、额外参数获取” 展开。
7.1.1 核心特性解析
特性类别 | 具体说明 | 示例验证 |
---|---|---|
特殊对象属性 | 是函数内部的局部变量,仅在函数体内生效,全局作用域中无法访问 | 全局打印console.log(arguments) → 报错(未定义) |
参数存储规则 | 按函数调用时的实参顺序存储,索引从 0 开始(如第一个实参存于arguments[0] ) | 调用sum(10,20,30) → arguments[0]=10 ,arguments[1]=20 |
类数组(array-like)本质 | 属于object 类型,具备数组的 “索引访问” 和 “length 属性”,但无数组的内置方法(如push 、forEach ) | typeof arguments → object ;arguments.forEach → undefined |
额外参数捕获 | 无论函数是否定义形参,或实参数量超过形参数量,所有实参都会存入arguments | 函数function sum(a,b){} 调用sum(10,20,30) → arguments[2]=30 (捕获额外实参) |
7.1.2 关键注意事项
-
作用域限制:仅在非箭头函数中存在,箭头函数没有
arguments
对象(需用剩余参数替代); -
动态关联:若函数定义了形参,
arguments
的索引值与形参值会 “动态同步”(修改形参值会影响
arguments
,反之亦然),但在严格模式(
'use strict'
)下此关联会失效;
// 非严格模式:形参与arguments动态同步 function test(a) {a = 20;console.log(arguments[0]); // 输出20(形参修改影响arguments)arguments[0] = 30;console.log(a); // 输出30(arguments修改影响形参) } test(10); // 严格模式:关联失效 function testStrict(a) {'use strict';a = 20;console.log(arguments[0]); // 输出10(形参修改不影响arguments) } testStrict(10);
-
长度属性(length):
arguments.length
表示函数调用时实际传入的实参数量,与函数定义的形参数量无关;
function fn(a, b) {console.log(arguments.length); // 输出实参数量,而非形参数量 } fn(10); // 实参1个 → 输出1 fn(10, 20, 30); // 实参3个 → 输出3
7.2 函数中 arguments 变量的使用方法
arguments
的核心价值是 “灵活处理不确定数量的参数”,需掌握其访问方式、遍历方法与使用场景。
7.2.1 基础使用:访问与遍历
-
索引访问:通过
arguments[索引]
获取单个实参,索引从 0 开始,上限为arguments.length - 1
;function showArgs() {console.log("第一个参数:", arguments[0]);console.log("第二个参数:", arguments[1]);console.log("参数总数:", arguments.length); } showArgs("a", 123, true); // 输出:第一个参数:a → 第二个参数:123 → 参数总数:3
-
遍历参数:因
arguments
是类数组,无法直接使用forEach
等数组方法,需通过for
循环遍历(基于length
属性);function logAllArgs() {// for循环遍历所有参数for (let i = 0; i < arguments.length; i++) {console.log(`第${i+1}个参数:`, arguments[i]);} } logAllArgs("apple", "banana", "orange"); // 输出:第1个参数:apple → 第2个参数:banana → 第3个参数:orange
7.2.2 核心特点总结
-
自动包含所有实参:无论函数是否定义形参(如
function fn(){}
),调用时传入的所有实参都会被arguments
捕获; -
与形参独立:即使形参数量少于实参,额外的实参仍可通过
arguments
访问(如function fn(a){}
调用fn(1,2)
,arguments[1]
可获取 2); -
局部性:仅在函数内部可见,函数外部无法访问,避免全局变量污染。
7.3 arguments 的实战案例:不定参求和
arguments
最典型的应用是 “处理参数数量不确定的场景”,以 “不定参求和” 为例,详解其实现逻辑与注意事项。
7.3.1 案例需求
定义一个函数sum
,可接收任意数量的数字参数(如 1 个、2 个、5 个),返回所有参数的总和。
7.3.2 实现步骤
-
不定义具体形参:函数无需预先声明形参(或声明任意数量形参),通过
arguments
获取所有实参; -
遍历 arguments 累加:用
for
循环遍历arguments
,将每个参数转为数字(避免非数字类型影响结果)后累加; -
返回累加结果:通过
return
将总和返回给外部。
7.3.3 完整代码
// 不定参求和函数:接收任意数量的数字参数,返回总和 function sum() {let total = 0; // 初始化总和为0// 遍历arguments,累加所有参数for (let i = 0; i < arguments.length; i++) {// 将参数转为数字,避免字符串拼接等异常const num = Number(arguments[i]);// 若转换后是有效数字,才累加(过滤NaN)if (!isNaN(num)) {total += num;}}return total; // 返回总和 } // 测试用例 console.log(sum(10, 20)); // 2个参数 → 输出30 console.log(sum(5, 10, 15, 20)); // 4个参数 → 输出50 console.log(sum(2, 4, 6, 8, 10)); // 5个参数 → 输出30 console.log(sum(10, "20", 30)); // 含字符串参数 → 转为数字后累加,输出60
7.3.4 注意事项
-
类型转换:
arguments
存储的是实参的原始类型(如字符串 “20”),需手动转为目标类型(如Number()
转数字),避免逻辑错误(如字符串拼接而非加法); -
数组方法限制:
arguments
无法直接使用
forEach
、
reduce
等数组方法,若需使用,需先将其转为真正的数组(如
const args = Array.from(arguments)
);
// 转为数组后使用reduce求和 function sumWithReduce() {const args = Array.from(arguments); // 类数组转数组return args.reduce((total, curr) => {const num = Number(curr);return !isNaN(num) ? total + num : total;}, 0); }
-
ES6 替代方案:ES6 引入的 “剩余参数(
...args
)” 可直接接收不定数量的参数并转为数组,语法更简洁,推荐优先使用(见 7.4)。
7.4 知识小结:arguments 的核心考点与 ES6 过渡
知识点 | 核心内容 | 考试重点 / 易混淆点 | 难度系数 |
---|---|---|---|
arguments 对象基础 | 函数内部的类数组对象,存储所有实参;仅非箭头函数可用,全局不可访问 | arguments 的类型(object 而非array );与形参的动态关联(严格模式下失效) | ⭐⭐⭐ |
函数参数传递机制 | 形参仅接收部分实参,arguments 捕获所有实参;arguments.length 表示实参数量 | 形参数量与arguments.length 无必然关联(实参可多于 / 少于形参);额外实参的获取方式 | ⭐⭐ |
类数组特性 | 具备索引访问和length 属性,无数组内置方法(push /forEach 等) | 类数组与真实数组的区别(typeof 结果、方法可用性);类数组转数组的方法(Array.from() ) | ⭐⭐⭐⭐ |
不定参处理案例 | 通过arguments 实现不定参求和,需遍历累加并处理类型转换 | 非数字参数的过滤(isNaN 判断);arguments 遍历与数组遍历的差异 | ⭐⭐⭐ |
作用域预告 | arguments 是函数局部变量,体现 “函数作用域” 与 “全局作用域” 的隔离 | 箭头函数无arguments 的原因;函数内外变量的访问权限差异 | ⭐⭐⭐ |
ES6 替代方案(剩余参数) | 剩余参数...args 接收不定参并转为数组,语法简洁,支持数组方法 | arguments 与剩余参数的区别(类数组 vs 数组、兼容性);剩余参数的使用场景(优先于arguments ) | ⭐⭐⭐⭐ |
7.5 实战建议:arguments 与剩余参数的选择
-
兼容性场景:若需兼容 ES5 及以下环境(如 IE 浏览器),可使用
arguments
; -
现代开发场景:优先使用 ES6 剩余参数(
...args
),原因如下:
-
语法更直观:
function sum(...args) {}
直接表明 “接收不定参”,可读性更高; -
天然是数组:
args
是真正的数组,可直接使用forEach
、reduce
等方法,无需手动转换; -
箭头函数支持:箭头函数无
arguments
,但可使用剩余参数(如const sum = (...args) => args.reduce(...)
);
-
-
特殊需求:若需获取 “形参与
arguments
的动态关联”(非严格模式),可临时使用arguments
,否则优先剩余参数。
8. JavaScript 作用域基础:概念、类型与 ES5 特性解析
作用域是 JavaScript 中变量访问规则的核心概念,决定了变量(或标识符)的有效访问范围。ES5 及之前的版本中,作用域的特性与现代 ES6 存在显著差异(如无块级作用域),理解这些基础规则是掌握变量管理的关键。本节将从作用域的基本概念入手,详解全局作用域、函数作用域的特性,以及 ES5 中 “无块级作用域” 的特殊表现。
8.1 作用域的基本概念与全局作用域
作用域的核心是 “变量可被访问的有效范围”,其中全局作用域是范围最广的一种,需明确其定义、访问规则与特殊现象。
8.1.1 作用域的定义与全局作用域
-
核心定义:作用域(scope)指变量或标识符能够被访问的有效范围,超出该范围则无法引用。
-
全局作用域:在
<script>
标签内直接定义的变量(未嵌套在任何函数或代码块中)属于全局作用域,具备以下特点:
-
访问范围:在脚本的任何位置(包括函数内部、条件语句块、循环块等)均可访问;
-
示例验证:
// 全局变量:在全局作用域定义 var message = "Hello World"; // 在if语句块中访问全局变量 if (true) {console.log(message); // 输出:Hello World(可访问) } // 在函数内部访问全局变量 function foo() {console.log(message); // 输出:Hello World(可访问) } foo();
-
8.1.2 作用域的 “变量提升” 现象
ES5 中存在 “变量提升” 机制,导致 “变量在定义前可被访问” 的特殊表现:
-
现象:在变量定义语句之前访问变量,不会报错,而是返回
undefined
(而非未定义错误);
console.log(message); // 输出:undefined(未报错) var message = "Hello World"; // 变量定义
-
当前理解要点:只要变量在脚本中被定义(无论定义位置),其作用域会覆盖定义后的所有代码(包括函数、条件块等);
-
术语与背景:作用域对应的英文为 “scope”;ES5 及之前的版本中,变量提升是作用域的重要特性,后续课程会深入解析其原理。
8.2 ES5 之前的 “无块级作用域” 特性
ES5 及之前的版本中,使用var
定义的变量不具备 “块级作用域”(即代码块{}
不会形成独立作用域),这是与现代 ES6 差异的核心点。
8.2.1 块级作用域的缺失:var
与代码块的关系
-
核心表现:用
var
定义的变量,其作用域不受代码块
{}
限制(无论if、for、while等代码块),在块外仍可访问;
// if代码块内用var定义变量 if (true) {var blockVar = "我在if块内定义"; } console.log(blockVar); // 输出:我在if块内定义(块外可访问) // for循环块内用var定义变量 for (var i = 0; i < 3; i++) {} console.log(i); // 输出:3(循环块外可访问,值为循环结束后的值)
-
历史背景:ES5 及之前只有
var
一种变量声明方式,而var
不支持块级作用域,因此可认为 “ES5 之前没有块级作用域的概念”。
8.2.2 控制语句中的作用域表现
所有控制语句(if
、for
、while
等)的代码块均不会形成独立作用域,变量作用域穿透代码块:
-
for 循环示例:循环变量
i
在循环结束后仍可访问,且值为循环终止时的结果; -
if 语句示例:条件块内定义的变量,在条件外仍可访问(如上述
blockVar
案例); -
结论:ES5 中,代码块
{}
仅用于逻辑分组,不影响变量的作用域范围。
8.3 函数作用域:ES5 中唯一的 “局部作用域”
在 ES5 及之前,函数是唯一能形成 “局部作用域” 的结构 —— 函数内部定义的变量仅在函数内可访问,外部无法引用,这是与代码块的核心区别。
8.3.1 函数作用域的基本特性
-
核心表现:函数内部用
var
定义的变量属于 “函数作用域”,仅在函数体内有效,函数外部无法访问;
function test() {var innerVar = "函数内部变量";console.log(innerVar); // 输出:函数内部变量(函数内可访问) } test(); console.log(innerVar); // 报错:innerVar is not defined(外部不可访问)
8.3.2 嵌套函数的作用域规则
当函数嵌套时(内部函数定义在外部函数内),作用域遵循 “内层可访问外层,外层不可访问内层” 的规则:
-
内层函数访问外层变量:内部函数可访问外部函数的变量(闭包的基础);
-
外层无法访问内层变量:外部函数(或全局)无法访问内部函数的变量;
function outer() {var outerVar = "外层变量"; // 嵌套的内部函数function inner() {var innerVar = "内层变量";console.log(outerVar); // 输出:外层变量(内层可访问外层)} inner();console.log(innerVar); // 报错:innerVar is not defined(外层不可访问内层) } outer();
8.4 知识小结:ES5 作用域的核心考点
知识点 | 核心内容 | 考试重点 / 易混淆点 | 难度系数 |
---|---|---|---|
作用域基本概念 | 变量的有效访问范围(scope);全局变量在整个脚本范围内可访问 | 全局作用域与函数作用域的边界;变量 “可访问” 与 “已定义” 的区别(如变量提升导致的 undefined) | ⭐⭐ |
ES5 的无块级作用域 | var 定义的变量不受代码块{} 限制,if /for 块内变量在块外可访问 | for 循环中变量i 的 “泄漏” 问题(循环结束后仍可访问);var 与后续let 的块级作用域差异(预告) | ⭐⭐⭐ |
函数作用域 | 函数内部形成独立作用域,变量仅内部可访问;嵌套函数可访问外层变量 | 函数内外变量的访问权限(外层不可访问内层);函数作用域与全局作用域的嵌套关系 | ⭐⭐ |
变量提升现象 | 变量定义前访问返回undefined ,而非报错 | 变量 “声明提升” 与 “赋值不提升” 的区别(如console.log(a); var a=1; 中a 声明被提升,赋值未提升) | ⭐⭐⭐⭐ |
ES6 块级作用域(预告) | let /const 引入块级作用域,解决var 的作用域穿透问题 | var 与let 在 for 循环中的表现对比(如let 定义的i 不会泄漏到循环外) | ⭐⭐⭐ |
8.5 核心结论
-
作用域的本质是 “变量的有效访问范围”,决定了变量在何处可被引用;
-
ES5 及之前:
-
只有 “全局作用域” 和 “函数作用域”,无块级作用域;
-
var
定义的变量不受代码块限制,但受函数边界限制; -
函数是唯一能创建局部作用域的结构,嵌套函数可访问外层变量。
-
-
理解这些特性是后续学习 ES6 块级作用域、闭包等概念的基础。
9. JavaScript 变量类型与访问规则:全局、局部变量及作用域链
变量是 JavaScript 中数据存储的基本单元,其可访问范围与查找顺序由作用域规则决定。本节将系统梳理全局变量、局部变量、外部变量的核心概念,详解变量访问时的 “作用域链” 规则,帮助建立变量管理的清晰认知。
9.1 全局变量、局部变量与外部变量的概念
根据变量的定义位置与作用域范围,可将变量划分为三类:全局变量、局部变量、外部变量,三者的核心特征与访问范围存在显著差异。
9.1.1 全局变量(Global Variable)
-
定义位置:在
<script>
标签最顶层(所有函数外部)定义的变量; -
核心特征:
-
访问范围:在代码的任何位置均可访问,包括函数内部、嵌套函数、条件语句块、循环块等;
-
示例:
// 全局变量:定义在所有函数外部 var message = "hello world"; function sayHello() {console.log(message); // 可访问全局变量 → 输出:hello world } function outer() {function inner() {console.log(message); // 嵌套函数中仍可访问 → 输出:hello world}inner(); } outer();
-
特殊机制:全局变量默认会被添加到
window
对象(如window.message
可获取全局变量值),具体原理后续课程详解; -
注意事项:若使用
var
声明,在变量定义前访问会触发 “变量提升”(返回undefined
);若使用let
声明,提前访问会直接报错(块级作用域特性)。
-
9.1.2 局部变量(Local Variable)
-
定义位置:在函数内部定义的变量;
-
核心特征:
-
作用域限制:仅能在定义它的函数内部被访问,函数外部(包括全局或其他函数)无法引用;
-
示例:
function sayHello() {// 局部变量:仅在sayHello函数内有效var nickname = "小明";console.log(nickname); // 输出:小明(函数内可访问) } sayHello(); console.log(nickname); // 报错:nickname is not defined(外部不可访问)
-
9.1.3 外部变量(Outer Variable)
-
定义:函数内部访问的、非自身定义的变量(即不属于当前函数作用域的变量);
-
判定标准:
-
若函数访问的变量是 “外层函数定义的变量”(如嵌套函数访问外层函数的变量),属于外部变量;
-
若函数访问的变量是 “全局变量”(函数外部定义的变量),也属于外部变量;
-
-
示例:
// 全局变量(对所有函数而言,若未重新定义则为外部变量) var globalVar = "全局变量"; function outer() {// 外层函数变量(对inner而言是外部变量)var outerVar = "外层变量"; function inner() {// 访问外部变量:outerVar(外层函数变量)和globalVar(全局变量)console.log(outerVar); // 输出:外层变量console.log(globalVar); // 输出:全局变量}inner(); } outer();
9.1.4 三类变量的对比总结
变量类型 | 定义位置 | 访问范围 | 英文术语 |
---|---|---|---|
全局变量 | <script> 顶层 / 函数外部 | 整个程序(所有函数、代码块内) | Global Variable |
局部变量 | 函数内部 | 仅限定义它的函数内部 | Local Variable |
外部变量 | 非当前函数作用域(外层函数或全局) | 被当前函数访问(作为外部依赖) | Outer Variable |
9.2 变量的访问顺序:作用域链规则
当函数访问变量时,并非随机查找,而是遵循 “作用域链” 规则 —— 从当前作用域开始,逐层向上查找,直至找到变量或确定不存在。
9.2.1 基本访问规则
变量查找顺序为:
-
优先查找当前函数作用域:先检查当前函数内部是否定义了该变量,若有则直接访问;
-
向上层作用域查找:若当前函数未定义,向上查找外层函数(若存在嵌套)的作用域;
-
查找全局作用域:若外层函数也未定义,继续查找全局作用域(
<script>
顶层定义的变量); -
检查 window 对象:若全局作用域仍未定义,检查
window
对象是否有该属性(全局变量默认挂载到window
); -
未找到则报错:若所有层级均未找到,触发
ReferenceError: 变量 is not defined
。
核心原则:“就近原则”—— 多层作用域存在同名变量时,优先访问 “最近作用域” 的变量。
9.2.2 例题分析:变量访问顺序的具体表现
例题 1:访问当前函数作用域的变量
function foo() {var message = "当前函数的局部变量";console.log(message); // 优先访问当前函数的变量 → 输出:当前函数的局部变量 } foo();
结论:当前函数定义了变量时,直接访问该局部变量。
例题 2:访问上层作用域的变量
function outer() {var message = "外层函数的变量";function inner() {// inner内部未定义message,向上查找外层函数console.log(message); // 输出:外层函数的变量}inner(); } outer();
变体:若外层变量未初始化(仅声明),访问结果为undefined
:
function outer() {var message; // 仅声明,未赋值function inner() {console.log(message); // 输出:undefined(找到变量但未初始化)}inner(); } outer();
例题 3:访问全局作用域的变量
// 全局变量 var message = "全局变量"; function outer() {function inner() {// 自身和外层函数均未定义message,查找全局console.log(message); // 输出:全局变量}inner(); } outer();
例题 4:访问 window 对象的变量(全局变量的特殊挂载)
// 全局变量默认挂载到window var message = "全局变量"; function foo() {console.log(window.message); // 输出:全局变量(通过window访问全局变量) } foo();
注意:若变量未定义,window.变量
会返回undefined
(而非报错):
function foo() {console.log(window.undefinedVar); // 输出:undefined(window无此属性)console.log(undefinedVar); // 报错:ReferenceError(未找到变量) } foo();
9.3 总结:变量访问的核心逻辑
-
变量按 “全局变量、局部变量、外部变量” 分类,核心区别在于定义位置与访问范围;
-
变量访问遵循 “作用域链” 规则:从当前函数作用域开始,逐层向上查找,直至
window
对象,未找到则报错; -
“就近原则” 是变量访问的关键:同名变量时,优先选择最近作用域的定义。
这些规则是理解 JavaScript 变量管理的基础,直接影响后续闭包、模块化等高级概念的学习,需重点掌握。
10. JavaScript 作用域与函数表达式:概念及差异解析
作用域决定了变量的访问范围,而函数作为 JavaScript 中的特殊值,存在函数声明与函数表达式两种定义方式,二者在创建时机、调用规则上存在显著差异。本节将系统梳理作用域中的变量分类、访问规则,详解函数表达式的特性及其与函数声明的核心区别,构建对变量与函数定义的完整认知。
10.1 作用域与变量类型
作用域是标识符(变量、函数名等)的有效访问范围,ES5 及之前虽无块级作用域,但函数可形成独立作用域,变量按作用域范围可分为局部变量、外部变量与全局变量。
10.1.1 作用域的核心定义
-
基本概念:作用域指变量或函数名能够被访问的有效范围,超出该范围则无法引用;
-
ES5 特性:不存在块级作用域(代码块
{}
不限制变量访问),但函数可定义独立作用域(函数内部形成局部范围)。
10.1.2 变量类型及特征
-
局部变量
-
定义位置:函数内部;
-
访问范围:仅限定义它的函数内部,外部无法访问;
-
示例:
function foo() {var localVar = "局部变量"; // 局部变量console.log(localVar); // 输出:局部变量(函数内可访问) } foo(); console.log(localVar); // 报错:localVar is not defined(外部不可访问)
-
-
外部变量
-
定义:函数内部访问的、非自身定义的变量(即来自函数外部的变量);
-
范围:包括外层函数的变量或全局变量;
-
示例:
var globalVar = "全局变量"; // 全局变量(对所有函数是外部变量) function outer() {var outerVar = "外层变量"; // 对inner是外部变量function inner() {console.log(outerVar); // 访问外层函数的外部变量 → 输出:外层变量console.log(globalVar); // 访问全局的外部变量 → 输出:全局变量}inner(); } outer();
-
-
全局变量
-
定义位置:
<script>
标签内(所有函数外部); -
访问范围:在任何函数、代码块中均可访问;
-
特殊机制:通过
var
声明的全局变量会成为window
对象的属性(如window.globalVar
可访问); -
示例:
var globalVar = "全局变量"; function test() {console.log(globalVar); // 函数内可访问 → 输出:全局变量 } test(); console.log(window.globalVar); // 输出:全局变量(挂载到window)
-
10.1.3 变量访问顺序
遵循 “就近原则”:
-
优先访问当前函数内部的局部变量;
-
未找到则向上层作用域(外层函数)查找外部变量;
-
仍未找到则访问全局变量;
-
全局未定义则报错(
ReferenceError
)。
10.2 函数表达式:定义与特性
函数在 JavaScript 中是特殊的 “值”(类型为function
,本质是对象),除函数声明外,还可通过 “函数表达式” 定义,二者在语法与执行机制上存在差异。
10.2.1 函数表达式的定义
-
语法:将函数赋值给变量,即 “变量 = function (参数){...}”;
-
匿名特性:函数表达式可省略函数名(匿名函数),通过变量名调用;
-
示例:
// 函数表达式:将匿名函数赋值给变量foo var foo = function() {console.log("函数表达式"); }; // 通过变量名调用 foo(); // 输出:函数表达式
10.2.2 函数声明与函数表达式的区别
对比维度 | 函数声明(Function Declaration) | 函数表达式(Function Expression) |
---|---|---|
语法形式 | 独立语句:function 函数名(参数){...} | 表达式中定义:变量 = function(参数){...} |
创建时机 | JavaScript 预处理阶段(脚本运行前)创建 | 代码执行到定义行时才创建 |
调用时机 | 可在定义前调用(预处理机制) | 必须在定义后调用(否则变量为undefined ,调用报错) |
命名要求 | 必须有函数名(通过函数名调用) | 可省略函数名(通过变量名调用) |
-
示例:创建与调用时机差异
// 函数声明:可在定义前调用 testDeclaration(); // 输出:函数声明(预处理时已创建) function testDeclaration() {console.log("函数声明"); } // 函数表达式:定义前调用报错 testExpression(); // 报错:testExpression is not a function(未执行到定义行,变量为undefined) var testExpression = function() {console.log("函数表达式"); };
10.2.3 开发实践建议
-
优先使用函数声明:因其支持提前调用,灵活性更高;
-
函数表达式用于特殊场景:如作为参数传递(回调函数)、创建匿名函数等;
-
注意:函数表达式赋值后,变量存储的是函数引用,可像普通值一样传递。
10.3 知识小结:核心考点与难度梳理
知识点 | 核心内容 | 考试重点 / 易混淆点 | 难度系数 |
---|---|---|---|
作用域与变量类型 | 作用域是变量的有效范围;变量分局部(函数内)、外部(函数外)、全局(script 顶层) | 变量访问的 “就近原则”;全局变量挂载到window 的特性 | ⭐⭐ |
函数表达式 | 通过 “变量 = function (){...}” 定义,属于表达式语法;可匿名 | 与函数声明的语法差异;调用时机限制(定义后才能调用) | ⭐⭐⭐ |
函数的本质 | 函数是特殊的值(类型为function ),本质是对象 | 函数作为 “头等公民” 的特性(可赋值给变量、作为参数传递) | ⭐⭐ |
声明与表达式的差异 | 函数声明预处理时创建,可提前调用;表达式执行到定义行才创建,不可提前调用 | 二者创建时机的底层机制;提前调用表达式导致的报错原因(变量暂为undefined ) | ⭐⭐⭐⭐ |
开发实践 | 优先用函数声明,特殊场景用表达式;匿名函数是表达式的常见形式 | 函数表达式作为回调函数的应用(如setTimeout(函数表达式, 1000) ) | ⭐⭐ |
10.4 核心结论
-
作用域决定变量访问范围,变量按作用域分为局部、外部、全局,访问遵循 “就近原则”;
-
函数表达式是定义函数的重要方式,与函数声明的核心差异在于创建时机与调用限制;
-
理解二者特性有助于合理选择函数定义方式,避免因调用时机错误导致的逻辑问题。
11. JavaScript 中的头等函数与函数式编程
在 JavaScript 中,函数被视为 “头等公民”(First-Class Function),这意味着函数可像普通值一样被灵活操作(如作为参数、返回值、存储在变量或数据结构中)。这种特性是函数式编程的基础,使 JavaScript 具备高度灵活性。本节将详解头等函数的核心特性、函数式编程的概念及实践案例,理解这一特性对掌握 JavaScript 高级用法至关重要。
11.1 函数的头等公民特性
“头等函数” 指函数在编程语言中拥有与其他数据类型(如数字、字符串)同等的地位,可被任意操作。JavaScript 完全支持这一特性,具体表现为以下核心能力。
11.1.1 函数的表达式写法:赋值与传递
函数可被赋值给变量,且变量间可传递函数引用,这是头等函数最直接的体现。
-
基础示例:
// 函数表达式:将函数赋值给变量foo1 const foo1 = function() {console.log("执行函数"); }; foo1(); // 通过变量名调用 → 输出:执行函数 // 函数在变量间传递(赋值引用) const foo2 = foo1; // foo2与foo1指向同一个函数 foo2(); // 调用 → 输出:执行函数(与foo1效果一致)
-
本质:函数是特殊的 “值”,变量存储的是函数的引用,而非函数本身,因此可像普通值一样传递。
11.1.2 函数作为参数、返回值与存储载体
头等函数的核心特性还包括 “作为参数传递”“作为返回值”“存储在数据结构中”,这些能力是函数式编程的基础。
11.1.2.1 函数作为参数传递
函数可作为实参传入其他函数,被调用函数可通过形参执行传入的函数(即 “回调函数” 模式)。
-
示例:
// 定义一个接收函数作为参数的函数bar function bar(fn) {console.log("执行传入的函数:");fn(); // 执行传入的函数 } // 定义要传入的函数foo const foo = function() {console.log("我是被传入的函数"); }; // 调用bar,传入foo作为参数 bar(foo); // 输出:执行传入的函数:→ 我是被传入的函数
11.1.2.2 函数作为返回值
函数可作为另一个函数的返回值,外部通过调用返回的函数实现逻辑延迟执行(如柯里化)。
-
示例:
// 函数createHello返回一个新函数 function createHello(name) {// 返回一个匿名函数,引用外部变量namereturn function() {console.log(`hi, ${name}`);}; } // 调用createHello,获取返回的函数 const sayHiToCopy = createHello("copy"); sayHiToCopy(); // 执行返回的函数 → 输出:hi, copy
-
扩展:这种 “返回函数” 的模式称为柯里化(Currying),可实现参数复用与逻辑拆分,后续课程会深入讲解。
11.1.2.3 函数存储在数据结构中
函数可像普通值一样存储在对象、数组等数据结构中,通过访问数据结构调用函数。
-
对象中存储函数:
const person = {name: "小明",// 函数作为对象的方法存储eating: function() {console.log(`${this.name}在吃饭`);},running: function() {console.log(`${this.name}在跑步`);} }; // 调用对象中的函数 person.eating(); // 输出:小明在吃饭 person.running(); // 输出:小明在跑步
-
数组中存储函数:
// 数组中存储多个函数 const actionFunctions = [function() { console.log("动作1:跳"); },function() { console.log("动作2:跑"); },function() { console.log("动作3:走"); } ]; // 调用数组中的函数(通过索引访问) actionFunctions[0](); // 输出:动作1:跳 actionFunctions[2](); // 输出:动作3:走
11.2 函数式编程:理念与语言对比
函数式编程是一种以 “函数为核心” 的编程范式,JavaScript 因支持头等函数而天然适配这种范式。
11.2.1 函数式编程的核心特征
函数式编程的核心依赖于头等函数的特性,具体表现为:
-
函数可被任意传递、返回或存储,作为逻辑的基本单元;
-
支持高阶函数(接收或返回函数的函数);
-
鼓励使用匿名函数简化逻辑;
-
强调 “无副作用”(函数执行不修改外部状态)与 “纯函数”(输入相同则输出必相同)。
11.2.2 不同语言对函数式编程的支持对比
并非所有语言都原生支持函数式编程,对比不同语言的特性可更清晰理解 JavaScript 的优势:
语言 | 对函数式编程的支持情况 | 特点与限制 |
---|---|---|
JavaScript | 原生支持头等函数,完全适配函数式编程 | 灵活度高,函数可作为参数、返回值等,是语言核心特性之一 |
Java | 早期版本不支持(需通过内部类模拟),Java 8 引入 Lambda 表达式后有限支持 | 语法较繁琐,函数式编程并非核心设计,更多作为补充特性 |
Swift | 原生支持函数式编程,将其作为现代语言特性之一 | 设计时融入函数式思想,支持高阶函数、闭包等,灵活性接近 JavaScript |
Lisp | 函数式编程思想的起源语言(1958 年诞生) | 开创性提出函数作为头等公民的理念,影响后续所有支持函数式编程的语言 |
11.2.3 函数式编程思想的起源与影响
函数式编程思想源自 1958 年的 Lisp 语言,其核心理念是 “将计算视为函数的组合”。尽管 Lisp 较为古老,但其提出的 “函数作为头等公民”“递归” 等理念深刻影响了现代编程语言(包括 JavaScript、Swift、Python 等)。
在 JavaScript 中,函数式编程思想的应用极为广泛,例如:
-
数组方法(
map
、filter
、reduce
)均基于函数作为参数的模式; -
异步编程中的回调函数、Promise、async/await,本质依赖函数作为返回值或参数的特性;
-
状态管理(如 Redux)的设计理念也借鉴了函数式编程的 “纯函数” 思想。
11.3 知识小结:头等函数与函数式编程的核心考点
知识点 | 核心内容 | 考试重点 / 易混淆点 | 难度系数 |
---|---|---|---|
头等函数的特性 | 函数可作为参数、返回值、赋值给变量、存储在数据结构中 | 函数作为 “值” 的本质(变量存储引用);与非头等函数语言(如早期 Java)的区别 | ⭐⭐⭐ |
函数表达式与传递 | 函数表达式是头等函数的体现(赋值给变量);函数可在变量间传递 | 函数引用传递的特性(赋值后变量指向同一函数);函数表达式与函数声明的调用差异 | ⭐⭐ |
函数作为参数 / 返回值 | 函数作为参数实现回调;作为返回值实现延迟执行(如柯里化) | 回调函数的执行时机;返回函数对外部变量的引用(闭包基础) | ⭐⭐⭐ |
函数式编程的支持 | JavaScript 原生支持,依赖头等函数特性;不同语言的支持对比(Java/Swift/Lisp) | Java 与 JavaScript 在函数式编程支持上的差异;Lisp 作为思想起源的地位 | ⭐⭐ |
函数式编程的影响 | 影响数组方法、异步编程、状态管理等众多领域 | 函数式编程与命令式编程的区别(前者强调函数组合,后者强调步骤执行) | ⭐⭐⭐⭐ |
11.4 核心结论
-
JavaScript 中的函数是 “头等公民”,可作为参数、返回值、变量值或数据结构元素,具备高度灵活性;
-
这种特性使 JavaScript 天然支持函数式编程,是其区别于传统面向对象语言的重要优势;
-
理解头等函数的特性是掌握回调函数、柯里化、数组高阶方法等高级用法的基础,需重点掌握函数作为参数与返回值的实践场景。
12. JavaScript 函数式编程核心:头等函数与高阶函数应用
JavaScript 因支持 “头等函数” 特性,衍生出回调函数、高阶函数、匿名函数等重要概念,这些是函数式编程的核心组成部分,尤其在异步编程、逻辑抽象中发挥关键作用。本节将从头等函数的基础特性入手,详解回调函数的异步应用、高阶函数的判定与使用,以及匿名函数的简化逻辑,构建函数式编程的核心知识体系。
12.1 头等函数的特性与函数式编程基础
头等函数(First-Class Function)是 JavaScript 函数式编程的根基,指函数具备与普通值同等的地位,可被灵活操作。
12.1.1 头等函数的核心特性
函数作为 “头等公民”,需满足以下关键特性:
-
作为参数传递:函数可作为实参传入其他函数(如回调函数);
-
作为返回值:函数可被另一个函数返回(如柯里化场景);
-
赋值与存储:可赋值给变量,或存储在数组、对象等数据结构中;
-
支持匿名函数:允许定义无名称的函数,简化临时逻辑。
这些特性使 JavaScript 天然支持函数式编程范式 —— 以函数为核心构建逻辑,强调函数的组合与复用。
12.1.2 函数式编程的核心场景
函数式编程依赖头等函数特性,典型应用包括:
-
异步操作处理(通过回调函数获取异步结果);
-
数据处理(通过高阶函数如
map
、filter
抽象逻辑); -
逻辑复用(通过函数返回函数实现参数固化)。
12.2 回调函数:异步逻辑的核心机制
回调函数(Callback Function)是头等函数特性的典型应用,指 “被作为参数传递给另一个函数,并在特定时刻被调用的函数”,尤其适用于处理异步操作。
12.2.1 回调函数的定义与核心特征
-
核心定义:通过变量引用传递给其他函数,在非定义位置被调用的函数;
-
关键作用:解决异步操作的 “结果获取” 问题(如网络请求、定时器等耗时操作);
-
核心特征:
-
作为参数传递给高阶函数;
-
在特定时机(如异步操作完成后)被触发执行;
-
无需主动调用,由接收它的函数负责执行。
-
12.2.2 回调函数的实践案例
案例 1:模拟网络请求的回调处理
网络请求是典型的异步场景,无法通过return
直接获取结果,需通过回调函数传递:
// 模拟网络请求函数(高阶函数,接收回调函数) function request(url, callback) {// 模拟请求延迟(异步操作)setTimeout(() => {const result = ["JavaScript", "函数式编程", "回调函数"]; // 模拟请求结果callback(result); // 操作完成后调用回调,传递结果}, 1000); } // 定义处理结果的回调函数 function handleResult(res) {console.log("处理请求结果:", res); } // 调用请求函数,传入回调 request("https://example.com/data", handleResult); // 1秒后输出:处理请求结果:["JavaScript", "函数式编程", "回调函数"]
案例 2:回调函数的正确传递方式
传递回调函数时需注意 “引用传递” 与 “立即执行” 的区别:
-
错误写法:
request(url, handleResult())
—— 加括号会立即执行函数,传递的是返回值(而非函数引用); -
正确写法:
request(url, handleResult)
—— 传递函数名(引用),由request
在合适时机调用。
案例 3:匿名函数简化回调逻辑
对于仅使用一次的回调函数,可直接定义为匿名函数,简化代码:
// 匿名函数作为回调,无需单独定义handleResult request("https://example.com/data", function(res) {console.log("匿名函数处理结果:", res); }); // 效果与案例1一致,代码更简洁
12.3 高阶函数:接收或返回函数的函数
高阶函数(Higher-Order Function)是函数式编程的重要工具,指 “接收函数作为参数” 或 “返回函数” 的函数,是回调函数得以应用的基础。
12.3.1 高阶函数的判定标准
满足以下任一条件即为高阶函数:
-
接收函数作为参数(如
request
函数接收回调函数); -
返回一个函数(如柯里化函数返回新函数)。
12.3.2 典型示例
// 示例1:接收函数作为参数(高阶函数) function higherOrder1(fn) {console.log("执行传入的函数:");fn(); // 调用传入的函数 } // 调用:传入匿名函数作为参数 higherOrder1(function() {console.log("我是被传入的函数"); }); // 示例2:返回一个函数(高阶函数) function higherOrder2(msg) {// 返回一个新函数,引用外部变量msgreturn function() {console.log("返回的函数执行:", msg);}; } // 调用:获取返回的函数并执行 const returnedFn = higherOrder2("Hello"); returnedFn(); // 输出:返回的函数执行:Hello
12.3.3 内置高阶函数(预告)
JavaScript 数组方法(如map
、filter
、reduce
)均为高阶函数,后续课程会详解,例如:
// map是高阶函数,接收函数作为参数 const numbers = [1, 2, 3]; const doubled = numbers.map(function(n) { return n * 2; }); console.log(doubled); // 输出:[2,4,6]
12.4 匿名函数:简化回调的临时函数
匿名函数是 “无名称的函数表达式”,常与高阶函数、回调场景配合使用,简化代码结构。
12.4.1 匿名函数的定义与特点
-
定义:通过
function()
语法定义,无函数名; -
使用场景:
-
作为回调函数直接传递(无需重复使用);
-
临时逻辑处理(如一次性操作);
-
-
优势:减少命名冗余,使代码更紧凑;
-
局限:无法在定义外通过名称调用,不适合复用逻辑。
12.4.2 与函数声明的对比
类型 | 语法形式 | 适用场景 | 特点 |
---|---|---|---|
匿名函数 | function() {...} | 临时回调、一次性逻辑 | 简洁,无名称,不可复用 |
函数声明 | function 名称() {...} | 需多次调用、复用的逻辑 | 可通过名称调用,支持提升 |
12.5 知识小结:核心考点与难度梳理
知识点 | 核心内容 | 考试重点 / 易混淆点 | 难度系数 |
---|---|---|---|
头等函数 | 函数可作为参数、返回值或赋值给变量,支持函数式编程 | 区分 “函数引用传递”(foo(bar) )与 “函数立即执行”(foo(bar()) ) | ⭐⭐ |
回调函数 | 作为参数传递,在非定义位置被调用,用于异步操作 | 异步场景中 “无法通过 return 获取结果” 的原因;回调函数的执行时机 | ⭐⭐⭐ |
高阶函数 | 接收函数作为参数或返回函数(如request 、map ) | 识别高阶函数的判定标准;与普通函数的区别 | ⭐⭐ |
匿名函数 | 无名称的函数表达式,常用于回调场景 | 匿名函数与函数声明的适用场景;匿名函数无法通过名称复用的局限 | ⭐⭐ |
异步请求处理 | 通过回调函数获取异步操作结果(如模拟网络请求案例) | 理解异步执行的时序(回调函数后于主程序执行);与同步执行的区别 | ⭐⭐⭐⭐ |
12.6 核心结论
-
头等函数是 JavaScript 函数式编程的基础,使函数可作为参数、返回值等灵活操作;
-
回调函数是异步编程的核心机制,通过 “延迟调用” 解决异步结果获取问题;
-
高阶函数(接收或返回函数)是函数式编程的重要工具,支撑回调函数与逻辑抽象;
-
匿名函数简化了临时逻辑(如回调)的代码结构,是开发中的常用写法。
掌握这些概念是理解 JavaScript 异步编程、数组高阶方法等高级内容的关键。