作用域和闭包
作用域
作用域是在运行时代码中的某些特定部分中变量,函数和对象的可访问性。 作用域就是一个独立的区域,让变量不会外泄、暴露出去。作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。
作用域类型:
- 全局作用域
- 函数作用域
- 块级作用域
作用域链
如果在当前作用域中没有找到变量,再一层一层向上寻找,直到找到全局作用域还是没找到,就宣布放弃。这种一层一层的关系,就是 作用域链 。
作用域在定义时就确定,并且不会改变。
执行上下文
执行上下文(Execution Context)是指在运行时创建的一个环境,用于存储当前执行的代码的所有 相关信息。每当代码开始执行时,JavaScript 引擎都会创建一个新的执行上下文。
执行上下文包含:
- 变量对象
- 作用域链
- this 指针
执行上下文在运行时确定,随时可能改变;
闭包
能够访问其他函数内部变量的函数,被称为 闭包。
闭包的形成发生在当一个内部函数引用了其外部函数的变量,并且这个内部函数在外部函数执行完毕后仍然可以被访问到时。
闭包的形成条件:
函数嵌套
内部函数引用外部函数的局部变量
闭包的作用
- 可以读取函数内部的变量,保留函数执行所需的状态信息
闭包的一个重要用途是可以让一个函数访问另一个函数作用域内的变量。这意味着即使外部函数已经执行完毕并返回,内部函数仍然可以访问并操作这些变量。这种能力在很多场景中都非常有用,特别是当需要保留一些状态信息时。
例如:
防抖(Debouncing):在用户输入时,只有在用户停止输入一定时间后,才进行搜索或验证操作。闭包可以用来保存定时器的状态,确保只有在最后一次输入后的规定时间内没有新的输入时,才执行相关操作。 节流(Throttling):在处理滚动或拖动等高频事件时,闭包可以用来保存时间戳或定时器的状态,确保在规定的时间间隔内只处理一次事件。
- 可以实现封装,属性私有化
JavaScript 本身并没有像其他面向对象 语言那样的“私有属性”概念。但是,通过闭包,我们可以实现类似的功能,保护函数内部的数据不被外部代码直接访问或修改。
例如:
私有变量:通过闭包,可以在一个函数内部声明变量,并通过返回一个函数来暴露有限的操作接口。这样一来,外部无法直接访问这些变量,只能通过提供的接口进行操作。
function createCounter() {
let count = 0; // 私有变量
return {
increment: function () {
count++;
},
getCount: function () {
return count;
},
};
}
通过这种方式,count 变量对外部来说是不可见的,只能通过 increment 和 getCount 方法来操作。
- 模块化开发,防止污染全局变量
在 JavaScript 中,很容易不小心定义了全局变量,导致命名冲突和其他问题。通过闭包,可以实现模块化的开发,将变量和功能封装起来,避免污染全局作用域。
例如:
模块模式:通过立即执行函数表达式(IIFE)和闭包,可以创建独立的作用域,保护内部变量不被外部访问。
(function () {
var privateData = "Some private data";
function privateMethod() {
// Private method implementation
}
window.myModule = {
publicMethod: function () {
// 使用私有方法和变量
privateMethod();
},
};
})();
privateData 和 privateMethod 都被封装在 IIFE 内部,并通过闭包对外提供了一个公共接口 publicMethod。
通过闭包实现模块化开发,可以有效防止全局变量的污染