JS闭包理解

Posted 每天七点半

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JS闭包理解相关的知识,希望对你有一定的参考价值。

概念

在一个函数中嵌套另一个函数,嵌套(内部)函数对其容器(外部)函数是私有的。闭包是一个可以自己拥有独立的环境与变量的表达式(通常是函数,因为ES6有块级作用域的概念)

闭包是指有权访问另一个函数作用域中变量的函数。

闭包作用:

  • 可以在函数外部访问到函数内部的局部变量;
  • 让这些变量始终保存在内存中,不会随着函数的结束而自动销毁;
// 案例
console.log(\'closure\')
function add()
  var count = 0;
  return function()
    count += 1;
    return count;
  

var a = add();
function fun()
  alert(a());

fun();
fun();

// 定义了add()函数,函数内定义了count,返回值也是一个函数,内部函数引用外部函数变量count,因此形成闭包

闭包经典案例:要求点击每个li标签,弹出对应的索引。(0,1,2,3)

<ul>
  <li>i</li>
  <li>i</li>
  <li>i</li>
  <li>i</li>
</ul>
// 错误答案
for (var i = 0; i < elements.length; i++) 
  elements[i].onclick = function () 
    return (function (n) 
      console.log(n);
      alert(n);
    )(i);
  ;

// 下面时这个题目的答案
// 每个li标签的onclick事件执行时,本身onclick绑定的function的作用域中没有变量i,i为undefined,则解析引擎会寻找父级作用域,
// 发现父级作用域中有i,且for循环绑定事件结束后,i已经赋值为4,所以每个li标签的onclick事件执行时,alert的都是父作用域中的i,也就是4。
// 这是作用域的问题。这里onclick事件绑定的函数形成闭包,但引用的变量i是全局的。闭包真正的含义是,如果一个函数访问了此函数的父级及父级以上的作用域变量,就可以称这个函数是一个闭包。
// 将for循环的var改为let就可以实现想要的效果
// 正确答案
for (let i = 0; i < elements.length; i++) 
  elements[i].onclick = function () 
    return (function (n) 
      console.log(n);
      alert(n);
    )(i);
  ;

闭包造成内存泄漏问题

// 借助以下实例进行讲解
function fn () 
	let count = 1
	function fun () 
		count ++
		console.log(`函数内部调用$count次`)
	
	return fun

const result = fn();
result() //2
result() //3
// 1、result是一个全局变量,代码执行完毕不会立即销毁
// 2、result可以找到fn()函数,fn()函数return fun(),因此 fun()也不会销毁。
// 3、fun()引用到了父级作用域的count,因此,在引用的count便不会被垃圾回收机制回收。
// 此时,闭包就引起了内存泄漏问题
// 正常情况下,一般的局部变量函数执行完就会销毁,但是我们这里的result没有销毁,所以它引用到的count也会留存在内存当中。

闭包的应用

函数工厂

function makeAdder(x) 
   return function(y) 
     return x + y;
   ;
 

 var add5 = makeAdder(5);
 var add10 = makeAdder(10);

 console.log(add5(2));  // 7
 console.log(add10(2)); // 12

模拟私有方法

 var makeCounter = function () 
   var privteCounter = 0;
   function changeBy(val) 
     privteCounter += val;
   
   return 
     increment: function () 
       changeBy(1);
     ,
     decrement: function () 
       changeBy(-1);
     ,
     value: function () 
       return privteCounter;
     ,
   ;
 ;

 var Counter1 = makeCounter();
 var Counter2 = makeCounter();
 console.log(Counter1.value()); /* logs 0 */
 Counter1.increment();
 Counter1.increment();
 console.log(Counter1.value()); /* logs 2 */
 Counter1.decrement();
 console.log(Counter1.value()); /* logs 1 */
 console.log(Counter2.value()); /* logs 0 */

JS 理解闭包

   闭包是js的一个难点,许多高级应用都需要用闭包实现。要理解闭包,首先必须理解Javascript特殊的变量作用域,其次是垃圾回收机制。

一、理解变量作用域

  ①  变量分为全局变量和局部变量,在函数内部可以直接读取全局变量,如:

 var a = 100;

function x1(){

   alert(a);

}

x1();  //得到 100

   而在函数外部,自然无法读取函数内部的局部变量,如:

 function x1(){

   var a = 100;

 }

 alert(a);//error

ps:如果在函数内部声明变量时不用var命令,意味声明了一个全局变量,那么函数外部可以调用,如:

  function x2(){

  b = 150; 

}

 x2();

alert(b);//得到150

② 如何从外部读取局部变量?

可以在函数的内部再定义一个函数,如:

  function x1(){

    var n = 200;

    function x2(){

      alert(n); //得到200;

   }

 }

以上代码,x2包含在x1中,x1中的所有局部变量对x2都是可见的,反之却不行,即“链式作用域结构”。

x2可以读取x1的变量,那么将x2作为返回值,便可以在x1外部读取x1内部的变量了。即:

 function x1(){

   var n = 200;

    function x2(){

     alert(n);//200

   }

   return x2;

}

  var result = x1();

  result();//200

 

以上代码中的x2函数就是闭包。

二、理解闭包

      简单理解,闭包就是可以读取其他函数内部变量的函数,即:定义在一个函数内部的函数。

闭包的作用不仅能读取函数内部的变量,还能让这些变量的值始终保持在内存中。

例:function x1(){

      var a = 999;

        add = function(){a+=1};

       function x2(){

        alert(a);

   }

   return x2;

  }

  var result = x1();

  result(); //999

  add();

  result();//1000

以上代码,result实际就是闭包x2函数,一共运行两次,一次为999,一次1000,说明函数x1中的局部变量a一直保存在内存中,并没有在x1调用后被自动清除。add变量前没有声明var ,即一个全局变量,并且add的值是一个匿名函数,这个匿名函数本身也是一个闭包,因此可以在函数外部对函数内部的局部变量进行操作。

ps:因为变量都会保存在内存中,内存消耗很大,因此不能滥用闭包,避免造成网页的性能问题。

简单总结:

1、闭包外层是个函数
2、闭包内部都有函数
3、闭包会return内部函数
4、闭包返回的函数内部不能有return.(因为这样就真的结束了)
5、执行闭包后,闭包内部变量会存在,而闭包内部函数的内部变量不会存在

 

以上是关于JS闭包理解的主要内容,如果未能解决你的问题,请参考以下文章

js闭包(closure),个人理解

javascript深入理解js闭包

js中的闭包理解

关于js中闭包的理解

js-我理解的闭包

JS闭包理解