JS学习之闭包初识

闭包

闭包是什么

  1. 当内部函数被保存到函数外时,就会产生闭包。
  2. 闭包会使得原有作用域链不释放,导致内存泄漏。

内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费。

举个栗子:

  function a () {
   function b() {
      var bbb = 234;
      console.log(aaa);  
  }
  var aaa = 123;
  return b;
}
  var glob = 100;
  var demo = a();
  demo();

联系上文作用域知识:

  1. a undefined --> 0 : GO
  2. a doing        --> 0 : aAO
                        --> 1 : GO
  3. b undefined --> 0 : aAO
                        --> 1 : GO
  4. b doing        --> 0 : bAO
                        --> 1 : aAO
                        --> 2 : GO

demo = a ();使得b函数保留在了demo中,所以可以在函数外部,对b进行访问。
同时,函数的return是这个传送门,可以将一个内部函数送出外部函数。 即使无法直接访问到外部函数内部的变量,也可以通过return出的内部函数去访问外部函数的变量。
这样的做法,被总结称为 闭包

由于闭包会导致内存泄露,所以使用闭包时一定要注意,在编码时更要注意无意产生闭包的情况,过多的闭包会导致加载慢。

闭包的作用

实现公有变量
  1. 累加器
function add({
  var count = 0;
  function demo(){
   count ++;
   console.log(count);
   }
   return demo;
}
var counter = add();
counter();//执行一次,count就会累加一次
存储结构
function eater(){
   var food = "";
   var obj = {
    eat:function () {
      console.log("I am eating" + food);
      food = "";
    },
    push:function(myFood){
      food = myFood;
    }
  }
  return obj;
}
var eater1 = eater();
eater1.push("banana");

私有化变量

function person(name,age){
this.name = name;
this.age = age;
var friend = 'john';
this.sayFriendName = function(){
console.log(friend);
 }
}
var person1 = new person('merry',21);

此时在函数外部调用friend时不能返回值。如person.friend返回Undefined.
而调用包含friend元素的方法时,可以引用到friend并返回值。

立即执行函数

当我们理解了 闭包 的概念后,就会出现一种需求,我们并不需要多次调用闭包函数,甚至并不需要闭包的返回值,仅仅是需要它执行一次。那上述函数最简单的写法是否如下:

function a(){/* code */}()      //SyntaxError: Unexpected token (  解析错误

很可惜,结果却是报错。
原因:
当JavaScript在读取代码时,看到 function 关键字后,就已经认为这是一个函数声明了,而在函数声明之后,是不可以直接加()来执行的。我们要做的就是让js知道这不是一个函数声明即可。
解决方法:
可以用圆括号将整个函数包起来再(),更可以使用简单的判断符号来处理。这样的做法,就称为 立即执行函数

    (function a(){/* code */})()

而一般情况下,只对匿名函数使用这种立即执行函数,通常步骤为:

  1. 声明一个匿名函数。
  2. 马上调用这个匿名函数,执行完立即释放
(function(){alert('我是匿名函数')});

上面是一个典型的立即执行函数。

  • 首先声明一个匿名函数 function(){alert(‘我是匿名函数’)}。
  • 然后在匿名函数后面接一对括号 (),调用这个匿名函数。

那么为什么还要用另一对括号把匿名函数包起来呢?
如果我们不加另一对括号,直接写成

    function(){alert('我是匿名函数')}()

会显示解析错误。
在js中,只有表达式才能被执行符号执行:
如:

var a = function test(){
    var aaa = 123;
    console.log(aaa);
}

语法可以被执行,但是被执行的函数将会被忽略函数名
因此test将变成未知量。
同样,以下的语法都可以被执行:

  • (function(){alert(‘我是匿名函数’)} ()) // 用括号把整个表达式包起来
  • (function(){alert(‘我是匿名函数’)}) () //用括号把函数包起来
  • !function(){alert(‘我是匿名函数’)}() // 求反,我们不在意值是多少,只想通过语法检查。
  • +function(){alert(‘我是匿名函数’)}()
  • -function(){alert(‘我是匿名函数’)}()
  • ~function(){alert(‘我是匿名函数’)}()
  • void function(){alert(‘我是匿名函数’)}()
  • new function(){alert(‘我是匿名函数’)}()
    同样的,()也是运算符,可以将原式转换成表达式。
    因此存在两种括号的写法。
    ( function ( ) { } ( ) )
    ( function ( ) { } ) ()

立即执行函数的作用

  1. 不必为函数命名,避免污染全局变量;
  2. 内部形成单独的块级作用域,封装一些私有变量;
  3. 内部变量执行完即销毁,不会占用更多的内存。

以一个著名的面试题为例:

    var liList = ul.getElementsByTagName('li');
    for(var i = 0; i < 6; i ++){
      liList[i].onclick = function(){
        alert(i) // 为什么 alert 出来的总是 6,而不是 0、1、2、3、4、5
      }
    }

为什么 alert 的总是 6 呢,因为 i 是贯穿整个作用域的,而不是给每个 li 分配了一个 i,如下:

那么怎么解决这个问题呢?用立即执行函数给每个 li 创造一个独立作用域即可:

    var liList = ul.getElementsByTagName('li')
    for(var i=0; i<6; i++){
      !function(ii){
        liList[ii].onclick = function(){
          alert(ii) // 0、1、2、3、4、5
        }
      }(i)
    }

在立即执行函数执行的时候,i 的值被赋值给 ii,此后 ii 的值一直不变。

i 的值从 0 变化到 5,对应 6 个立即执行函数,这 6 个立即执行函数里面的 ii 「分别」是 0、1、2、3、4、5。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值