JavaScript函数指南:从基础到进阶的实用教程
一、函数到底是什么
函数是JavaScript中最基础也最重要的概念之一。函数就是一段可以重复使用的代码块,用来完成特定的任务。它接收一些输入(参数),经过处理后返回一个输出(返回值)。有了函数,我们就能把复杂的大型程序拆分成一个个小而 manageable 的模块。
在的开发中,我见过太多人一开始就陷入复杂逻辑的泥潭,就是因为没有用好函数。把代码拆分成函数,就像整理房间一样,东西分门别类放好,找起来才方便。
二、声明函数的方式
2.1 函数声明(基本方式)
使用function关键字来声明函数,这是最传统也最常用的方式:
function 计算总和(数字1, 数字2) {
return 数字1 + 数字2;
}
let 结果 = 计算总和(15, 27);
console.log(结果); // 输出42
函数名的命名规则需要留意:
-
区分大小写(getData和getdata是两个不同的函数)
-
可以用字母、下划线_、美元符号$开头
-
不能包含空格
-
不能用JavaScript的保留字(如if、for、return等)
2.2 函数表达式
函数可以赋值给变量,这种方式叫函数表达式:
const 乘法计算 = function(数字1, 数字2) {
return 数字1 * 数字2;
};
let 乘积 = 乘法计算(6, 7);
console.log(乘积); // 输出42
个人经验分享:函数声明和函数表达式有个重要区别——函数声明会被提升(hoisting),可以在声明前调用;函数表达式不会提升,必须先定义后使用。早期我因为不了解这个区别,踩过不少坑:
// 这样可以
console.log(声明函数(5)); // 输出10
function 声明函数(x) {
return x * 2;
}
// 这样会报错
console.log(表达式函数(5)); // TypeError: 表达式函数 is not a function
const 表达式函数 = function(x) {
return x * 2;
};
三、函数的参数
函数可以接收零个或多个参数:
// 无参数
function 获取当前时间() {
return new Date().toLocaleDateString();
}
// 多个参数
function 创建用户(姓名, 年龄, 邮箱) {
return {
name: 姓名,
age: 年龄,
email: 邮箱,
createdAt: "2026-03-12"
};
}
let 新用户 = 创建用户("王小明", 28, "alan@ebingou.cn");
console.log(新用户);
四、函数的方法
JavaScript的函数有一些内置方法,可以用来控制函数的执行方式。
4.1 call()方法
call()方法可以指定函数内部的this指向,参数一个个传递:
function 介绍() {
console.log("我是" + this.姓名 + ",今年" + this.年龄 + "岁");
}
let 人员 = {
姓名: "李华",
年龄: 25
};
介绍.call(人员); // 输出: 我是李华,今年25岁
4.2 apply()方法
apply()和call()功能类似,区别在于参数以数组形式传递:
function 介绍(城市, 职业) {
console.log(`${this.姓名}来自${城市},职业是${职业}`);
}
let 用户 = { 姓名: "张明" };
介绍.apply(用户, ["上海", "前端开发"]);
// 输出: 张明来自上海,职业是前端开发
4.3 bind()方法
bind()不会立即执行函数,而是返回一个绑定了this的新函数:
function 打招呼() {
console.log("你好," + this.名字);
}
let 人物 = { 名字: "陈静" };
let 打招呼绑定 = 打招呼.bind(人物);
打招呼绑定(); // 输出: 你好,陈静
个人见解:这三个方法中,我平时用得最多的是bind(),特别是在React类组件中处理事件绑定。call()和apply()在需要临时改变this指向时很有用,但现在开发中很多场景已经被箭头函数替代了。
4.4 toString()方法
这个方法返回函数的源码字符串:
function 测试函数(a, b) {
return a + b;
}
console.log(测试函数.toString());
// 输出: function 测试函数(a, b) { return a + b; }
调试的时候偶尔会用上,但日常开发中用得不多。
五、函数的各种类型
5.1 箭头函数
ES6引入的箭头函数写法更简洁,而且不绑定自己的this:
// 传统写法
let 数字 = [1, 2, 3, 4, 5];
let 平方1 = 数字.map(function(项) {
return 项 * 项;
});
// 箭头函数写法
let 平方2 = 数字.map(项 => 项 * 项);
console.log(平方2); // [1, 4, 9, 16, 25]
为什么用箭头函数:
-
语法简洁,特别适合简单的回调函数
-
不创建自己的this,继承外层this,解决了传统函数this指向混乱的问题
-
不能用作构造函数,没有arguments对象
5.2 回调函数
把一个函数作为参数传给另一个函数,这个传进去的函数就是回调函数:
function 处理数据(数据, 回调) {
console.log("开始处理数据...");
let 处理结果 = 数据.toUpperCase();
回调(处理结果);
}
function 显示结果(内容) {
console.log("处理结果:", 内容);
}
处理数据("hello world", 显示结果);
// 输出:
// 开始处理数据...
// 处理结果: HELLO WORLD
在工作中回调函数无处不在,比如事件监听、定时器、异步请求等。
5.3 匿名函数
没有名字的函数就是匿名函数,作为临时使用的函数:
// 赋值给变量
let 打招呼 = function() {
console.log("你好啊");
};
// 作为回调
setTimeout(function() {
console.log("3秒后执行");
}, 3000);
// 立即执行
(function() {
console.log("定义完就执行");
})();
5.4 立即执行函数(IIFE)
定义完就立即执行的函数,主要用来创建独立作用域:
(function() {
let 私有变量 = "只有内部能访问";
console.log("IIFE执行了");
})();
// console.log(私有变量); // 报错,访问不到
个人经验:在ES6模块普及之前,IIFE是模拟模块化、避免变量污染全局环境的常用手段。现在有了let、const和模块系统,IIFE用得少了,但在一些老项目或特定场景下还是会遇到。
5.5 嵌套函数(闭包)
函数内部定义函数,内部函数可以访问外部函数的变量:
function 创建计数器(初始值) {
let 计数 = 初始值 || 0;
function 增加() {
计数 += 1;
return 计数;
}
return 增加;
}
let 我的计数器 = 创建计数器(10);
console.log(我的计数器()); // 11
console.log(我的计数器()); // 12
这就是闭包的典型例子——内部函数记住了它被创建时的环境变量。很多源码(https://www.ebingou.cn/yuanma/)里都用这种模式来封装私有状态。
六、函数的优势
6.1 代码复用
写一次,用多次:
function 计算折扣(价格, 折扣率) {
return 价格 * (1 - 折扣率);
}
let 衣服价格 = 计算折扣(200, 0.2);
let 鞋子价格 = 计算折扣(500, 0.15);
let 帽子价格 = 计算折扣(80, 0.1);
6.2 模块化
把相关功能组织在一起:
// 用户相关功能
function 用户登录(账号, 密码) { /* ... */ }
function 用户登出() { /* ... */ }
function 获取用户信息() { /* ... */ }
// 订单相关功能
function 创建订单(商品列表) { /* ... */ }
function 取消订单(订单号) { /* ... */ }
function 查询订单(订单号) { /* ... */ }
6.3 易于调试
错误定位更精准:
function 第一步处理(数据) {
// 如果这里出错,知道是第一步的问题
return 数据.trim();
}
function 第二步处理(数据) {
// 如果这里出错,知道是第二步的问题
return 数据.toUpperCase();
}
function 第三步处理(数据) {
// 如果这里出错,知道是第三步的问题
return 数据.split('');
}
6.4 可读性
好名字的函数就是的注释:
// 不友好的写法
let a = b * c;
let d = a + e;
let f = d / g;
// 友好的写法
let 税后收入 = 月收入 * 税率;
let 可支配收入 = 税后收入 - 固定支出;
let 日均预算 = 可支配收入 / 30;
七、函数的实践
7.1 函数职责单一
一个函数只做一件事:
// 不推荐
function 处理用户(用户) {
// 验证用户
if (!用户.姓名) return "姓名不能为空";
// 保存到数据库
db.save(用户);
// 发送邮件
email.send(用户.邮箱, "欢迎注册");
// 记录日志
log.write("新用户注册");
}
// 推荐
function 验证用户(用户) { /* ... */ }
function 保存用户(用户) { /* ... */ }
function 发送欢迎邮件(邮箱) { /* ... */ }
function 记录注册日志(用户) { /* ... */ }
7.2 使用有意义的函数名
函数名要能看出做什么:
// 不清晰
function f(a, b) {
return a * b;
}
// 清晰
function 计算矩形面积(宽度, 高度) {
return 宽度 * 高度;
}
7.3 合理使用默认参数
function 创建用户(姓名, 年龄 = 18, 角色 = "访客") {
return {
name: 姓名,
age: 年龄,
role: 角色
};
}
console.log(创建用户("张三")); // 年龄默认18,角色默认访客
console.log(创建用户("李四", 25, "管理员"));
7.4 提前返回处理错误情况
function 获取用户详情(用户ID) {
if (!用户ID) return { error: "用户ID不能为空" };
if (用户ID < 0) return { error: "用户ID无效" };
// 正常逻辑
return { id: 用户ID, name: "用户" + 用户ID };
}
本节课程知识要点:
-
函数名要见名知意
-
每个函数只做一件事
-
处理异常情况优先于主逻辑
-
善用默认参数和提前返回
-
根据场景选择合适的函数类型(普通函数、箭头函数、回调等)
八、本节课程小结
函数是JavaScript编程的基石。从简单的函数声明到复杂的闭包,从传统的function到现在的箭头函数,函数的形式在变,但核心思想不变——把代码组织成可复用的单元。
回顾一下核心内容:
-
声明方式:函数声明(提升)、函数表达式(不提升)
-
方法:call()、apply()、bind()、toString()
-
类型:箭头函数、回调函数、匿名函数、IIFE、嵌套函数
-
优势:复用、模块化、可读性、易调试
-
实践:职责单一、命名规范、提前返回
建议:新手学函数,不要急着把所有花样都学会。先把function关键字用熟,理解参数和返回值,知道怎么调用。然后再慢慢接触箭头函数、闭包这些进阶概念。写代码的时候多想想:这段逻辑能不能抽成函数?这个函数名字够不够清楚?这样练上几个月,函数这块就算真正入门了。