丸子学JS(学习1小时 - 闭包的应用)
2023-11-30 本文已影响0人
丸子小姐__不懂爱
何为闭包
如果一个函数访问了此函数的父级及父级以上的作用域变量,那这个函数就是一个闭包
本质上, JS中的每个函数都是一个闭包,因为每个函数都可以访问全局变量
闭包的执行过程
function a() {
var i = '初始值';
i = i + "—_执行a"
// 此处的函数b访问了父级函数a中的局部变量i,成为了一个闭包
function b() {
i = i + "_执行b"
console.log(i)
}
return b;
}
var c = a(); // 此时 i 的值为 :初始值—_执行a
c() // 此时 i 的值为 :初始值—_执行a_执行b
c() // 此时 i 的值为 :初始值—_执行a_执行b_执行b
- 将函数a赋值给全局变量c时,a会执行一次,局部变量 i 的值变为初始值—执行a,最终返回函数b,此时全局变量c的值为闭包函数b的引用。
此时函数a虽然已执行完,但因为内部包含闭包函数b,所以函数 a 的执行期上下文会继续保留在内存中,不会被销毁,所以局部变量 i 仍是初始值—执行a
执行期上下文:当函数执行时,会创建一个执行期上下文的内部对象。每调用一次函数,就会创建一个新的上下文对象,他们之间是相互独立的。当函数执行完毕,它所产生的执行期上下文会被销毁
- 第一次执行 c() 时,闭包函数b第一次执行,局部变量 i 的值变为初始值—执行a执行b
- 第二次执行 c() 时,闭包函数b第二次执行,局部变量 i 的值变为初始值—执行a执行b_执行b
图解闭包
var a = "global variable";
var F = function () {
var b = "local variable";
var N = function () {
var c = "inner local";
return b;
};
return N;
};
var d = F()
d()
● 全局作用域 G 中有:
○ —— 函数 F
○ —— 全局变量 a
○ —— 全局变量 d (存有对闭包函数 N 的引用)
● 函数 F 中有:
○ —— 返回闭包函数N
○ —— 函数 F 作用域中的局部变量 b
○ —— 闭包函数 N
● 闭包函数 N 中有:
○ —— 返回局部变量b
○ —— 函数 N 作用域中的局部变量 c
闭包的特点
- 被闭包函数访问的父级及以上的函数的局部变量会一直存在于内存中, 不会被JS的垃圾回收机制回收
2.闭包函数实现了对其他函数内部变量的访问。(函数内部的变量对外是无法访问的,闭包通过这种变通的方法,实现了访问。)
闭包的用途
1.访问函数内部的变量
2.让变量始终保持在内存中
闭包的应用场景
模拟面向对象的代码风格
模拟两人对话
function person(name) {
function say(content) {
console.log(name + ':' + content)
}
return say
}
a = person("张三")
b = person("李四")
a("在干啥?")
b("没干啥。")
a("出去玩吗?")
b("去哪啊?")
================打印start================
张三:在干啥?
李四:没干啥。
张三:出去玩吗?
李四:去哪啊?
================打印end================
使setTimeout支持传参
通过闭包实现setTimeout第一个函数传参(默认不支持传参)
function func(param){
return function(){
alert(param)
}
}
var f1 = func(1);
setTimeout(f1,1000);
封装私有变量
//用闭包定义能访问私有函数和私有变量的公有函数。
var counter = (function () {
var privateCounter = 0; //私有变量
function change(val) {
privateCounter += val;
}
return {
increment: function () {
change(1);
},
decrement: function () {
change(-1);
},
value: function () {
return privateCounter;
}
};
})();
console.log(counter.value());//0
counter.increment();
console.log(counter.value());//1
counter.increment();
console.log(counter.value());//2
模拟块作用域
var elements = document.getElementsByTagName('li');
var length = elements.length;
for (var i = 0; i < length; i++) {
elements[i].onclick = function (num) {
return function () {
alert(num);
};
}(i);
}
实现迭代器
function setup(x) {
var i = 0;
return function(){
return x[i++];
};
}
var next = setup(['a', 'b', 'c']);
================打印start===============
> next();
"a"
> next();
"b"
> next();
"c"
================打印end===============
闭包的优点
- 可以减少全局变量的定义,避免全局变量的污染
- 能够读取函数内部的变量
- 在内存中维护一个变量,可以用做缓存
闭包的缺点
- 造成内存泄露
- 闭包可能在父函数外部,改变父函数内部变量的值
- 造成性能损失
闭包范例
返回匿名闭包
function funA(){
var a = 10; // funA的活动对象之中;
return function(){ //匿名函数的活动对象;
alert(a);
}
}
var b = funA();
b(); //10
各自独立的闭包
function outerFn(){
var i = 0;
function innerFn(){
i++;
console.log(i);
}
return innerFn;
}
var inner = outerFn(); //每次外部函数执行的时候,都会开辟一块内存空间,外部函数的地址不同,都会重新创建一个新的地址
inner();
inner();
inner();
var inner2 = outerFn();
inner2();
inner2();
inner2(); //1 2 3 1 2 3
访问全局变量的闭包
var i = 0;
function outerFn(){
function innnerFn(){
i++;
console.log(i);
}
return innnerFn;
}
var inner1 = outerFn();
var inner2 = outerFn();
inner1();
inner2();
inner1();
inner2(); //1 2 3 4
闭包的链式调用
var add = function (x) {
var sum = 1;
var tmp = function (x) {
console.log('执行tmp')
sum = sum + x;
return tmp;
}
tmp.toString = function () {
return sum;
}
return tmp;
}
console.log(add(1)(2)(3).toString())
===============打印start================
执行tmp
执行tmp
6
===============打印end================