ife task0003学习笔记(三):JavaScript闭包

一、this易错分析

在学习闭包的时候,有一个概念this很重要,关于this的理解,下面3种情况:this指向谁?

fn.call(obj1);
obj2.fn()
fn()

答案是obj1 obj2 window

判断this的指向有3条规则:

执行函数的过程中肯定存在两方,一方是调用函数的人caller,一方是被调用的人callee,callee永远是函数fn,caller遵循以下的规范:

  • fn.call(xxx) fn.apply(xxx)的时候,this永远指向xxx
  • ooo.fn()或者ooo['fn']()的时候,this永远指向ooo
  • fn()的时候this指向window
    所以针对上面代码:
  1. call/apply的作用是改变函数的调用者,或者说是改成this,改变this为 call /apply的第一个参数

  2. 一个函数没人调用的时候,相当于window调用了他

  3. 函数中除去this和arguments的其它变量,均遵守闭包规则,闭包的位置永远是函数定义的位置。

二、闭包易错代码分析

下面看两个代码实例,它们来源于阮一峰的文章闭包讲解。

 var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      return function(){
        return this.name;
      };
    }
  };
  alert(object.getNameFunc()());

返回结果是:

the window

分析:

alert(object.getNameFunc()());

这个里面有2个括号,要分析两次,把语句拆开:
第一步:var fn = object.getNameFunc();这时执行getNameFunc的时候,this是object,第一次return返回:function(){return this.name;}
第二步fn(),这时候函数由于没有调用者,根据规则3,函数的this ->window,这个函数里面的this.name实际上是window.name


 var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      var that = this;
      return function(){
        return that.name;
      };
    }
  };
  alert(object.getNameFunc());

返回结果:My Object

分析

同样分成两步执行:
第一步执行:var fn = object.getNameFunc();这个时候,this指向object,同时在函数执行的过程中,在getNameFunc所在的闭包内创建了变量that,that = this 所以that指向object,然后第二步执行fn(),这个时候由于没有作用对象,fn里面的this指向window,但是我们在这访问的是that.name,在fn里面没有找到叫that的东西,然后根据闭包规则找到fn的上一级闭包getNameFunc,我们发现在这有一个变量that,它指向object,所以that,name就是object.name,是my object

由此可见,闭包的概念更好理解了:

  1. 闭包就是函数的局部变量集合,只是这些局部变量在函数返回后会继续存在。
  2. 闭包就是就是函数的“堆栈”在函数返回后并不释放,我们也可以理解为这些函数堆栈并不在栈上分配而是在堆上分配
  3. 当在一个函数内定义另外一个函数就会产生闭包

简单来说,闭包就是在另一个作用域中保存了一份它从上一级函数或作用域取得的变量(键值对),而这些键值对是不会随上一级函数的执行完成而销毁。周爱民说得更清楚,闭包就是“属性表”,闭包就是一个数据块,闭包就是一个存放着“Name=Value”的对照表。就这么简单。但是,必须强调,闭包是一个运行期概念。有两个主要特点:

  • 作为一个函数变量的一个引用 - 当函数返回时,其处于激活状态。
  • 一个闭包就是当一个函数返回时,一个没有释放资源的栈区。

三、闭包的作用

  1. 可以读取函数内部的变量
  2. 让这些变量的值始终保持在内存中。

四、闭包的缺点

1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

参考资料:
学习Javascript闭包:http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
javascript的闭包:http://www.cnblogs.com/rubylouvre/archive/2009/07/24/1530074.html
JavaScript 闭包深入理解:http://www.jb51.net/article/18303.htm
理解 Javascript 的闭包:http://www.oschina.net/question/28_41112

posted @ 2015-05-06 09:28  青青flye  阅读(178)  评论(0编辑  收藏  举报