函数:三种写法、参数机制与返回值

[迷你控制台]

一、同一件事,三种写法

// ① 函数声明:有名字,会「提升」,声明前也能调用
function double(n) { return n * 2 }

// ② 函数表达式:把一个函数存进变量,不提升
const double = function (n) { return n * 2 }

// ③ 箭头函数:回调场景的简写,没有自己的 this(第七章细讲)
const double = (n) => { return n * 2 }
const double = n => n * 2               // 单参数可省括号、单表达式可省 return

函数声明的提升(hoisting)是「整体搬到作用域顶部」,所以先调用后定义没问题;const 箭头函数只有变量提升、且处于 TDZ,先调用直接 ReferenceError。日常建议:用到的函数先定义再调用,别依赖提升。

二、参数:默认值、undefined 与剩余参数

function greet(name = '游客') { return '你好,' + name }
greet()            // '你好,游客' —— 没传参,用默认值
greet(undefined)   // '你好,游客' —— undefined 触发默认值
greet('')          // '你好,'     —— 空串是「传了」的值,不触发默认
greet(null)        // '你好,null' —— null 也不触发默认(只有 undefined 会)
function sum(...nums) {          // 剩余参数:把所有实参收成一个真数组
  return nums.reduce((acc, n) => acc + n, 0)
}
sum(1, 2, 3)      // 6
sum(...[1, 2, 3]) // 6 —— 展开运算符反向操作,把数组摊成实参

参数传递规则:**少传的自动为 undefined(再由默认值接手),多传的不会报错**,被 ...剩余参数 收走或直接忽略。旧代码里的 arguments 对象是「类数组」,没有数组方法,新代码统一用剩余参数代替。

三、返回值:没有 return 就是 undefined,return 即结束

function divide(a, b) {
  if (b === 0) return '除数不能为 0'   // 守卫子句:提前 return,后续代码不再执行
  if (typeof a !== 'number' || typeof b !== 'number') return '参数必须是数字'
  return a / b                        // 正常路径放在最后
}

function noReturn() { const x = 1 }   // 没有 return → 返回 undefined
function multi() { return { total, count } }   // 想返回多个值 → 包成对象

两个小心处:忘记 return 的函数返回 undefined,链式调用时会得到 undefined.xxx 报错;return 后面不要换行,ASI 会自动补分号,把它变成「return 了 undefined」。

四、箭头函数简写谱系

完整写法简写能省掉什么
(a, b) => { return a + b }(a, b) => a + b函数体只有单表达式时,{} 和 return 都能省
(n) => { return n * 2 }n => n * 2单个参数可省括号
() => { return 42 }() => 42没有参数时括号不能省
(n) => { return { id: n } }n => ({ id: n })直接以 {"{"} 开头会被当成函数体,要包一层括号

箭头函数适合「一行就说完」的回调(数组方法、事件处理),逻辑一多就换回函数声明或表达式——有名字、有函数体括号、还能用 debugger 断点看到清晰的调用栈。