原创文章,转载请注明:JavaScript中的闭包理解  By Lucio.Yang

1.JavaScript闭包

  在小学期开发项目的时候,用node.js开发了服务器,过程中遇到了node.js的第一个陷阱:由事件和回调函数形成的特殊的循环。解决这个问题时我使用了创建闭包的方法,当然如果不需要控制循环的变量的话也可以使用数组的forEach函数。最近ES6在紧锣密鼓的准备,新标准里面的Harmony Generator和yield十分引人瞩目,也可以用来决解这个问题。这是后话了。

  这里来着重介绍一下js的闭包这一重要特性。

  首先什么是闭包?

  1.1闭包的概念解释

    闭包(wikipedia):In programming languages, a closure (also lexical closure or function closure) is a function or reference to a function together with a referencing environment—a table storing a reference to each of the non-local variables (also called free variables or upvalues) of that function.

    也就是说,闭包是一个函数或者函数的引用,并绑定了一些变量和其生存环境。

    通过以下闭包的两个简单的用途来看闭包的概念。

    1.提取函数内部的变量

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

    函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1 就是不可见的。这就是Javascript语言特有的“链式作用域”结构(chain scope)。简单来说,作用域链就是函数在定义的时候创建的,用于寻找使用到的变量的值的一个索引,而他内部的规则是,把函数自身的本地变量放在最前面,把自身的父级函数中的变量放在其次,把再高一级函数中的变量放在更后面,以此类推直至全局对象为止。当函数中需要查询一个变量的值的时候,js解释器会去作用域链去查找,从最前面的本地变量中先找,如果没有找到对应的变量,则到下一级的链上找,一旦找到了变量,则不再继续。如果找到最后也没找到需要的变量,则解释器返回undefined。正是因为这种机制,闭包才能提取父级函数内部的变量。

    2.在内存中保有某些变量。

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

    首先明确一点,在函数内部定义变量/函数的时候,如果不用var,那么该变量/函数就是全局变量/函数。

    result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。这里需要解释一下js的内存回收机制。

    一般来说,一个函数在执行开始的时候,会给其中定义的变量划分内存空间保存,以备后面的语句所用,等到函数执行完毕返回了,这些变量就被认为是无用的了。对应的内存空间也就被回收了。下次再执行此函数的时候,所有的变量又回到最初的状态,重新赋值使用。但是如果这个函数内部又嵌套了另一个函数,而这个函数是有可能在外部被调用到的。并且这个内部函数又使用了外部函数的某些变量的话。这种内存回收机制就会出现问题。如果在外部函数返回后,又直接调用了内部函数,那么内部函数就无法读取到他所需要的外部函数中变量的值了。所以js解释器在遇到函数定义的时候,会自动把函数和他可能使用的变量(包括本地变量和父级和祖先级函数的变量(自由变量))一起保存起来,也就是构建一个闭包,这些变量将不会被内存回收器所回收,只有当内部的函数不可能被调用以后(例如被删除了,或者没有了指针),才会销毁这个闭包,而没有任何一个闭包引用的变量才会被下一次内存回收启动时所回收。

    3.实现对象的封装

var person = function(){
//变量作用域为函数内部,外部无法访问
var name = "default"; return {
getName : function(){
return name;
},
setName : function(newName){
name = newName;
}
}
}(); print(person.name);//直接访问,结果为undefined
print(person.getName());
person.setName("abruzzi");
print(person.getName()); 得到结果如下: undefined
default
abruzzi

    这是一个简单的js的对象的封装,只有通过对象才可以访问对象中的变量,实现了变量和环境(对象)的绑定。也就是通过闭包,我们模拟了面向对象语言中的封装对象的模板。

    4.高效率的匿名自执行函数

var datamodel = {
table : [],
tree : {}
}; (function(dm){
for(var i = 0; i < dm.table.rows; i++){
var row = dm.table.rows[i];
for(var j = 0; j < row.cells; i++){
drawCell(i, j);
}
} //build dm.tree
})(datamodel);

    代码中的闭包实际是用来做UI的初始化,执行一次后临时变量即销毁,这样的机制不会污染全局变量,效率很好。

  1.2闭包与匿名函数的概念辨别

    首先来看匿名函数的概念:

    anonymous function(wikipedia):In computer programming, an anonymous function (also function literal or lambda abstraction) is a function definition that is not bound to an identifier.

    简单来说,没有标识符的函数就是匿名函数。一下是匿名函数的一些写法:

(function() {})();最常见到的;
(function(){}());
void function(){};
错 误的写法
function(){}();

    关于匿名函数的原理和理解这里就不再赘述。我们只探讨匿名函数和闭包的关系。

    再回顾一下闭包的概念中的关键字-“ is a function or reference to a function”,说明只要是函数内部申明的函数是闭包,不管是什么函数。所以,得出结论:

      如果匿名函数不在函数内部,就不是闭包。

      函数内的匿名函数如果引用了父函数的变量,那么这个匿名函数就成为了一个闭包。

    就这么简单"( ̄▽ ̄)"""。所以产生了以下的闭包的定义方法:

