javascript闭包的理解

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; 并改变其值.

(0)

相关推荐

  • 一不小心就做错的JS闭包面试题

    由工作中演变而来的面试题 这是一个我工作当中的遇到的一个问题,似乎很有趣,就当做了一道题去面试,发现几乎没人能全部答对并说出原因,遂拿出来聊一聊吧. 先看题目代码: function fun(n,o) { console.log(o) return { fun:function(m){ return fun(m,n); } }; } var a = fun(0); a.fun(1); a.fun(2); a.fun(3);//undefined,?,?,? var b = fun(0).fun(

  • 几句话带你理解JS中的this、闭包、原型链

    原型链 所有对象都是基于Object.prototype,Object.prototype就是JavaScript的根对象,在Object.prototype中定义的方法都可以被其它对象访问到,当然也可以被重写了,所以直接在Object.prototype上调用的是原始功能的toString()方法,该方法会放回参数对象的内置属性[[class]]的值,这个值是个字符串,比如'[Object String]' 要理解原型链机制的话,首先得知道根本原因:JavaScript中的对象都有一个内置属性

  • 一道JS前端闭包面试题解析

    问题 代码A function fun(n,o){ console.log(o); return { fun:function(m){//[2] return fun(m,n);//[1] } } } var a=fun(0); a.fun(1); a.fun(2); a.fun(3); var b=fun(0).fun(1).fun(2).fun(3); var c=fun(0).fun(1); c.fun(2); c.fun(3); 求出程序输出 这是一个闭包测试题 转换为等价代码 retu

  • javascript 闭包详解及简单实例应用

    JS 闭包详解及实例: 最近学习JS的基础知识,学习了闭包的知识点,有很多疑惑,这一段时间还是一直有在看闭包的相关知识理解就更深入了一点,下面说说我的理解. function fn(){ var a = 0; return function (){ return ++a; } } 如上所示,上面第一个return返回的就是一个闭包,那么本质上说闭包就是一个函数.那么返回这个函数有什么用呢? 那是因为这个函数可以调用到它外部的a这个变量.其实也就是说,return返回的是这个函数 + a这个变量

  • js实现为a标签添加事件的方法(使用闭包循环)

    本文实例讲述了js实现为a标签添加事件的方法.分享给大家供大家参考,具体如下: 以示例说明: 实现效果:循环为带有ml-praise样式类的a标签添加事件,并且在点击a标签后,相应的数量增加1. Html结构如下所示: <ul> <li><a href="javascript:;" class="ml-praise">数量:<span class="ding-num">100</span>

  • 理解javascript中的闭包

    阅读目录 什么是闭包? 闭包的特性 闭包的作用: 闭包的代码示例 注意事项 总结 闭包在javascript来说是比较重要的概念,平时工作中也是用的比较多的一项技术.下来对其进行一个小小的总结 什么是闭包? 官方说法: 闭包是指有权访问另一个函数作用域中的变量的函数.创建闭包的常见方式,就是在一个函数内部创建另一个函数,通过另一个函数访问这个函数的局部变量------<javascript高级程序设计第三版> 下面就是一个简单的闭包: function A(){ var text="

  • js闭包所用的场合以及优缺点分析

    先上段代码: //函数a function a() { var i=0; //函数b function b() { alert(++i); } return b; } //函数c var c = a(); c(); 代码特点: 1.函数b嵌套在函数a内部: 2.函数a返回函数b. 代码中函数a的内部函数b,被函数a外面的一个变量c引用的时候,这就叫创建了一个闭包.有时候函数b也可以用一个匿名函数代替来返回,即return function(){}; 优点:1.保护函数内的变量安全,加强了封装性

  • 一分钟理解js闭包

    什么是闭包? 先看一段代码: function a(){ var n = 0; function inc() { n++; console.log(n); } inc(); inc(); } a(); //控制台输出1,再输出2 简单吧.再来看一段代码: function a(){ var n = 0; this.inc = function () { n++; console.log(n); }; } var c = new a(); c.inc(); //控制台输出1 c.inc(); //

  • javascript从作用域链谈闭包

    神马是闭包 关于闭包的概念,是婆说婆有理. 闭包是指有权访问另外一个函数作用域中的变量的函数 这概念有点绕,拆分一下.从概念上说,闭包有两个特点: 1.函数 2.能访问另外一个函数作用域中的变量 在ES 6之前,Javascript只有函数作用域的概念,没有块级作用域(但catch捕获的异常 只能在catch块中访问)的概念(IIFE可以创建局部作用域).每个函数作用域都是封闭的,即外部是访问不到函数作用域中的变量. function getName() { var name = "美女的名字&

  • 深入理解javascript函数参数与闭包

    最近在学习javascript的函数,函数是javascript的一等对象,想要学好javascript,就必须深刻理解函数.本人把学习的过程整理成文章,一是为了加深自己函数的理解,二是给读者提供学习的途径,避免走弯路.内容有些多,但都是笔者对于函数的总结. 1.函数参数 1.1:参数是什么 1.2:参数的省略 1.3:参数默认值 1.4:参数传递方式 1.5:同名参数 1.6:arguments对象 2.闭包 2.1:闭包定义 2.2:立即调用的函数表达式(IIFE, Immediately

随机推荐