作用域(scope)定义了程序中变量的可访问性和生命周期,在js中,作用域有两种主要的类型:词法作用域(Lexical Scope)和动态作用域(Dynamic Scope);
词法作用域又称静态作用域,是指作用域在代码编写阶段就确定的,与函数的定义位置有关。javascript使用词法作用域,函数的作用域在函数定义时就已经确定
var globalVariable = 'I am global'
function outer() {
var outerVariabl = 'I am outer'
function inner() {
var innerVariable = 'I am inner'
console.log(innerVariable) // 可以访问innerVariable
console.log(outerVariable) // 可以访问outerVariable
console.log(globalVariable) // 可以访问globalVariable
}
inner();
console.log(innerVariable) // Error: innerVariable is not defined
}
outer()
动态作用域是在运行时根据调用链来确定的,与函数的调用位置有关。js不使用动态作用域,而是使用词法作用域
闭包(Closure)是指一个函数和其词法作用域的组合。当函数在词法作用域以外的地方被调用时,它仍然能够访问自己的词法作用域,形成了闭包。
或说
闭包(Closure)是指有权访问另一个函数作用域中变量的函数,即便是在外部函数执行完毕之后。
function outer() {
var outerVariable = 'I am outer'
function inner() {
console.log(outerVariable) // 形成闭包,可以访问outerVariable
}
return inner
}
var closureFunction = outer()
closureFunction() // 通过闭包访问outerVariable
通过闭包,可以创建私有变量,只能通过闭包内部的函数访问,不会被外部直接访问到;
function counter() {
var count = 0;
return function() {
count++;
console.log(count);
};
}
var increment = counter();
increment(); // 输出 1
increment(); // 输出 2
使用闭包可以创建模块,将相关的功能封装到一个闭包中,避免全局污染
var module = (function() {
var privateVariable = 'I am private';
function privateFunction() {
console.log('This is private')
}
return {
publicVariable: 'I am public',
publicFunction: function() {
console.log('This is public')
}
}
})()
console.log(module.publicVariable) // I am public
module.publicFunction() // This is public
由于闭包可以访问外部函数的变量,所以可以保持状态
function createCounter() {
var count = 0;
return {
increment: function() {
count++;
console.log(count)
},
reset: function() {
count = 0;
console.log('Count reset')
}
}
}
var counter = createCounter();
counter.increment(); // 输出1
counter.increment(); // 输出2
在异步回调中经常使用闭包来保存状态
function fetchData(url, callback) {
var data = null;
fetchDataFromServer(url, function(result)) {
// 调用 fetchDataFromServer 函数从服务器异步获取数据,并传给data
data = result;
callback();
});
return function() {
// 使用闭包中的data
console.log(data);
}
}
var getData = fetchData('https://example.com/data', function() {
console.log('Data received');
})
// 在合适的时机调用getData()