💡 作用域(Scope)
一、作用域的概念与分类
🔹 什么是作用域?
- 定义:作用域是变量或函数的作用范围,在函数定义时就已确定。
- 目的:提高程序可靠性,避免命名冲突。
🔹 JS 中的作用域分类(ES5):
- 全局作用域
- 作用范围:整个
script
标签或独立 JS 文件。
- 作用范围:整个
- 函数作用域(局部作用域)
- 作用范围:函数内部。
二、全局作用域与 window 对象
- 页面打开时创建,关闭时销毁。
- 全局变量/函数 会挂载到
window
对象上:
const a = 100; console.log(window.a); // 100
- 全局函数也是
window
的方法。
三、作用域的访问关系
✅ 规则:内层可以访问外层,反之不行。
const a = 'aaa'; function foo() { const b = 'bbb'; console.log(a); // ✅ 可以访问外部变量 } foo(); console.log(b); // ❌ 报错:b is not defined
四、变量的作用域分类
🔸 全局变量
- 在全局作用域中定义,整个代码中都能访问。
🔸 局部变量
- 定义在函数内部,仅限函数内部访问。
- 函数参数也是局部变量。
🔸 执行效率
- 全局变量:占内存,直到页面关闭才释放。
- 局部变量:函数执行完即销毁,节省内存。
五、变量声明的特殊情况
- 未声明直接赋值的变量是全局变量
function fn() { a = 1; // 没有 var/let/const,a 成为全局变量 } fn(); console.log(a); // ✅ 1
⚠️ 不推荐这样使用!
- 局部变量与全局变量同名时,优先使用局部变量
六、作用域的就近原则
- 查找变量时:先当前作用域 → 上层 → 全局,直到找到或报错。
var a = 1; function fn() { var a = 2; console.log(a); // ✅ 2,使用局部变量 } fn(); console.log(a); // ✅ 1
- 要访问全局变量可使用
window.a
七、预处理(声明提前)
📌 什么是预处理?
- JS 在执行前会“预处理”变量和函数声明。
- 行为:将变量/函数声明提前到作用域顶部(变量只声明不赋值)。
变量声明提前(变量提升)
console.log(a); // undefined var a = 123;
- 声明提前了,但赋值没有提前。
console.log(a); a = 123; // ❌ 报错:a is not defined
函数声明提前(函数提升)
fn1(); // ✅ 可以先调用 function fn1() { console.log('函数 fn1'); }
- 函数表达式不会提升:
fun2(); // ❌ 报错 const fun2 = function() { console.log('我是 fun2'); };
函数提升优先于变量提升(经典面试题)
fun(); // B var fun = function () { console.log('A'); }; function fun() { console.log('B'); } fun(); // A
解释(伪代码理解):
function fun() { console.log('B'); } var fun; fun(); // B fun = function() { console.log('A'); }; fun(); // A
八、函数作用域中的预处理
- 函数中
var
声明的变量也会提前。 - 函数中没有
var
声明的变量则为全局变量。
var a = 1; function foo() { console.log(a); a = 2; // 相当于 window.a } foo(); // ✅ 1 console.log(a); // ✅ 2
🔹 补充:函数参数相当于在内部声明了变量
function fun(e) { console.log(e); } fun(); // undefined fun(123); // 123
九、ES5 中没有块级作用域 ❗
if (true) { var num = 123; console.log(num); // 123 } console.log(num); // ✅ 123(依然可以访问)
不像 Java 等语言中 if/for 等语句块内的变量会被限制作用域。
🔟 作用域链
- 函数嵌套 = 多层作用域嵌套
- 变量查找采用就近原则
- 从内向外链式查找,形成作用域链
var num = 10; function fn() { var num = 20; function fun() { console.log(num); // ✅ 20,查到外层变量 } fun(); } fn();
📚 总结建议:
- 开发中应尽量 先声明变量再使用,避免变量提升带来的混淆。
- 多层函数嵌套时,注意变量名重复问题。
- ES6 之后可以使用
let
、const
创建块级作用域,避免 ES5 的作用域陷阱。
评论(0)
暂无评论