javascript闭包—围观大神如何解释闭包

简介: 闭包的概念已经出来很长时间了,网上资源一大把,本着拿来主意的方法来看看。 这一篇文章 学习Javascript闭包(Closure) 是大神阮一峰的博文,作者循序渐进,讲的很透彻。下面一一剖析。 1.变量的作用域 变量的作用域有局部和全局两种,在javascript的函数内部可以访问全局变量,如下: // 函数内部可以直接读取全局变量 var n = 99; function f1() { alert(n); } f1(); 在f1函数中可以访问到全局变量n。

闭包的概念已经出来很长时间了,网上资源一大把,本着拿来主意的方法来看看。

这一篇文章 学习Javascript闭包(Closure) 是大神阮一峰的博文,作者循序渐进,讲的很透彻。下面一一剖析。

1.变量的作用域

变量的作用域有局部和全局两种,在javascript的函数内部可以访问全局变量,如下:

  // 函数内部可以直接读取全局变量
  var n = 99;
  function f1() {
    alert(n);
  }
  f1();

在f1函数中可以访问到全局变量n。输出如下:

反过来就不行了,在函数外部不能读取函数内部的变量,例如这样:

  //函数外部无法读取函数内部的局部变量,这里会报错
  function f2() {
    var m = 99;
  }
  alert(m) //报错

javascript还有一个比较特殊的地方,在函数内部如果没有使用var,const,let修饰符声明变量,那么这个变量不再是局部变量而是一个全局变量,如下:

  function f2() {
    m = 99;
  }
  f2();
  alert(m)

输出99:

是不是很神奇,但是这个经常给人造成困惑。

 

2.如何从外部读取函数内部的局部变量

很多场合下要访问函数内部的局部变量,变通的方式是在函数内部定义函数,如下:

  function f3() {
    var a = 999;

    function f4() {
      alert(a);
    }
    return f4
  }
  var result = f3();
  result();

函数f4包含在函数f3里面,所以f4范围内可以访问到f3中的那个变量a,反过来是不行的,Javascript语言特有的"链式作用域"结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。

 

3.闭包的概念

可以简单理解成定义在函数内部的函数,这个内部函数可以把外部函数作用域内的变量传播到外部函数的外面。

 

4.闭包的作用

闭包的第一个用处就是概念里硕大的读取函数内部的变量,另一个作用就是让这些变量始终保存在内存中。来看下面的代码:

  function f5() {
    var b = 111;
    nAdd = function () {
      b += 1;
    }
    function f6() {
      alert(b);
    }
    return f6
  }
  var result1 = f5();
  result1();
  nAdd();
  result1();

上面代码两次弹出框,第一次是输出111,第二次是112,

这就说明,第一次调用result1();的时候给变量b赋值了,然后调用全局函数nAdd的时候变量b仍然还在内存中,给他加1就变成112了。原因就在于f5是f6的父函数,而f6被赋给了一个全局变量,这导致f6始终在内存中,而f6的存在依赖于f5,因此f5也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

这段代码中另一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。

5.注意 

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

最后作者给出了思考题目,如下:

  var name = 'The Window';
  var object = {
    name: 'My Object',
    getNameFunc: function () {
      // 这里的this是函数
      console.info(this)
      return function () {
        //匿名函数的执行环境是windows
        console.info(this)
        return this.name;
      }
    }
  };
  alert(object.getNameFunc()());

其实从变量的值已经看到答案了,在对象object内部的函数getNameFunc里面返回了一个匿名函数这个匿名函数的作用域是window,所以这里输出的是'The Window',如下:

我在日志里面加上的调试语句可以看出端倪:

看下面的代码:

  var name1 = "The Window";
  var object1 = {
    name1: 'My Object1',
    getNameFunc:function () {
      var that = this;
      return function () {
        return that.name1
      }
    }
  }
  alert(object1.getNameFunc()());

这里在函数内部使用var that = this语句先把当前上下文的对象保存下来,在匿名函数中使用that.name1,这样就是当前对象中的name1,于是输出了“MyObject1”。其实可以用es6中的箭头函数,如下:

  var name1 = "The Window";
  var object1 = {
    name1: 'My Object1',
    getNameFunc:function () {
      return  () => {
        return this.name1
      }
    }
  }
  alert(object1.getNameFunc()());

箭头函数会绑定object1的作用域,于是仍任是object1的属性name1,和上面输出的结果一样。

6.深入的理解

 在知乎上也有人在讨论这个问题,知乎你懂的,号称比较严谨(装逼),如何通俗易懂的解释javascript里面的‘闭包’?这一篇问答里有人给出了其他的解释。

 1.每次定义一个函数,都会产生一个作用域链(scope chain)。当JavaScript寻找变量varible时(这个过程称为变量解析),总会优先在当前作用域链的第一个对象中查找属性varible ,如果找到,则直接使用这个属性;否则,继续查找下一个对象的是否存在这个属性;这个过程会持续直至找到这个属性或者最终未找到引发错误为止。
