JavaScript 函数与作用域深度详解:从基础语法到底层机制(新手必懂)
在 JavaScript 中,如果说 循环 是代码的“重复能力”,那 函数和作用域 就是代码的“组织能力”。
函数让代码可复用,作用域让变量可控。
绝大多数 JS 疑难问题、面试题、bug 来源:变量污染、变量提升、闭包、全局污染、变量找不到、函数执行顺序混乱,根源全部来自函数与作用域。
今天这篇文章,我从零到一细致拆解:函数的所有种类、使用细节、作用域分层、变量查找规则、生命周期、经典坑点,一次性彻底学通透。
一、什么是 JS 函数?核心本质
函数就是一段可以重复调用、具备独立功能的代码块。
函数的出现就是为了解决一个问题:代码重复、冗余、难维护。
函数三要素:函数名、参数、返回值。
- 函数名:标记函数,用于调用
- 参数:外部传入的数据,让函数更灵活
- 返回值 return:函数执行完对外输出结果
二、JS 函数的 4 种写法(细节+区别)
很多新手只会一种函数写法,但实际开发中四种写法机制完全不同,提升、调用时机、this 指向全部不一样。
- 函数声明(最常用、支持提升)
标准的 function 函数名(){} 写法。
function fn() {
console.log(‘我是函数声明’)
}
核心细节:
- 支持 变量提升:可以 先调用,后定义
- 有函数名,可递归、可监听堆栈
- 适合封装通用工具函数
- 函数表达式(匿名函数赋值)
把匿名函数赋值给变量。
const fn = function() {
console.log(‘函数表达式’)
}
核心细节:
- 不提升:必须先定义、后调用
- 更严谨,防止全局函数泛滥
- 开发中使用频率极高
- 箭头函数(ES6 最简写法)
const fn = () => {
console.log(‘箭头函数’)
}
关键特性(面试必考):
- 无自己的 this,继承上层作用域 this
- 无 arguments
- 不能作为构造函数 new
- 极简语法,适合回调、遍历、简单逻辑
- 立即执行函数(IIFE)
(function(){
// 立即执行,执行完销毁
})()
作用:制造独立作用域,防止变量污染全局(ES6 之前唯一模块化方案)
三、函数参数的详细规则(超多新手坑)
- 形参和实参
- 形参:函数定义时的占位变量
- 实参:调用函数时传入的真实数据
- 参数个数不匹配规则
- 实参多于形参:多余参数自动忽略
- 实参少于形参:缺少的形参值为 undefined
- 默认参数
function fn(a = 10) {
console.log(a)
} - 剩余参数 …(收集多个参数)
function fn(…args) {
console.log(args) // 数组形式接收所有参数
}
替代废弃的 arguments,开发首选。
四、函数返回值 return 细节
- return 终止函数执行,后面代码不再运行
- 没有 return 的函数,默认返回 undefined
- return 可以返回任意类型:基本数据、数组、对象、甚至另一个函数(闭包基础)
五、JS 作用域完整详解(核心重点)
作用域:变量可以生效、可以被访问的范围。
JS 作用域分为三层:全局作用域、函数作用域、块级作用域。
- 全局作用域
页面打开就存在,关闭页面才销毁。
所有在 函数外、代码最外层 定义的变量属于全局变量。
缺点:全局变量过多,容易 变量污染、命名冲突。 - 函数作用域
函数内部定义的变量,外部无法访问。
function fn() {
let a = 10
}
console.log(a) // 报错:a 未定义
优点:变量私有化,不会污染全局 - 块级作用域(ES6 核心)
被 {} 包裹的代码块:if、for、while、switch。
let / const 才有块级作用域,var 没有。
if(true){
let b = 20
}
console.log(b) // 访问不到
六、变量查找规则:作用域链(重中之重)
作用域链:内层向外层逐层查找变量的机制
查找规则一句话:就近原则,从内向外,找到即停,找不到报错
let num = 100
function outer() {
let num = 200
function inner() {
console.log(num) // 输出 200
}
inner()
}
outer()
查找流程:
- 先在当前作用域找
- 没有就去父作用域
- 一直找到全局作用域
- 全局都没有 → Uncaught ReferenceError
七、变量提升机制(为什么函数声明可以先调用?)
- 提升规则
- var 变量:提升声明,值为 undefined
- function 函数声明:整体提升(可以先调用后定义)
- let / const / 箭头函数表达式:不提升,存在暂时性死区
- 经典对比
fn1() // 正常执行
function fn1(){}
fn2() // 报错
const fn2 = function(){}
八、作用域生命周期(变量什么时候销毁?)
- 全局变量:页面打开存在,页面关闭销毁
- 局部/块级变量:函数/代码块执行完毕,立即销毁,释放内存
这也是为什么:局部变量更省内存、更安全。
九、新手最容易踩的 6 个函数/作用域坑点
坑1:var 没有块级作用域,导致变量泄露
for(var i=0;i<3;i++){}
console.log(i) // 3 泄露到全局
坑2:箭头函数无 this,乱用导致指向错误
回调场景优先箭头函数,对象方法不要用箭头函数。
坑3:函数表达式不能先调用
只有函数声明支持提升。
坑4:全局变量随意定义造成污染
多人开发极易命名冲突、覆盖变量。
坑5:return 后面换行,导致隐性终止
return
{ name: “test” } // 不会生效
坑6:内层变量和外层同名,造成变量遮蔽
内层变量会遮蔽外层同名变量,导致外层无法访问。
十、开发最佳实践(总结干货)
- 禁止使用 var,统一 let / const,杜绝变量泄露
- 通用工具方法用 函数声明
- 业务逻辑、回调优先 箭头函数、函数表达式
- 尽量少写全局变量,多用局部作用域封装
- 利用作用域隔离代码,避免全局污染
- 理解作用域链,是学习 闭包、this、原型、模块化 的前置基础
十一、结尾总结
函数决定代码能力,作用域决定代码秩序。
弄懂函数:你会写复用、简洁、规范的代码。
弄懂作用域:你能解决 90% 的变量报错、作用域污染、查找异常 问题,同时为后续 闭包、ES6模块化、this指向 打好地基。