闭包

吐槽君 分类:javascript

1.上级作用域的概念

函数的上级作用域在哪里创建的,上级作用域就是谁

  var a = 10;
  
  function foo(){
   console.log(a);
  }
  
  function sum(){
   var a = 20;
   foo();
  }
  
  sum();   //10;
 

函数 foo() 是在全局下创建的,所以a的上级作用域就是window,输出的就是10

思考题

var n = 10;

function fn(){
  var n = 20;
  function f(){
    n++;
    console.log(n)
  }
  f()
  return f
}

var x = fn();
x(); 
x(); 
console.log(n);
 

图片.png

fn 的返回值是什么变量x就是什么,这里fn的返回值是函数名f也就是f的堆内存地址, x()也就是执行的函数 f(),而不是fn(),输出的结果显而易见;

2.js堆栈内存释放

堆内存:存储引用类型值,对象类型就是键值对,函数就是代码字符串

堆内存释放:将引用类型的空间地址变量赋值为null,或没有变量占用堆内存了浏览器就会注释掉这个地址

栈内存:提供代码执行的环境和存储基本类型的值

栈内存释放:一般当函数执行完后函数的私有作用域就会被释放掉

但是栈内存的释放也有特殊情况:1:函数执行完,但是函数的私有作用域内有内容被栈外的变量还在使用的,栈内存就不能释放里面的基本值也就不会被释放 2:全局下的栈内存只有页面被关闭的时候才会被释放

3.闭包是什么

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

4.形成闭包的原因

内部的函数存在外部作用域的引用就会导致闭包

var a = 0;
function foo(){
 var b = 14;
 function fo(){
   console.log(a,b)
 }
 fo();
}
foo();
 

这里的子函数 fo 内存就存在外部作用域的引用 ab,所以这就产生了闭包

5.闭包的作用

  • 保护函数的私有变量不受外部的干扰,形成不销毁的栈内存
  • 保存,把一些函数内的值保存下来,闭包可以实现方法和属性的私有化

6.闭包的经典使用场景

1.return 回一个函数

 var n = 10;
 function fn(){
    var n = 20;
    function f(){
       n++;
       console.log(n);
    }
    return f;
 }
 

这里的 return ffn()就是一个闭包,存在上级作用域的引用


2.函数作为参数

 var a = 'a';
 function foo(){
   var a = 'foo';
   function fo(){
     console.log(a);
   }
   return fo;
 }
 
 function f(p){
   var a='f';
   p();
 }
 
 f(foo());   //foo
 

使用return fo 返回回来,fo就是闭包,f(foo())执行的参数就是函数fo,因为fo() 中的a 的上级作用域就是函数 foo(),所以输出的就是foo


3.IIFE (自执行函数)

 var n = 1;
 (function(){
  console.log(n);
 })()
 // 1
 

同样也是产生了闭包 p(),存在window下的引用 n


4.循环赋值

 for(var i=0 ; i<10 ; i++){
   (function(j){
     setTimeout(function(){
        console.log(j)
     },1000)
   })(i)
 }
 

图片.png

因为存在闭包的原因上面才能依次输出0-9,闭包形成了10个互不干预的私有作用域,将外层的自治性函数去掉后就不存在外部作用域的引用了,输出的结果就是连续的9,为什么呢,因为js是单线程的遇到异步的代码不会先执行,等到同步的代码执行完i++到最大,异步代码才会开始执行,所以输出的都是9


5.使用回调函数就是在使用闭包

 window.name = 'window';
 setTimeout(function timeHandler(){
   console.log(window.name);
 },100)
 

7.使用闭包需要注意什么

容易导致内存泄漏。闭包会携带包含其他的函数作用域,因此会比其他函数占用更多的内存。过度使用闭包会导致内存占用过多,所以要谨慎使用闭包

8.经典面试题

for循环和闭包

 var data = [];
 for(var i=0 ; i<3 ; i++){
    data[i] = function(){
       console.log(i)
    }
 }
 
 data[0]();   //3
 data[1]();   //3
 data[2]();   //3
 

这里的i是全局下的i,共用一个作用域,当函数被执行的时候,这是的 i=3 .导致输出的结果都是3

使用闭包去解决
1.使用自执行函数

var data = [];
for(var i=0 ; i<3 ; i++){
 (function(j){
   data[j] = function(){
     console.log(j)
   }
 })(i)
}

data[0]();  //0
data[1]();  //1
data[2]();  //2
 

2.使用let

  var data = [];
  for(let i=0 ; i<3 ; i++){
     data[i] = function(){
       console.log(i)
     };
  }
  
data[0]();  //0
data[1]();  //1
data[2]();  //2
 

let 具有块级作用域,形成的三个私有作用域是互不干扰的

回复

我来回复
  • 暂无回复内容