Loading

JS闭包

  什么是闭包:

  函数和对其周围状态(词法环境--词法作用域)的引用捆绑在一起构成闭包;这个环境包含了闭包创建时所能访问到所有局部变量,这个概念非常重要!

  闭包的环境是独立的、互不干扰的;结合上面的概念,可以得知,指的是所能访问到的所有的局部变量;

  闭包是函数和其所在的词法作用域的组合;词法作用域包含了它所能访问到的所有局部变量;

  在JavaScript中,每当函数被创建时,就会在函数生成时生成闭包;


 

  闭包的作用:

  1. 主要是使局部变量常驻内存;普通函数的局部变量在函数执行完毕后,会被垃圾回收机制回收!
  2. 使从函数外部访问函数内的局部变量成为可能

  闭包的缺点:

  1. 会造成内存泄漏,有一块内存被占用,而不被释放;
  2. 闭包会在父函数外部,改变父函数内部变量的值;所以,如果把父函数当作对象使用,把内部变量当作私有属性,把闭包当作公有方法,可以在父函数外部通过访问公有方法改变私有变量的值;一定要注意,不要随意改变内部变量的值;(联想Java中,私有属性一般只是提供访问,而不能更改的;所以JavaScript中,用这种方法模拟对象,也一定不要改变私有属性的值!)

 

  词法作用域,若闭包引用的是全局变量,那么这个变量被同类闭包(由同一个函数创建的闭包)共享,若闭包引用的变量是局部变量,那么闭包对于这个变量的引用是独立的、互不影响的;

  各个闭包所处环境是独立的、互不干扰的;每当函数被调用时,若函数的引用地址不同,都会重新创建一个新的地址,也就是重新生成了一个闭包;

  在JavaScript中,每当函数被创建时,就会在函数生成时生成闭包;

  理论知识需要实践来验证!


  从最简单的函数开始

function fn(){
  var num = 0;
  console.log(++num); 
}//函数被创建时,在函数生成时生成了闭包;
fn();//创建了一个闭包并且调用了函数,引用的num是局部变量,属于闭包环境中的变量,而各个闭包的环境是独立的、互不影响的;
fn();//创建了一个闭包并且调用了函数,但是两个闭包引用的变量num是处于局部作用域下的,所以是独立的、互不影响;输出如下:1,1;
var num = 0;
function fn(){
  console.log(++num);
}
fn();//创建了一个闭包并调用
fn();//创建了一个闭包并调用,但是两个闭包引用的变量num是处于全局作用域下的
//(也就是这个变量并不是处于闭包环境下的,闭包通过作用域链从全局作用域访问到),所以变量共享,输出如下:1,2;

  嵌套函数中的闭包

function father(){
  var num = 0;
  function son(){
    console.log(++num);
  }
  return son;
}
var fn1 = father();//创建了一个闭包,fn1指向的是内部函数son,
var fn2 = father();//创建了另一个闭包,fn2指向的是内部函数son
fn1();//引用的变量num是处于闭包环境下的,所以互不干扰,(闭包环境包括了其能访问到的所有局部变量,也就是包含了变量num 


fn2();//输出如下:1,1
fn1();//输出2
var num = 0;
function father(){
  function son(){
    console.log(++num);
  }
  return son;
}
var fn1 = father();//创建了一个闭包(生了一个函数),fn1指向的是内部函数son
var fn2 = father();//创建了一个闭包,fn2指向的是内部函数son
fn1();//引用的变量num是处于全局作用域下的,所以变量共享
fn2();//输出如下:1,2;

  个人总结:闭包就是函数,其对于变量的访问,与普通函数对变量的访问规则是一样的,首先都是通过作用域链查找变量,如果变量是处于词法作用域中的全局作用域下,所以该变量会被所有闭包共享;

  各个闭包的环境是独立的、互不影响的;这句话什么意思呢?环境指的是函数所处的词法环境,包含了所有能访问到的局部变量,不包含全局变量哦,配合这个理解,就很好明白所有函数都是闭包这个概念了,

  掌握执行函数时,便创建了一个闭包并调用的概念,也就是说两次执行函数,生成了两个闭包,它们对于函数内部变量的作用是互不干扰的,也就是说闭包的所处环境是独立的,互不干扰的(理解为对于局部变量的引用是互不干扰的);可以从上面第一和第三个例子验证

 

  上文重复强调了函数的词法作用域,也就是闭包环境,仅仅是包括了函数所能访问到的所有局部变量!!

  每个闭包环境都是独立互不干扰的;

  闭包中可以通过作用域链访问到闭包环境外的变量---也就是全局作用域下的变量,该变量是共享的;


  闭包的应用:

  1. 用闭包模拟私有方法;在Java中,是支持将方法声明为私有的,私有方法只能被同一类中的其他方法访问;
    var counter = function(){
      var privaterNum = 18;
      function changeBy(val){
        privateNum += val;
      }
      return {
      increment: function(){
        changeBy(1);
      },
      decrement: function(){
        changeBy(-1);
      },
    value: function(){
    return privateNum;
    } } }();//立即执行函数中,返回了一个闭包,闭包内的词法环境被三个函数共享 counter.increment();
    //privateNum == 19
    console.log(counter.value()
    );//19

    2、在循环中创建闭包

    for(var i = 0; i < 4; i++){
      setTimeout(function(){
        console.log(i);
      },1000)
    }
    //直接输出四个4,在闭包环境中找不到i变量,沿着作用域链访问到全局变量i,在定时器回调输出时,i已经变成了4
    for(var i = 0; i < 4; i++){
      (function(i){
        setTimeout(function(){
          console.log(i);
        },1000*i)
      })(i);
    }
    //输出0,1,2,3每隔一秒输出一个;定时器回调输出i,在闭包环境中找到了各自的局部变量i,(局部变量是不会受到其他作用域的影响的);

     3、函数工厂

    function makeSizer(size){
      return function(){
        document.body.style.fontSize = size + 'px
      }
    }
    var size12 = makeSizer(12);
    var size14 = makeSizer(14);//函数工厂生成函数,

     

posted @ 2020-04-24 15:52  姑苏天阳  阅读(200)  评论(0编辑  收藏  举报