javascript笔记整理系列

来源:互联网 发布:js删除注释工具 编辑:程序博客网 时间:2024/06/17 20:30

1 什么是闭包

1.1 作用域链

  1. 函数的执行依赖于变量作用域,这个作用域是在创建函数定义是决定的,而不是在调用时决定的。
     要理解闭包,就必须先了解作用域链。看代码
function f1() {        var n = 999;        function f2() {        var m = 1;              alert(n);    //999    }      alert(m);        //error:无法访问变量m。}

从上面代码我们可以看出,f2可以访问f1内局部变量,但反过来不行。

1.2 从外部获取函数局部变量

 还是上面的函数,如果我们希望从外部获取f1函数的变量n,就像在f1中获取f2的局部变量m一样不可行。但是f2何以获取n,我们利用这一点来获取变量n。

function f1() {        var n = 999;        function f2() {           console.log(n);    }    return f2;}f1()();    //999

1.3 闭包的概念

闭包概念
  闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
其他理解
  通过1.2我们了解如何从函数外访问函数内的变量,例子中的f2就是闭包。闭包连接了函数外与函数的作用域。所以在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。有权访问另一个函数作用域内变量的函数都是闭包
个人理解
  我们也可以通过”闭包”这两个字面来理解。拿上面的代码来说,有人会提出质疑:既然在javascript中函数也是对象,那执行f1()返回的f2函数也只是简单的将f2函数当作对象返回。这种质疑没错,f1函数中确实将f2函数当作对象返回,但重要的时,f1的return同时将内部作用域也一起返回出来,而并不是仅仅返回f2对象。如此说来,”包”这个字就好理解了:将作用域及f2”打包”返回。而”闭”则代表封闭的意思,也就是f1函数的作用域封闭。

2 闭包的作用

总结起来,闭包有俩作用:
1. 可在外部读取函数内部的变量。
2. 让闭包所占用的资源始终保存在内存中,不会被垃圾回收机制GC回收。。

function f1() {        var n = 999;        nAdd = function () {        n += 1    }        function f2() {              alert(n);        }        return f2;  }  var result = f1();  result(); // 999  nAdd();  result(); // 1000

3 Javascript的垃圾回收机制

  在Javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收。如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。

4 详解闭包

function a() {    var i = 0;    function b() {        alert(++i);    }    return b;}var c = a();c();

  如果要更加深入的了解闭包以及函数a和嵌套函数b的关系,我们需要引入另外几个概念:
函数的执行环境(excution context)、活动对象(call object)、作用域(scope)、作用域链(scope chain)。以函数a从定义到执行的过程为例阐述这几个概念。

  1. 当定义函数a的时候,js解释器会将函数a的作用域链(scope chain)设置为定义a时a所在的“环境”,如果a是一个全局函数,则scope chain中只有window对象。
  2. 当执行函数a的时候,a会进入相应的执行环境(excution context)。
  3. 在创建执行环境的过程中,首先会为a添加一个scope属性,即a的作用域,其值就为第1步中的scope chain。即a.scope=a的作用域链。
  4. 然后执行环境会创建一个活动对象(call object)。活动对象也是一个拥有属性的对象,但它不具有原型而且不能通过JavaScript代码直接访问。创建完活动对象后,把活动对象添加到a的作用域链的最顶端。此时a的作用域链包含了两个对象:a的活动对象和window对象。
  5. 下一步是在活动对象上添加一个arguments属性,它保存着调用函数a时所传递的参数。
  6. 最后把所有函数a的形参和内部的函数b的引用也添加到a的活动对象上。在这一步中,完成了函数b的的定义,因此如同第3步,函数b的作用域链被设置为b所被定义的环境,即a的作用域。
      到此,整个函数a从定义到执行的步骤就完成了。此时a返回函数b的引用给c,又函数b的作用域链包含了对函数a的活动对象的引用,也就是说b可以访问到a中定义的所有变量和函数。函数b被c引用,函数b又依赖函数a,因此函数a在返回后不会被GC回收。

5 闭包的应用场景

  1. 保护函数内的变量安全。
  2. 在内存中维持一个变量。
  3. 通过保护变量的安全实现JS私有属性和私有方法(不能被外部访问)。
var counter = function () {    var n = 0;    return {        count: function () { return n++; },        reset: function () { n = 0; }    }}var c = counter(), d = counter();c.count();  //0d.count();  //0c.reset();c.count();  //1d.count();  //0

每次调用counter()都会创建一个新的作用域链和一个新的私有变量,所以c和d的私有变量互不干扰。

6 通过闭包实现getter和setter

function counter(n){    return {        get count() { return n++; },        set count(m){            n = m;        }    };}var a = counter(100);console.log(a.count);   //100a.count = 200;console.log(a.count);   //200
  1. get set关键字为ES5添加,支持IE9+。

7 闭包出现的问题

  1. 由于闭包将变量式中保存在内存中,可能会导致内存溢出。
  2. 闭包可以访问函数内的局部变量,所以可能会错误的修改局部变量。
  3. 不希望共享的变量共享了其他的闭包:
function constfuncs(){    var funcs = [];    for(var i=0; i<10; i++){        funcs[i] = function(){ return i; };    }    return funcs;}var funcs = constfuncs();funcs[5]();     //结果是?

上面函数利用for循环创建了多个闭包,但这些闭包内返回的是constcuncs函数内的局部变量i。其实只有一个变量i,每一个闭包返回的都是这个变量i。最后i为10跳出循环,所以funcs5的结果是10。

0 0