闭包
作用域
- 函数内部可以使用全局变量。
- 函数外部不可以使用局部变量
- 当函数执行完毕,本作用域内的局部变量会被销毁。
函数分为两个阶段
-
定义阶段
会在堆内存中开辟一个新的空间,将函数中的代码作为字符串存储进去。并将这个新空间的地址返回给一个变量。
-
调用阶段
在执行空间中开辟一个新的空间,执行函数中的代码,声明新的变量,执行完代码后,该空间会被销毁,变量也会被销毁。
什么是闭包
1、什么是闭包?
关于什么是闭包,官方的解释是:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。相信很少有人能直接看懂这句话,因为他描述的太学术。而网上对闭包的理解也是众说纷纭,而我的理解就是
闭包就是定义在函数内部并且能够读取其他函数局部变量的函数,或
者更通俗的理解:闭包就是函数嵌套函数,通过return把函数返回,内部函数可以引用外部函数的参数和变量,参数和变量不会被垃圾回收机制收回
有权访问另外一个函数作用域中的变量的函数。

2、闭包有什么好处?
1>、外围函数可以读取函数内部函数的变量:由于作用域链的结构,外围函数是无法访问内部变量的,为了能够访问内部变量,我们就可以使用闭包。
2>、可以让一个变量长期驻扎在内存中
3>、可以避免全局变量的污染
4>、私有成员的存在
说了这么多,下面我们用例子来看看到底什么是闭包,以及使用闭包的好处
var a=1;
function aaa(){
a++;
alert(a);
}
aaa();//2
aaa();//3
我们想要让a重复累加,这是我们不使用闭包时候的做法。我们可以从代码中看到,我们使用了全局变量。但我们在实际的项目中为了提高性能,是要尽量避免使用全局变量。所以如果我们不使用全局变量结果会是怎样的呢
function aaa(){
var a=1;
a++;
alert(a);
}
aaa();//2
aaa();//2
很显然这并不是我们想要的结果,每次执行a都是2,这是因为局部变量每次执行之后,都会被垃圾回收机制收回,再执行再重新赋值。
那如果我们想要既能重复累加,又使用局部变量,要怎么做呢?对,就是使用闭包,看例子
function aaa(){
var a= 1;
return function(){
a++;
alert(a);
}
}
var b =aaa();
b();//2
b();//3
这就是闭包,函数嵌套函数,内部函数可以引用外部函数的参数和变量
闭包的特点:
- 函数嵌套函数
- 内部函数引用外部函数的变量。
<body>
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>
</body>
<!-- <script>
var btns = document.querySelectorAll("button");
for (var i = 0; i < btns.length; i++) {
btns[i].onclick = function(){
alert(i);//每次都输出5
};
}
</script> -->
<script>
var btns = document.querySelectorAll("button");
for (var i = 0; i < btns.length; i++) {
btns[i].onclick = btnClick(i);
}
function btnClick(i) {
return function () {
alert(i);//点击每个按钮输出每个按钮的下标
}
}
</script>
闭包的作用
内存管理机制:
当有变量指向了一块内存地址时,因为随时可能会被调用,所以这块内存不会被销毁。
<script>
function person(name) {
var name = name;
// console.log(name);//走到这一步 name 就已经为小明
function sayName() {
console.log(name);// 对于sayName()这个函数来讲,name就是外部变量
}
return sayName;
}
var fun = person("小明");
fun();//输出小明
</script>
闭包的缺点
容易造成内存泄露。
(闭包后的参数一直在内存中)
闭包中的this
<!-- 输出user的值 -->
<!-- <script>
let tm ={
user:"提莫",
get:function(){
return function(){
// console.log(this);//这个里面this指向window window.user当然找不到值
return this.user;
}
}
}
let a = tm.get();//a就是给这个对象起的名字
console.log(a());//undefined(其实是window.a)
</script> -->
<!-- <script>
let tm ={
user:"提莫",
get:function(){
// console.log(this);//这个里面this指tm对象 tm.user就能找到值
let qthis=this;
return function(){
return qthis.user;
}
}
}
let a = tm.get();
console.log(a());//提莫
</script> -->
<script>
let tm ={
user:"提莫",
get:function(){
// console.log(this);//this指向tm对象(我们之前学过箭头函数的this指向是由这个匿名函数上下文决定的)
return () =>{
return this.user;
}
}
}
let a = tm.get();
console.log(a());//提莫
</script>