js闭包的原理及应用
js闭包的原理及应用
闭包的实现原理和作用
1、闭包的概念:指有权访问另一个函数作用域中的变量的函数,一般情况就是在一个函数中包含另一个函数。
2、闭包的作用:访问函数内部变量、保持函数在环境中一直存在,不会被垃圾回收机制处理。
因为函数内部声明 的变量是局部的,只能在函数内部访问到,但是函数外部的变量是对函数内部可见的,这就是作用域链的特点了。子级可以向父级查找变量,逐级查找,找到为止。
例:
function f(){
//外层函数声明的变量
var value=1;
function foo(){
console.log(value);
}
return foo();
};
var k=f;
k();//1
//实际上f()函数并没有因为执行完就被垃圾回收机制处理掉 原因是有个全局的变量在应用f()函数
//这就是闭包的作用,调用k()函数,就会执行里面的foo函数,foo这时就会访问到外层的变量
因此我们可以在函数内部再创建一个函数,这样对内部的函数来说,外层函数的变量都是可见的,然后我们就可以访问到他的变量了。
3、闭包的优点:
方便调用上下文中声明的局部变量
逻辑紧密,可以在一个函数中再创建个函数,避免了传参的问题
4、闭包的缺点:
因为使用闭包,可以使函数在执行完后不被销毁,保留在内存中,如果大量使用闭包就会造成内存泄露,内存消耗很大
实际开发中js闭包的应用
1。在函数外使用函数内的变量 .函数作为返回值 (闭包作用:避免变量被环境污染)
function F1(){
var a = 100;
return function(){
console.log(a)
}
}
var f1 =F1();
var a = 200;
f1()//100
function f(){
var name = "wangcai";//name是一个被f创建的局部变量
function sayName(){//sayName是一个内部函数,闭包
alert(name);//使用了父级函数声明的变量name
}
sayName();
}
f();//"wangcai"
2.函数作为参数传递
function F1(){
var a = 100;
return function(){
console.log(a)
}
}
var f1 =F1();
function F2(fn){
var a = 200;
fn();
}
F2(f1); // 100
3.将函数与其所操作的某些数据关联起来,通常,你使用只有一个方法的对象的地方,都可以使用闭包
// 改变dom样式
document.getElementById("a").onclick = setSize(12);
document.getElementById("b").onclick = setSize(18);
document.getElementById("c").onclick = setSize(22);
function setSize(fontSize){
return function(){
document.body.style.fontSize = fontSize + 'px';
}
}
4.用闭包模拟私有方法
//这三个公共函数是共享同一个环境的闭包。多亏 JavaScript 的词法作用域,它们都可以访问 privateCounter 变量和 changeBy 函数。
var makeCounter = function () {
var privateCounter = 0;
function changeBy(val){
privateCounter += val;
};
return {
increment: function(){
changeBy(1);
},
decrement: function(){
changeBy(-1);
},
value: function(){
return privateCounter;
}
}
};
var Counter1 = makeCounter();
var Counter2 = makeCounter();
Counter1.increment();
console.log(Counter1.value());//1 每次调用其中一个计数器时,通过改变这个变量的值,会改变这个闭包的词法环境。然而在一个闭包内对变量的修改,不会影响到另外一个闭包中的变量。
console.log(Counter2.value());//0 以这种方式使用闭包,提供了许多与面向对象编程相关的好处 —— 特别是数据隐藏和封装。
js面试题(闭包)
本来讲闭包,为什么这里还会有一部分讲面试题呢? 因为看了这个面试题你可能更重视闭包的作用!
1、下面这段代码的输出结果是?
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(new Date, i);
}, 1000);
}
console.log(new Date, i);
相信能看到这里的读者得到的答案是下面三个答案的一种:
A:0,1,2,3,4,5
B:5,0,1,2,3,4
C:5,5,5,5,5,5
只要你对 JS 中同步和异步代码的区别、变量作用域、闭包等概念有正确的理解,就知道正确答案是 C,代码的实际输出是:
Fri Jul 24 2020 19:33:42 GMT+0800 (中国标准时间) 5
Fri Jul 24 2020 19:33:43 GMT+0800 (中国标准时间) 5
Fri Jul 24 2020 19:33:43 GMT+0800 (中国标准时间) 5
Fri Jul 24 2020 19:33:43 GMT+0800 (中国标准时间) 5
Fri Jul 24 2020 19:33:43 GMT+0800 (中国标准时间) 5
Fri Jul 24 2020 19:33:43 GMT+0800 (中国标准时间) 5
分析:
settimeout是异步执行,10ms后往任务队列里面添加一个任务,只有主线上的全部执行完,才会执行任务队列里的任务,当主线执行完成后,i是5,所以此时再去执行任务队列里的任务时,i全部是5了。
对于打印6次:
每一次for循环的时候,settimeout都执行一次,但是里面的函数没有被执行,而是被放到了任务队列里面,等待执行,for循环了6次,就放了6次,当主线程执行完成后,才进入任务队列里面执行。(注意:for循环从开始到结束的过程,需要维持几微秒或几毫秒。)
2、怎么让这道题的输出结果是5,0,1,2,3,4?
下面给出了两种解法:
可以利用IIFE(Immediately Invoked Function Expression:声明即执行的函数表达式)来解决闭包带来的问题
for (var i = 0; i < 5; i++) {
(function(j) { // j = i
setTimeout(function() {
console.log(new Date, j);
}, 1000);
})(i);
}
console.log(new Date, i);
有没有更符合直觉的做法?答案是有,我们只需要对循环体稍做手脚,让负责输出的那段代码能拿到每次循环的 i 值即可。该怎么做呢?
var output = function (i) {
setTimeout(function() {
console.log(new Date, i);
}, 1000);
};
for (var i = 0; i < 5; i++) {
output(i); // 这里传过去的 i 值被复制了
}
console.log(new Date, i);
总结
感谢你花时间读到这里,相信你对闭包有了更多的收获。
本文地址:https://blog.csdn.net/weixin_45631722/article/details/107566371
上一篇: 外观超赞:华擎联手宝马打造紧凑游戏PC