闭包的实现原理和作用
1、闭包的概念:指有权访问另一个函数作用域中的变量的函数,一般情况就是在一个函数中包含另一个函数。
2、闭包的作用:访问函数内部变量、保持函数在环境中一直存在,不会被垃圾回收机制处理
因为函数内部声明 的变量是局部的,只能在函数内部访问到,但是函数外部的变量是对函数内部可见的,这就是作用域链的特点了。
子级可以向父级查找变量,逐级查找,找到为止
1 function bar(){ 2 //外层函数声明的变量 3 var value=1; 4 5 function foo(){ 6 console.log(value); 7 } 8 return foo(); 9 }; 10 var bar2=bar; 11 //实际上bar()函数并没有因为执行完就被垃圾回收机制处理掉 12 //这就是闭包的作用,调用bar()函数,就会执行里面的foo函数,foo这时就会访问到外层的变量 13 bar2();
因此我们可以在函数内部再创建一个函数,这样对内部的函数来说,外层函数的变量都是可见的,然后我们就可以访问到他的变量了。
3、闭包的优点:
- 方便调用上下文中声明的局部变量
- 逻辑紧密,可以在一个函数中再创建个函数,避免了传参的问题
4、闭包的缺点:
因为使用闭包,可以使函数在执行完后不被销毁,保留在内存中,如果大量使用闭包就会造成内存泄露,内存消耗很大
实际开发中JS闭包的应用
1。在函数外使用函数内的变量 .函数作为返回值 (闭包作用:避免变量被环境污染)
1 function F1(){ 2 var a = 100; 3 return function(){ 4 console.log(a) 5 } 6 } 7 var f1 =F1(); 8 var a = 200; 9 f1()//100
1 function init(){ 2 var name = "hello world";//name是一个被init创建的局部变量 3 function sayName(){//sayName是一个内部函数,闭包 4 alert(name);//使用了父级函数声明的变量name 5 } 6 sayName(); 7 } 8 init();//"hello world"
2.函数作为参数传递
1 function F1(){ 2 var a = 100; 3 return function(){ 4 console.log(a) 5 } 6 } 7 var f1 =F1(); 8 function F2(fn){ 9 var a = 200; 10 fn(); 11 } 12 F2(f1); // 100
3.将函数与其所操作的某些数据关联起来,通常,你使用只有一个方法的对象的地方,都可以使用闭包
1 // 改变dom样式 2 document.getElementById("a").onclick = setSize(12); 3 document.getElementById("b").onclick = setSize(18); 4 document.getElementById("c").onclick = setSize(22); 5 function setSize(fontSize){ 6 return function(){ 7 document.body.style.fontSize = fontSize + 'px'; 8 } 9 }
4.用闭包模拟私有方法
1 //这三个公共函数是共享同一个环境的闭包。多亏 JavaScript 的词法作用域,它们都可以访问 privateCounter 变量和 changeBy 函数。 2 var makeCounter = function () { 3 var privateCounter = 0; 4 function changeBy(val){ 5 privateCounter += val; 6 }; 7 return { 8 increment: function(){ 9 changeBy(1); 10 }, 11 decrement: function(){ 12 changeBy(-1); 13 }, 14 value: function(){ 15 return privateCounter; 16 } 17 } 18 }; 19 var Counter1 = makeCounter(); 20 var Counter2 = makeCounter(); 21 Counter1.increment(); 22 console.log(Counter1.value());//1 每次调用其中一个计数器时,通过改变这个变量的值,会改变这个闭包的词法环境。然而在一个闭包内对变量的修改,不会影响到另外一个闭包中的变量。 23 console.log(Counter2.value());//0 以这种方式使用闭包,提供了许多与面向对象编程相关的好处 —— 特别是数据隐藏和封装。
5.循环里面的闭包
怎么才能实现输出0-5呢?
1 for (var i = 0; i < 5; i++) { 2 setTimeout(function () { 3 console.log(i); 4 }, 1000 * i); 5 }//55555
//方法一,makeCallback函数为每一个回调创建一个新的词法环境。 function makeCallback(i) { return function() { console.log(i) }; } for(var i=0;i<10;i++){ setTimeout(makeCallback(i),1000) }
//另一种方法使用了匿名闭包 for(var i=0;i<10;i++){ (function(i){ setTimeout(function () { console.log(i) },1000) })(i) }
1 //使用let声明变量 2 for (let i = 0; i < 5; i++) { 3 setTimeout(function () { 4 console.log(i); 5 }, 1000 * i); 6 }