------有道理,关键是弄懂这个作用域链,说白了就是花括号的层级及各种函数的的上下文作用域,比如在函数中定义变量不用var,let它居然是全局的,这个是javascript比较特殊的地方,强类型语言估计早就报错了。

2.JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里。
------有道理,上面的最后一个代码段中的例子,本来执行object1.getNameFunc()()这一句的时候,执行作用域中的name1是var name1 = "The Window";这个,但是弹出来的确实定义getNameFunc这个函数的作用域内的name1: 'My Object1',

3.子函数能够访问父函数的局部变量,反之则不行。而那个子函数就是闭包!
------有道理,就是上面阮大师反复说明的

好了,就这么多了。

 

作者:Tyler Ning
出处:http://www.cnblogs.com/tylerdonet/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,如有问题,可以通过以下邮箱地址williamningdong@gmail.com  联系我,非常感谢。

目录
相关文章
|
2天前
|
自然语言处理 JavaScript 前端开发
Javascript中的闭包encloure
【10月更文挑战第1天】闭包是 JavaScript 中一种重要的概念,指函数能够访问其定义时的作用域内的变量,即使该函数在其词法作用域之外执行。闭包由函数及其词法环境组成。作用域链和词法作用域是闭包的核心原理。闭包常用于数据隐藏和封装,如模块模式;在异步操作中也广泛应用,如定时器和事件处理。然而,闭包也可能导致内存泄漏和变量共享问题,需谨慎使用。
|
15天前
|
JSON JavaScript 前端开发
JavaScript第五天(函数,this,严格模式,高阶函数,闭包,递归,正则,ES6)高级
JavaScript第五天(函数,this,严格模式,高阶函数,闭包,递归,正则,ES6)高级
|
6天前
|
缓存 JavaScript 前端开发
了解js基础知识中的作用域和闭包以及闭包的一些应用场景,浅析函数柯里化
该文章详细讲解了JavaScript中的作用域、闭包概念及其应用场景,并简要分析了函数柯里化的使用。
了解js基础知识中的作用域和闭包以及闭包的一些应用场景,浅析函数柯里化
|
8天前
|
JavaScript 前端开发
JavaScript 闭包
JavaScript 闭包
11 1
|
11天前
|
自然语言处理 前端开发 JavaScript
探索JavaScript中的闭包及其实际应用
本文深入探讨了JavaScript中闭包的概念、特性及其在实际项目中的应用。通过具体示例,详细讲解了闭包的创建方法和用途,揭示了闭包在数据保护和模块化开发中的重要性。同时,还讨论了闭包可能带来的内存管理问题及优化策略,为前端开发者提供了全面的闭包知识和实践指导。
|
20天前
|
自然语言处理 JavaScript 前端开发
探索JavaScript中的闭包:从基础概念到实际应用
本文深入探讨了JavaScript中闭包的概念,从定义、作用域链和实际应用等方面进行了详细阐述。通过生动的比喻和实例代码,帮助读者理解闭包在函数执行上下文中的重要性,以及如何在实际开发中有效利用闭包解决复杂问题。同时,文章也指出了过度使用闭包可能导致的潜在问题,并给出了相应的优化建议。
|
2月前
|
JavaScript 前端开发 安全
详细讲解JavaScript中的闭包问题附代码演示
闭包是JavaScript中一关键概念,它允许内部函数访问外部函数的作用域,从而实现变量的封装与持久化。本文通过示例解释了闭包的工作原理及其优势,如数据隐藏和私有变量的实现;同时也指出了闭包可能导致的内存占用问题,强调合理使用的重要性。
24 1
|
2月前
|
JavaScript 前端开发 Java
JavaScript中的闭包概念讲解
闭包是指函数内部嵌套另一个函数,并且内部函数引用了外部函数的数据(如变量或函数)。这样的内部函数被称为闭包。以示例代码为例,`fn1` 中有两个闭包函数 `fn2` 和 `fn3`,它们都可以访问并修改 `fn1` 中的变量 `a`。
16 1
|
2月前
|
开发者 图形学 C#
深度解密:Unity游戏开发中的动画艺术——Mecanim状态机如何让游戏角色栩栩如生:从基础设置到高级状态切换的全面指南,助你打造流畅自然的游戏动画体验
【8月更文挑战第31天】Unity动画系统是游戏开发的关键部分,尤其适用于复杂角色动画。本文通过具体案例讲解Mecanim动画状态机的使用方法及原理。我们创建一个游戏角色并设计行走、奔跑和攻击动画,详细介绍动画状态机设置及脚本控制。首先导入动画资源并添加Animator组件,然后创建Animator Controller并设置状态间的转换条件。通过编写C#脚本(如PlayerMovement)控制动画状态切换,实现基于玩家输入的动画过渡。此方法不仅适用于游戏角色,还可用于任何需动态动画响应的对象,增强游戏的真实感与互动性。
58 0
|
2月前
|
自然语言处理 JavaScript 前端开发