何为闭包 |
在一个函数内可以访问到另一个函数(外层作用域)的局部变量时,此时「函数」和「函数内部能访问到的变量」的总和,就是一个闭包。
看得到的闭包? |
(function(){
var local='局部变量';
function foo(){
console.log(local);
console.dir(foo);
}
foo()
})()
闭包的作用 |
闭包常常用来「间接访问一个变量」。换句话说,「隐藏一个变量」。还有一个作用就是,让这些变量的值始终保持在内存中。
有一些变量不希望直接被其他人调用,则可以通过函数,让别人可以「间接访问」。如果设为全局变量,全部用户都可以访问,被人不小心改了,谁知道会引发什么后果呢?
闭包经典使用:嵌套函数+尾调用 |
让全局作用域访问到局部作用域的变量。
function outer(){
var local='局部变量'
return function (){
console.log(local);
}
}
let foo =outer();
foo();
local 变量和 匿名函数就组成了一个闭包(Closure)。
模拟执行上下文栈 |
👉JS执行过程与执行上下文(栈)
按理来说,outer
函数执行完成后,里面的变量对象都会被销毁,但是因为生成了闭包。outer
函数执行上下文对象中的变量对象不会立刻被销毁,需要等到闭包内部变量被调用完成才会被销毁。
主要是因为匿名函数的执行上下文对象中的scope属性保存了对outer变量对象的引用。
看下该匿名函数执行上下文对象:
anonymousContext={
VO:{
arguments:
length:0
},
Scope:[AO,outerContext.VO,globalContext.AO],
this:undefined
}
如果在函数A的内部调用函数B,那么在A的调用帧上方,还会形成一个B的调用帧。等到B运行结束,将结果返回到A,B的调用帧才会消失。
尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用帧,因为调用位置、内部变量等信息都不会再用到了,只要直接用内层函数的调用帧,取代外层函数的调用帧就可以了。
一定要函数套函数吗 |
因为需要局部变量,所以才把 local 放在一个函数里,如果不把 local 放在一个函数里,local 就是一个全局变量了,达不到使用闭包的目的——隐藏变量。
函数套函数只是为了造出一个局部变量,跟闭包无关。
闭包应用 |
定义具有特定功能的js模块(隐藏变量接口) |
设计私有的方法和变量。(作用)
function myModule() {
//私有数据
var msg = 'Smyhvae Haha'
//操作私有数据的函数
function doSomething() {
console.log('doSomething() ' + msg.toUpperCase()); //字符串大写
}
function doOtherthing() {
console.log('doOtherthing() ' + msg.toLowerCase()) //字符串小写
}
//通过【对象字面量】的形式进行包裹,向外暴露多个函数
return {
doSomething1: doSomething,
doOtherthing2: doOtherthing
}
}
let m=myModule();
m.doSomething1();
让变量的值始终保持在内存中 |
存储变量(作用)
function f1(){
var n=999;
nAdd=function(){n+=1}
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
nAdd();
result(); // 1000
注意:nAdd
是全局变量
模仿块级作用域 |
if(){}for(){}
等没有作用域,所以在其块内声明的变量,在外部是可以使用的。(匿名自执行函数)
//javaScript没有块级作用域的概念
function fn(num){
for(var i=0;i<num;i++){}
console.log(i);//在for外部i不会失败
}
fn(2);
if(true){
var a=13;
}
console.log(a);//在if定义的变量在外部可以访问
通过匿名自执行函数可以模拟块级作用域
(function(){
//i在外部就不认识啦
for(var i=0;i<count;i++){}
})();
console.log(i);//报错,无法访问
其他:内存泄漏问题 |
变量不可销毁,需要主动销毁
function assignHandler() {
var element = document.getElementById('someElement');
var id = element.id;
element.onclick = function() {
alert(id);
};
element = null;
}