返回笔记列表
01 · NOTES / WebGIS 开发

第 11 节 · 函数与作用域

2026 年 9 月 1 日

第 11 节 · 函数与作用域

📌 版本信息:基于 ECMAScript 2025(基础语义多年稳定;2026-08-29 核对) 📚 来源:现代 JS 教程 · 函数现代 JS 教程 · 函数表达式与箭头函数

一、这一节的目标

  1. 掌握函数三种写法:声明、表达式、箭头函数
  2. 理解参数、返回值、默认参数
  3. 建立作用域直觉:变量在哪儿能看见
  4. 会把重复逻辑打包成函数——AI 生成代码里 90% 的可读性来自合理的函数拆分

二、函数:逻辑的打包单位

一句话:函数是一段"起好名字、可反复调用"的逻辑积木。

// ① 函数声明(有提升:写在后面也能提前调用)
function degToRad(deg) {
  return deg * (Math.PI / 180);   // return:交回结果并结束函数
}

// ② 函数表达式:函数也是值,能存进变量
const radToDeg = function (rad) {
  return rad * (180 / Math.PI);
};

// ③ 箭头函数(现代主流,更短)
const square = (n) => n * n;          // 单参数可省括号,单语句可省 return
const sum = (a, b) => a + b;
const toFixed2 = (n) => n.toFixed(2); // 弧度转经纬度显示时天天用

三种写法在"读代码"层面等价,识别即可。书写建议:顶层工具函数用声明(一眼可见),回调里用箭头(第 13 节大量出现)。

参数细节

// 默认参数:调用时不传就用默认值
function formatCoord(lat, lng, digits = 4) {
  return lat.toFixed(digits) + 'N, ' + lng.toFixed(digits) + 'E';
}
formatCoord(30.5929, 114.3052);       // '30.5929N, 114.3052E'
formatCoord(30.5929, 114.3052, 2);    // '30.59N, 114.31E'(注意 toFixed 四舍五入)

// 参数多了用"对象参数"——比一堆位置参数可读得多
function createMarker({ lat, lng, title = '未命名', color = 'red' }) {
  // 解构传参:直接从对象里拆出字段(第 12 节细讲解构)
  return title + ' @ ' + lat + ',' + lng + ' (' + color + ')';
}
createMarker({ lat: 30.59, lng: 114.3, title: '武汉站' });

⚠️ 没有 return 的函数返回 undefined。还有个经典规则:函数应当只做一件事;当你说不出函数名时,多半是它做了两件事。


三、作用域:变量能被看见的范围

const zoom = 10;                 // 全局作用域:哪儿都能用

function setView() {
  const center = [30.59, 114.3]; // 函数作用域:只在 setView 里活着
  for (let i = 0; i < 3; i++) {
    const offset = i;            // 块作用域:只在 {} 里活着(let/const 的恩惠)
    console.log(zoom, center, offset);   // 三层都能看见,层层向外找
  }
  // console.log(offset);       // ❌ ReferenceError,出了块就没了
}

作用域链:JS 找变量从当前层往外层层层查找,找到即止。这解释了两件事:

  1. 内层可以读外层变量(但不能反过来)
  2. 闭包:内层函数把外层变量"记住"了——第 33 节(阶段二)深挖原理,现在只需知道:回调函数里能用外面的变量,就是闭包在干活
// 闭包速览:counter 记住了自己的 count
function makeCounter() {
  let count = 0;              // 外层变量被内层函数"闭"住了
  return function () {
    count += 1;
    return count;
  };
}
const c1 = makeCounter();
c1(); // 1
c1(); // 2   ← 每次调用都在给"自己那份" count 加一,互不干扰

四、函数式小工具:把逻辑当值传递

函数是值 → 能存变量、能当参数、能当返回值。当参数传入的函数叫回调函数(callback)

// setTimeout:2 秒后执行传入的函数
setTimeout(() => {
  console.log('2 秒到了');
}, 2000);

// 事件监听:点击时执行传入的函数
button.addEventListener('click', () => {
  console.log('被点了');
});
// 这两行你已经在 m00 的练习里用过——现在你知道它叫"回调"了

第 16 节的异步世界、第 13 节的 map/filter,全都建立在"函数是值"这一块基石上。


五、动手跟练:11 · 计算器

配套文件:02-web-basics/examples/11-计算器.html(页面计算器 + 纯函数练习)

步骤:

  1. Live Server 打开,玩一玩,然后读代码
  2. 注意观察:计算逻辑(纯函数)和页面更新(DOM 操作)是分开的——这是现代前端的分层思想雏形
  3. 完成 4 个 TODO:加"取余"运算、除零保护(返回 '不能除以 0')、加"历史记录"数组并在面板显示最近 3 条、把两个数的四则运算抽成 calc(a, op, b) 纯函数

通关标准:

  • 除以 0 不再显示 Infinity / NaN
  • 历史记录最近 3 条可见
  • 能口头解释"纯函数"和"操作 DOM 的函数"为什么分开写

六、自测题

  1. 函数声明和函数表达式的核心区别(提示:提升)?
  2. 箭头函数 (a, b) => a + b 的完整写法是什么?
  3. 没有 return 的函数调用后得到什么?
  4. 块级作用域({} 内的 let)出了块还能访问吗?var 一样吗?
  5. 回调函数是什么?举一个你已经在 m00 用过的例子。

参考答案

  1. 声明有提升(可在定义前调用);表达式按赋值顺序,定义前不可用。
  2. function (a, b) { return a + b; }
  3. undefined
  4. 不能,let/const 是块作用域;var 没有块作用域(会泄漏到函数级甚至全局),这也是弃用 var 的原因之一。
  5. 当作参数传给另一个函数、在合适时机被调用的函数。例:addEventListener('click', 回调)setTimeout(回调, 2000)

七、下一步

单个函数会写了,但数据还是散装 → 第 12 节:对象与数组,把数据组织成 JS 世界的骨架结构。

TAGSweb