JavaScript: JavaScript 函数
函数是可复用的代码块——写一次,用无数次。就像菜谱:定义好步骤,每次做菜照着来就行,不用重新发明。不会用函数的程序员,就像每做一道菜都从头试配方,累死还容易出错。
1. 函数是什么
函数把一段逻辑"打包",给它起个名字,需要时调用就行:
HTML
<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8"><title>函数调用</title></head>
<body>
<div id="output"></div>
<script>
function sayHello() {
document.getElementById('output').textContent += '你好!\n';
}
sayHello();
sayHello();
</script>
</body>
</html>
函数的好处:
- 复用:写一遍,到处调用
- 封装:藏住复杂细节,只暴露接口
- 维护:改一处,所有调用方受益
▶ 示例
JAVASCRIPT
// 本节代码示例
const message = "你好,世界!";
console.log(message);
这是最简单的示例,后续章节会详细解释。## 2. 函数声明 vs 函数表达式
创建函数有两种主要方式:
HTML
<script>
// 函数声明
function greet(name) {
return `你好,${name}!`;
}
// 函数表达式
let greet2 = function(name) {
return `你好,${name}!`;
};
</script>
关键区别:函数声明会被提升(hoisting),你可以在定义之前调用它;函数表达式不会,必须先定义再调用。
HTML
<script>
// 函数声明:可以先用后定义
hello(); // 正常运行
function hello() { console.log('hi'); }
// 函数表达式:先用后定义会报错
// hi(); // TypeError: hi is not a function
// let hi = function() { console.log('hi'); };
</script>
⚠️ 注意: 在
let 声明前访问变量属于"暂时性死区"(TDZ),会直接报错,不是 undefined。所以函数表达式一定要先定义再使用。
3. 参数和默认参数
函数通过参数接收外部数据:
HTML
<script>
function add(a, b) {
return a + b;
}
add(1, 2); // 3
</script>
调用时没传的参数是 undefined。ES6 开始支持默认参数:
HTML
<script>
function greet(name, greeting = '你好') {
return `${greeting},${name}!`;
}
greet('小明'); // '你好,小明!'
greet('小明', 'Hello'); // 'Hello,小明!'
</script>
💡 提示: 默认参数放在参数列表末尾。放在前面没意义——调用时没法跳过它只传后面的。
4. 返回值(return)
return 把结果返回给调用方,同时终止函数执行:
HTML
<script>
function square(n) {
return n * n;
}
let result = square(5); // 25
</script>
没有 return 或只写 return; 的函数,返回 undefined。
HTML
<script>
function doNothing() {
// 没有 return
}
doNothing(); // undefined
</script>
⚠️ 注意:
return 后面换行会自动加分号,导致返回 undefined:
return // 实际上 return; undefined
obj; // 这行永远不会执行到
解决办法:return 和返回值写同一行,或用括号包起来。
5. 箭头函数(=>)
箭头函数是 ES6 引入的简写语法:
HTML
<script>
// 普通函数
let double = function(n) {
return n * 2;
};
// 箭头函数
let double2 = (n) => n * 2;
// 多条语句需要花括号和 return
let greet = (name) => {
let msg = `你好,${name}!`;
return msg;
};
</script>
语法规则:
- 单个参数可以省略括号:
n => n * 2 - 0 个或多个参数要括号:
() => 'hi'、(a, b) => a + b - 单行表达式自动返回,多行需要
{}+return
▶ 示例:函数基本用法
HTML
<!DOCTYPE html>
<html>
<body>
<h2>函数基本用法</h2>
<div id="output"></div>
<script>
function add(a, b) {
return a + b;
}
function greet(name, greeting) {
if (greeting === undefined) greeting = '你好';
return greeting + ',' + name + '!';
}
let multiply = (a, b) => a * b;
let square = n => n * n;
let html = '';
html += 'add(3, 5) = ' + add(3, 5) + '<br>';
html += 'add(10, 20) = ' + add(10, 20) + '<br>';
html += "greet('小明') = " + greet('小明') + '<br>';
html += "greet('小明', 'Hello') = " + greet('小明', 'Hello') + '<br>';
html += 'multiply(4, 6) = ' + multiply(4, 6) + '<br>';
html += 'square(7) = ' + square(7) + '<br>';
document.getElementById('output').innerHTML = html;
</script>
</body>
</html>
▶ 示例:简易计算器
HTML
<!DOCTYPE html>
<html>
<body>
<h2>简易计算器</h2>
<input type="number" id="numA" placeholder="数字1">
<select id="op">
<option value="+">+</option>
<option value="-">-</option>
<option value="*">×</option>
<option value="/">÷</option>
</select>
<input type="number" id="numB" placeholder="数字2">
<button onclick="calc()">计算</button>
<p id="result"></p>
<script>
function calc() {
let a = Number(document.getElementById('numA').value);
let b = Number(document.getElementById('numB').value);
let op = document.getElementById('op').value;
let res;
switch (op) {
case '+': res = a + b; break;
case '-': res = a - b; break;
case '*': res = a * b; break;
case '/':
if (b === 0) {
document.getElementById('result').innerHTML = '除数不能为0!';
return;
}
res = a / b;
break;
}
document.getElementById('result').innerHTML =
a + ' ' + op + ' ' + b + ' = <strong>' + res + '</strong>';
}
</script>
</body>
</html>
6. 函数作用域
变量在函数内部声明,只在函数内部有效,外面访问不到——这就是作用域:
HTML
<script>
function test() {
let x = 10; // 局部变量
const y = 20;
var z = 30;
}
test();
// console.log(x); // ReferenceError: x is not defined
</script>
函数可以访问外部变量(闭包的基础),但外部不能访问函数内的变量:
HTML
<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8"><title>函数作用域</title></head>
<body>
<pre id="output"></pre>
<script>
let global = '全局变量';
function show() {
let local = '局部变量';
document.getElementById('output').textContent +=
'函数内 global: ' + global + '\n' +
'函数内 local: ' + local + '\n';
}
show();
document.getElementById('output').textContent +=
'函数外 global: ' + global + '\n' +
'函数外 local: 无法访问(局部变量)';
</script>
</body>
</html>
⚠️ 注意: 函数内不用
let/const/var 直接赋值,会创建全局变量,这是 bug 的温床。始终用 let 或 const 声明变量。
▶ 示例:作用域演示
HTML
<!DOCTYPE html>
<html>
<body>
<h2>函数作用域</h2>
<div id="output"></div>
<script>
let outer = '外层变量';
function demo() {
let inner = '内层变量';
let html = '';
html += '函数内访问 outer: ' + outer + '<br>';
html += '函数内访问 inner: ' + inner + '<br>';
function nested() {
let deep = '嵌套变量';
html += '嵌套函数访问 outer: ' + outer + '<br>';
html += '嵌套函数访问 inner: ' + inner + '<br>';
html += '嵌套函数访问 deep: ' + deep + '<br>';
}
nested();
return html;
}
let result = demo();
result += '函数外访问 outer: ' + outer + '<br>';
result += '函数外访问 inner: 无法访问(局部变量)';
document.getElementById('output').innerHTML = result;
</script>
</body>
</html>
7. 回调函数简介
JavaScript 允许把函数当作参数传给另一个函数——传进去的那个就叫回调函数:
HTML
<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8"><title>回调函数</title></head>
<body>
<pre id="output"></pre>
<script>
function processArray(arr, callback) {
for (let item of arr) {
callback(item);
}
}
let names = ['小明', '小红', '小刚'];
processArray(names, function(name) {
document.getElementById('output').textContent += '你好,' + name + '\n';
});
</script>
</body>
</html>
你其实已经用过回调了——forEach、map、filter 的参数都是回调函数:
HTML
<script>
// 这里的匿名函数就是回调
[1, 2, 3].forEach(function(n) {
console.log(n);
});
// 用箭头函数更简洁
[1, 2, 3].forEach(n => console.log(n));
</script>
💡 提示: 回调是 JavaScript 的灵魂。浏览器的事件处理、定时器、网络请求,全靠回调机制运作。现在先知道"函数能当参数传"就行,后面会深入。
▶ 示例:回调函数实战
HTML
<!DOCTYPE html>
<html>
<body>
<h2>回调函数</h2>
<div id="output"></div>
<script>
function processList(items, action) {
let results = [];
for (let item of items) {
results.push(action(item));
}
return results;
}
let nums = [1, 2, 3, 4, 5];
let doubled = processList(nums, n => n * 2);
let squared = processList(nums, n => n * n);
let labeled = processList(nums, n => '第' + n + '项');
let html = '';
html += '原数组: [' + nums.join(', ') + ']<br><br>';
html += '<strong>回调:n => n * 2</strong><br>';
html += '结果: [' + doubled.join(', ') + ']<br><br>';
html += '<strong>回调:n => n * n</strong><br>';
html += '结果: [' + squared.join(', ') + ']<br><br>';
html += '<strong>回调:n => "第" + n + "项"</strong><br>';
html += '结果: [' + labeled.join(', ') + ']<br><br>';
html += '<strong>setTimeout 回调(2秒后显示):</strong><br>';
html += '<span id="timer">等待中...</span>';
document.getElementById('output').innerHTML = html;
setTimeout(function() {
document.getElementById('timer').textContent = '时间到!回调已执行';
document.getElementById('timer').style.color = 'green';
}, 2000);
</script>
</body>
</html>
❓ 常见问题
Q 箭头函数和普通函数有什么区别?
A 除了写法更短,箭头函数没有自己的
this,它的 this 继承自外层。在对象方法或事件处理中需要 this 时,用普通函数更安全。简单工具函数用箭头函数没问题。Q 函数可以返回多个值吗?
A 不能直接返回多个,但可以返回数组或对象,然后用解构接收:
function getInfo() { return [name, age]; },调用时 let [name, age] = getInfo()。Q 参数太多怎么办?
A 参数超过 3 个时,考虑用对象传参:
function createUser({ name, age, city }) {},调用时 createUser({ name: '小明', age: 20, city: '北京' })。顺序不重要,可读性更好。📖 小节
- 函数声明会提升,函数表达式不会——习惯先定义再使用最安全
- 默认参数让函数调用更灵活,放在参数列表末尾
return终止函数并返回值,没写return就返回undefined- 箭头函数
(a, b) => a + b是简写,单行自动返回,多行需要{} - 函数内声明的变量是局部的,外部访问不到
- 回调函数 = 把函数当参数传,
forEach、setTimeout都在用
📝 作业
- 写一个函数
isEven(n),判断数字是否为偶数,返回true或false,在页面上测试多组数据 - 写一个函数
formatPrice(price),接收数字返回格式化的价格字符串(如formatPrice(9.9)返回"¥9.90"),并用箭头函数写法 - 写一个"问候生成器":用回调函数实现,传入名字数组和问候函数,在页面上显示每个人的个性化问候语