01 · NOTES / WebGIS 开发
第 11 节 · 函数与作用域
2026 年 9 月 1 日
第 11 节 · 函数与作用域
📌 版本信息:基于 ECMAScript 2025(基础语义多年稳定;2026-08-29 核对) 📚 来源:现代 JS 教程 · 函数 | 现代 JS 教程 · 函数表达式与箭头函数
一、这一节的目标
- 掌握函数三种写法:声明、表达式、箭头函数
- 理解参数、返回值、默认参数
- 建立作用域直觉:变量在哪儿能看见
- 会把重复逻辑打包成函数——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 找变量从当前层往外层层层查找,找到即止。这解释了两件事:
- 内层可以读外层变量(但不能反过来)
- 闭包:内层函数把外层变量"记住"了——第 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(页面计算器 + 纯函数练习)
步骤:
- Live Server 打开,玩一玩,然后读代码
- 注意观察:计算逻辑(纯函数)和页面更新(DOM 操作)是分开的——这是现代前端的分层思想雏形
- 完成 4 个 TODO:加"取余"运算、除零保护(返回 '不能除以 0')、加"历史记录"数组并在面板显示最近 3 条、把两个数的四则运算抽成
calc(a, op, b)纯函数
通关标准:
- 除以 0 不再显示 Infinity / NaN
- 历史记录最近 3 条可见
- 能口头解释"纯函数"和"操作 DOM 的函数"为什么分开写
六、自测题
- 函数声明和函数表达式的核心区别(提示:提升)?
- 箭头函数
(a, b) => a + b的完整写法是什么? - 没有
return的函数调用后得到什么? - 块级作用域(
{}内的 let)出了块还能访问吗?var一样吗? - 回调函数是什么?举一个你已经在 m00 用过的例子。
参考答案
- 声明有提升(可在定义前调用);表达式按赋值顺序,定义前不可用。
function (a, b) { return a + b; }。undefined。- 不能,
let/const是块作用域;var没有块作用域(会泄漏到函数级甚至全局),这也是弃用 var 的原因之一。 - 当作参数传给另一个函数、在合适时机被调用的函数。例:
addEventListener('click', 回调)、setTimeout(回调, 2000)。
七、下一步
单个函数会写了,但数据还是散装 → 第 12 节:对象与数组,把数据组织成 JS 世界的骨架结构。
TAGSweb