javascript闭包的理解


Posted in Javascript onApril 01, 2015

1、首先我们要知道变量作用域链

变量的作用域分两种:全局变量和局部变量。没有定义到任何函数中的变量为全局变量,在函数中定义的变量为局部变量,注意在函数内部定义变量时一定要使用var关键字,不带var关键字的变量为全局变量。

javascript中每一段代码都有与之关联的作用域链,这个作用域链是一个对象列表或者链表,定义了这段代码“作用域”中的变量。顶层代码的作用域由全局变量组成;不包含嵌套的函数的作用域链有两个对象:一个是定义的函数参数和局部变量的对象,一个是全局变量对象;而嵌套函数的作用域链有三个对象:函数参数和局部变量--外部函数的参数和局部变量--全局变量。函数可以访问作用域链上的对象,因此函数可以访问全局变量,而反过来却不可以,即在函数外部不能访问函数内的局部变量。

var a=1;
function wai(){
  alert(a);        
  var m=10;
  n=20;
}

wai();          //=> 1; 函数内部可以访问全局变量
alert(m);        //=> error; 外部访问函数局部变量报错  
alert(n);        //=> 20; 函数内部定义的变量未使用var关键字,所以是全局变量,外部可以访问

2、如何在外部读取局部变量

有时候,我们需要在外部访问函数内板的局部变量,这个时候我们就需要使用变通的方法来实现。我们利用javascript变量作用域的特点,在函数内部定义子函数,子函数就可以访问父函数里的变量了

function wai(){
 var m=10;
  function nei(){
    alert(m); 
 }
  return nei;
}

var f = wai();
nei();              //=> error; nei()函数是一个局部变量,在外部不能访问
f();               //=> 10;

3、闭包

上一段代码的nei()函数就是一个闭包,从上面可知,闭包就是可以读取函数内部局部变量的函数,是定义到函数内部的函数,本质上可以认为是函数内部和函数外部连接到一起的桥梁。

闭包的作用有两个:

一是前面提到的可以读取函数内部的变量

二是可以让这些局部变量保存在内存中,实现变量数据共享

function wai(){
  var m=99;
  function nei(){
    alert(m);
    m++;
  }
  return nei;
}
      
var f= wai();
f();         //=> 99;
f();         //=> 100;
f();         //=> 101;

上述实例当wai()函数运行时,变量m就保存到内存中,执行f()就可以读取m的值,但直接alert(m)不可以!

我们还可以向闭包函数传参数,如下面示例所示,定义一个匿名函数并返回了一个闭包函数,该函数将传入的参数与匿名函数中的局部变量i相加,并使i自增;

var wai=(function(){
  var i=0;
  return function(num){
    num+=i;
    alert(num);
    i++;
 }
})();
wai(1);//1
wai(2);//3
wai(3);//5

为了更深入理解闭包,我们看下面一个示例:

现在我想定义一个函数,该函数返回一个数组,且数组每个元素都是一个函数,每个函数会弹出对应的索引值

我们可能会这样写

function box(){
 var arr=[];
  for(i=0;i<5;i++){
    arr[i]=function(){return i;}
  }
 return arr;  
}
var a=box();
alert(a);        //=>包含五个函数体的数组
alert(a[0]());     //=> 5;
alert(a[1]());    //=> 5;

上面的代码发现弹出的都是5,而不是我们预想的0,1,2,3,4,这是因为i也是存在于内存中的局部变量,当我们运行a[0]()时,i的值已经是5,i的值在整个box()函数运行过程中是不断自增的。

解决方法:闭包的实现

function box(){
var arr=[];
    for(var i=0;i<5;i++){

         arr[i]=(function(num){
           return function(){return num;}
         })(i);

     }
return arr;     
}

var arr=box();

for(var i=0;i<5;i++){

  alert(arr[i]());//0,1,2,3,4
}

4、使用闭包的注意事项

1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数的变量值。

5、下面是从几个关于闭包的思考题

如果你能理解下面代码的运行结果,应该就算理解闭包的运行机制了。

Js代码

var name = "The Window"; 
var object = { 
  name : "My Object", 
  getNameFunc : function(){ 
    return function(){ 
      return this.name;       //=>嵌套函数的this为全局变量或undefined,不会继承父函数的this
    }; 
  } 
}; 
alert(object.getNameFunc()()); //The Window

以上之所以输出的为“The window”是因为在嵌套函数中this不会继承父函数this,其值为全局变量或undefined(ECMAScript5下),因此返回的为全局对象的name变量。要想让其返回object的name属性,代码如下:

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

以上代码将父函数object的this赋给cur变量,其嵌套函数就可以通过cur变量访问其属性了

------------------------------------------------------------------------------------------------------
JavaScript闭包例子

function outerFun()
{
var a=0;
function innerFun()
{
a++;
alert(a);
} 
}
innerFun();  //=>error

上面的代码是错误的.innerFun()的作用域在outerFun()内部,所在outerFun()外部调用它是错误的.

改成如下,也就是闭包:

Js代码

function outerFun()
{
   var a=0;
  function innerFun()
  {
    a++;
    alert(a);
  }
  return innerFun; //注意这里
}
var obj=outerFun();
obj(); //结果为1
obj(); //结果为2
var obj2=outerFun();
obj2(); //结果为1
obj2(); //结果为2

什么是闭包:

