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>

语法规则:

▶ 示例:函数基本用法

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="*">&times;</option>
    <option value="/">&divide;</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 的温床。始终用 letconst 声明变量。

▶ 示例:作用域演示

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>

你其实已经用过回调了——forEachmapfilter 的参数都是回调函数:

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 =&gt; n * 2</strong><br>';
    html += '结果: [' + doubled.join(', ') + ']<br><br>';

    html += '<strong>回调:n =&gt; n * n</strong><br>';
    html += '结果: [' + squared.join(', ') + ']<br><br>';

    html += '<strong>回调:n =&gt; "第" + 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: '北京' })。顺序不重要,可读性更好。

📖 小节

📝 作业

  1. 写一个函数 isEven(n),判断数字是否为偶数,返回 truefalse,在页面上测试多组数据
  2. 写一个函数 formatPrice(price),接收数字返回格式化的价格字符串(如 formatPrice(9.9) 返回 "¥9.90"),并用箭头函数写法
  3. 写一个"问候生成器":用回调函数实现,传入名字数组和问候函数,在页面上显示每个人的个性化问候语
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