var datamodel = {
table : [],
tree : {}
}; (function(dm){
//something
})(datamodel);

参考:javascript深入理解闭包-脚本之家墓中无人sunlylorn十个流年

JavaScript中的闭包理解的更多相关文章

  1. 让你分分钟学会Javascript中的闭包

    Javascript中的闭包 前面的话: 闭包,是 javascript 中重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,你很难从定义去理解它 ...

  2. 难道这就是JavaScript中的"闭包"

    其实对于JavaScript中的"闭包"还没真正理解,这次在实际Coding中似乎遇到了"闭包"的问题,仅此摘录,以待深究. 表现为jQuery的post方法回 ...

  3. 浅谈JavaScript中的闭包

    浅谈JavaScript中的闭包 在JavaScript中,闭包是指这样一个函数:它有权访问另一个函数作用域中的变量. 创建一个闭包的常用的方式:在一个函数内部创建另一个函数. 比如: functio ...

  4. javascript中的闭包解析

    学习javaScript已经有一段时间了,在这段时间里,已经感受到了JavaScript的种种魅力,这是一门神奇的语言,同时也是一门正在逐步完善的语言,相信在大家的逐步修改中,这门语言会逐步的完善下去 ...

  5. 【JS】JavaScript中的闭包

    在JavaScript中,闭包指的是有权访问另一个函数作用域中的变量的函数:创建闭包最常见的方式就是在一个函数内创建另一个函数.如下例子: function A(propertyName){ retu ...

  6. Javascript中的闭包(转载)

    前面的话: 闭包,是 javascript 中重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,你很难从定义去理解它.下面是作者从作用域链慢慢讲到 ...

  7. 狗日的Javascript中的闭包

    前面的话: 闭包,是 javascript 中重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,你很难从定义去理解它.下面是作者从作用域链慢慢讲到 ...

  8. [译]Javascript中的闭包(closures)

    本文翻译youtube上的up主kudvenkat的javascript tutorial播放单 源地址在此: https://www.youtube.com/watch?v=PMsVM7rjupU& ...

  9. javaScript中的闭包原理 (译)

    这篇文章通过javaScript代码解释了闭包的原理,来让编程人员理解闭包.它不是写给大牛或使用功能性语言进行编程的程序员的.一旦意会了其核心概念,闭包理解起来并不难.然而,你不可能通过阅读任何有关闭 ...

随机推荐

  1. this.parentMenu.dataRecord.data.testID的作用

    在JS里,有个this.parentMenu.dataRecord.data.XXID的方法,这个方法一般都是用来加载某个控件到一个面板或控件上的.如: loaddata(this.parentMen ...

  2. C# 通过Devart连接Oracle(不需要客户端)

    16年一月底回了四川,接下来两年就准备在四川工作了.哈哈,虽然收入比沿海城市少了很多,但离老家近些感觉还是很不错的哈,好了,废话不多说,直接上干货. 最近的项目需要用到C#连接Oracle,以前要么是 ...

  3. 安全的PHP代码编写准则

    原文链接 绝不要信任外部数据或输入 关于 Web 应用程序安全性,必须认识到的第一件事是不应该信任外部数据.外部数据(outside data) 包括不是由程序员在 PHP 代码中直接输入的任何数据. ...

  4. TCP/IP详解之:Ping程序、Traceroute程序

    Ping程序: ping程序是通过发送一份ICMP回显请求报文(即ICMP报文的一种,其类型为8,代码为0)给主机,并等待返回ICMP回显应答 来测试另一台主机是否可达. ping程序不用经过传输层, ...

  5. Euclid gcd规则的证明

    Euclid 规则:如果x和y都是正整数,而且x>=y,那么gcd(x,y)=gcd(x mod y, y) 假设x和y的gcd为a,那么必然有 x=a*n1 y=a*n2(gcd(n1,n2) ...

  6. JQuery表格展开与内容筛选

    单击分类的时候,可以关闭打开相对应的内容.例如点击前台设计组,则只显示前台设计组的内容.再次点击则收缩. 筛选的话就是匹配输入框的内容,如果某行数据存在,则显示出来. <html> < ...

  7. ETL中的数据增量抽取机制

    ETL中的数据增量抽取机制 (     增量抽取是数据仓库ETL(extraction,transformation,loading,数据的抽取.转换和装载)实施过程中需要重点考虑的问 题.在ETL过 ...

  8. ODI利用goldengate实现增量数据捕获

    ODI利用goldengate实现增量数据捕获 上个月,Oracle发布了ODI的最新版本10.1.3.6_02,其中增加了针对采用goldengate获取源数据库增量变化的知识模块,这样当系统需要实 ...

  9. restful_api

    http://www.ruanyifeng.com/blog/2014/05/restful_api.html

  10. /etc/security/limits.conf 配置

    <pre name="code" class="python">* soft nofile 65535 * hard nofile 65535 * ...