当内部函数 在定义它的作用域 的外部 被引用时,就创建了该内部函数的闭包 ,如果内部函数引用了位于外部函数的变量,当外部函数调用完毕后,这些变量在内存不会被 释放,因为闭包需要它们.

--------------------------------------------------------------------------------------------------------

再来看一个例子

Js代码

function outerFun()
{
   var a =0;
   alert(a); 
}
var a=4;
outerFun();   //=> 0
alert(a);      //=> 4

结果是 0,4 . 因为在函数内部使用了var关键字 维护a的作用域在outFun()内部.

再看下面的代码:

Js代码

function outerFun()
{
//没有var 
a =0;
alert(a); 
}
var a=4;
outerFun();    //=> 0
alert(a);      //=> 0

结果为 0,0 真是奇怪,为什么呢?

作用域链是描述一种路径的术语,沿着该路径可以确定变量的值 .当执行a=0时,因为没有使用var关键字,因此赋值操作会沿着作用域链到var a=4; 并改变其值.

Javascript 相关文章推荐
js获取时间(本周、本季度、本月..)
Nov 22 Javascript
JS Array创建及concat()split()slice()的使用方法
Jun 03 Javascript
JavaScript代码里的判断小结
Aug 22 Javascript
js中toString()和String()区别详解
Mar 23 Javascript
bootstrap-Treeview实现级联勾选
Nov 23 Javascript
Angular angular-file-upload文件上传的示例代码
Aug 23 Javascript
详解小程序不同页面之间通讯的解决方案
Nov 23 Javascript
用Fundebug插件记录网络请求异常的方法
Feb 21 Javascript
Vue.js实现备忘录功能
Jun 26 Javascript
Vue.js路由实现选项卡简单实例
Jul 24 Javascript
javascript实现画板功能
Apr 12 Javascript
JS sort方法基于数组对象属性值排序
Jul 10 Javascript
JavaScript数据类型之基本类型和引用类型的值
Apr 01 #Javascript
JavaScript之Object类型介绍
Apr 01 #Javascript
JS修改iframe页面背景颜色的方法
Apr 01 #Javascript
JS返回iframe中frameBorder属性值的方法
Apr 01 #Javascript
javascript算法题:求任意一个1-9位不重复的N位数在该组合中的大小排列序号
Apr 01 #Javascript
jQuery scrollFix滚动定位插件
Apr 01 #Javascript
JS动态修改iframe高度和宽度的方法
Apr 01 #Javascript
You might like
PHP遍历二维数组的代码
2011/04/22 PHP
Yii2下session跨域名共存的解决方案
2017/02/04 PHP
ThinkPHP3.2框架操作Redis的方法分析
2019/05/05 PHP
PHP使用 Imagick 扩展实现图片合成,圆角处理功能示例
2019/09/09 PHP
PHP pthreads v3使用中的一些坑和注意点分析
2020/02/21 PHP
Sample script that deletes a SQL Server database
2007/06/16 Javascript
javascript 支持链式调用的异步调用框架Async.Operation
2009/08/04 Javascript
40款非常有用的 jQuery 插件推荐(系列一)
2011/12/21 Javascript
快速掌握WordPress中加载JavaScript脚本的方法
2015/12/17 Javascript
jQuery实现的文字hover颜色渐变效果实例
2016/02/20 Javascript
jQuery视差滚动效果网页实现方法经验总结
2016/09/29 Javascript
通过原生JS实现为元素添加事件的方法
2016/11/23 Javascript
javascript完美实现给定日期返回上月日期的方法
2017/06/15 Javascript
Vue 中可以定义组件模版的几种方式
2019/08/06 Javascript
小程序简单两栏瀑布流效果的实现
2019/12/18 Javascript
Vue——解决报错 Computed property &quot;****&quot; was assigned to but it has no setter.
2020/12/19 Vue.js
React服务端渲染原理解析与实践
2021/03/04 Javascript
Python 列表(List)操作方法详解
2014/03/11 Python
python用字典统计单词或汉字词个数示例
2014/04/22 Python
详解Python中的from..import绝对导入语句
2016/06/21 Python
Python3.5实现的罗马数字转换成整数功能示例
2019/02/25 Python
python批量修改图片尺寸,并保存指定路径的实现方法
2019/07/04 Python
树莓派使用python-librtmp实现rtmp推流h264的方法
2019/07/22 Python
pycharm重命名文件的方法步骤
2019/07/29 Python
Python编程快速上手——Excel表格创建乘法表案例分析
2020/02/28 Python
Python pip使用超时问题解决方案
2020/08/03 Python
python爬取2021猫眼票房字体加密实例
2021/02/19 Python
css3 利用transform打造走动的2D时钟
2020/10/20 HTML / CSS
Smallable意大利家庭概念店:设计师童装及家居装饰
2018/01/08 全球购物
西班牙家用电器和电子产品购物网站:Mi Electro
2019/02/25 全球购物
Paradox London官方网站:英国新娘鞋婚礼鞋品牌
2019/08/29 全球购物
英国Radley包德国官网:Radley London德国
2019/11/18 全球购物
制药工程专业毕业生推荐信
2013/12/24 职场文书
物理教师自荐信范文
2013/12/28 职场文书
即将毕业大学生自荐信
2014/01/24 职场文书
30年同学聚会邀请函
2014/01/25 职场文书